ROS机器人GUI程序开发(蒋程杨)-一

Posted juzhango

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ROS机器人GUI程序开发(蒋程杨)-一相关的知识,希望对你有一定的参考价值。


title: ROS机器人GUI程序开发(蒋程杨)-一
date: 2020-07-12 08:35:59
tags: 记录


周末这两天完整的学了一下蒋程杨大佬做的ros的gui开发第二部分。简单做一下记录吧,以便以后能快速的复制粘贴??,里面包括一些信号和槽函数的链接,编辑回调函数和信号函数,订阅图像话题,实现快速指令。。。

技术图片

技术图片

按键速度控制

技术图片

连接:

connect(ui.horizontalSlider_linear,SIGNAL(valueChanged(int)),this,SLOT(on_horizontalSlider_linear_valueChanged(int)));
connect(ui.horizontalSlider_raw,SIGNAL(valueChanged(int)),this,SLOT(on_horizontalSlider_raw_valueChanged(int)));

槽函数:

void robot_hmi::MainWindow::on_horizontalSlider_linear_valueChanged(int value)
{
  ui.label_linear->setText(QString::number(value));

}

void robot_hmi::MainWindow::on_horizontalSlider_raw_valueChanged(int value)
{
  ui.label_raw->setText(QString::number(value));
}

话题发布:

ros::Publisher cmd_vel_pub;

cmd_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel",1000);

node与window接口:

public:
	void set_cmd_vel(char k, float linear, float angular);
void QNode::set_cmd_vel(char k, float linear, float angular)
{
  std::map<char, std::vector<float>> moveBindings
      {
        {‘i‘, {1, 0, 0, 0}},
        {‘o‘, {1, 0, 0, -1}},
        {‘j‘, {0, 0, 0, 1}},
        {‘l‘, {0, 0, 0, -1}},
        {‘u‘, {1, 0, 0, 1}},
        {‘,‘, {-1, 0, 0, 0}},
        {‘.‘, {-1, 0, 0, 1}},
        {‘m‘, {-1, 0, 0, -1}},
        {‘O‘, {1, -1, 0, 0}},
        {‘I‘, {1, 0, 0, 0}},
        {‘J‘, {0, 1, 0, 0}},
        {‘L‘, {0, -1, 0, 0}},
        {‘U‘, {1, 1, 0, 0}},
        {‘<‘, {-1, 0, 0, 0}},
        {‘>‘, {-1, -1, 0, 0}},
        {‘M‘, {-1, 1, 0, 0}},
        {‘t‘, {0, 0, 1, 0}},
        {‘b‘, {0, 0, -1, 0}},
        {‘k‘, {0, 0, 0, 0}},
        {‘K‘, {0, 0, 0, 0}}
      };
      char key=k;
      //计算是往哪个方向
      float x = moveBindings[key][0];
      float y = moveBindings[key][1];
      float z = moveBindings[key][2];
      float th = moveBindings[key][3];

      // Update the Twist message
     geometry_msgs::Twist twist;
     twist.linear.x = x * linear;
     twist.linear.y = y * linear;
     twist.linear.z = z * linear;

     twist.angular.x = 0;
     twist.angular.y = 0;
     twist.angular.z = th * angular;

     // Publish it and resolve any remaining callbacks
     cmd_vel_pub.publish(twist);
}

按钮:

connect(ui.pushButton_u,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));
connect(ui.pushButton_i,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));
connect(ui.pushButton_o,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));
connect(ui.pushButton_j,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));
connect(ui.pushButton_l,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));
connect(ui.pushButton_m,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));
connect(ui.pushButton_bb,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));
connect(ui.pushButton_bc,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));
void robot_hmi::MainWindow::on_pushButton_clicked()
{
  QPushButton* btn = qobject_cast<QPushButton*> (sender());
  char k = btn->text().toStdString()[0];
  // 全向轮模式
  bool is_all = ui.checkBox_is_all->isChecked();
  float linear = ui.label_linear->text().toFloat()*0.01;
  float angular = ui.label_raw->text().toFloat()*0.01;
  switch (k) {
  case ‘u‘:
    qnode.set_cmd_vel(is_all?‘U‘:‘u‘,linear,angular);
    break;
  case ‘i‘:
    qnode.set_cmd_vel(is_all?‘I‘:‘i‘,linear,angular);
    break;
  case ‘o‘:
    qnode.set_cmd_vel(is_all?‘O‘:‘o‘,linear,angular);
    break;
  case ‘j‘:
    qnode.set_cmd_vel(is_all?‘J‘:‘j‘,linear,angular);
    break;
  case ‘l‘:
    qnode.set_cmd_vel(is_all?‘L‘:‘l‘,linear,angular);
    break;
  case ‘m‘:
    qnode.set_cmd_vel(is_all?‘M‘:‘m‘,linear,angular);
    break;
  case ‘,‘:
    qnode.set_cmd_vel(is_all?‘<‘:‘,‘,linear,angular);
    break;
  case ‘.‘:
    qnode.set_cmd_vel(is_all?‘>‘:‘.‘,linear,angular);
    break;
  default:
    break;
  }
}

技术图片

速度仪表盘

技术图片

最小size 300*300

创建两个仪表盘:

CCtrlDashBoard* speed_x_dashBoard;
CCtrlDashBoard* speed_y_dashBorad;
//ui
speed_x_dashBoard = new CCtrlDashBoard(ui.widget_speed_x);
speed_y_dashBorad = new CCtrlDashBoard(ui.widget_speed_y);
speed_x_dashBoard->setGeometry(ui.widget_speed_x->rect());
speed_y_dashBorad->setGeometry(ui.widget_speed_y->rect());
speed_x_dashBoard->setValue(0);
speed_x_dashBoard->setValue(0);

添加订阅者

#include <turtlesim/Pose.h>
ros::Subscriber pose_sub;
// 添加信号
Q_SIGNALS:
	void speed_vel(float linear_vel,float angular_vel);
    void pose_callBack(const turtlesim::Pose& msg);
    pose_sub = n.subscribe("/turtle1/pose",1000,&QNode::pose_callBack,this);

这里注意:emit 改成 Q_EMIT

void QNode::pose_callBack(const turtlesim::Pose& msg)
{
    Q_EMIT speed_vel(msg.linear_velocity,msg.angular_velocity);
}

此时可以在MainWindow连接这个信号:

connect(&qnode,SIGNAL(speed_vel(float,float)),this,SLOT(on_update_dashBoard(float,float)));

void robot_hmi::MainWindow::on_update_dashBoard(float x,float y)
{
  ui.label_dir_x->setText(x>=0?"forward":"backward");
  ui.label_dir_y->setText(y>=0?"forward":"backward");
  speed_x_dashBoard->setValue(abs(x*100));
  speed_y_dashBoard->setValue(abs(y*100));
}

技术图片

显示电池电量

添加一些图片

技术图片

QNode:

void power_vel(float); //信号

ros::Subscriber power_sub;

void power_callBack(const std_msgs::Float32& msgs);


power_sub = n.subscribe("power",1000,&QNode::power_callBack,this);

void QNode::power_callBack(const std_msgs::Float32& msg)
{
  float p = 90.012;
  //Q_EMIT power_vel(msg.data);
  Q_EMIT power_vel(p);
}

MainWindow:

void on_update_power(float);

connect(&qnode,SIGNAL(power_vel(float)),this,SLOT(on_update_power(float)));

void robot_hmi::MainWindow::on_update_power(float value)
{
    ui.label_power_val->setText(QString::number(value).mid(0.5)+"V");
    ui.progressBar->setValue(value);
}

订阅图像话题并显示

添加依赖

find_package(catkin REQUIRED COMPONENTS qt_build roscpp
    sensor_msgs
    cv_bridge
    std_msgs
    image_transport
)

添加头文件

#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>
#include <QImage>

UI设计

技术图片

Label的黑框,Frame的分隔符

声明图像话题订阅者

包括回调函数,信号函数

void image_val(QImage);	//信号哦函数
image_transport::Subscriber image_sub;
void image_callBack(const sensor_msgs::ImageConstPtr& msg);	

QImage Mat2QImage(cv::Mat const& src);  //转换算法



void QNode::image_callBack(const sensor_msgs::ImageConstPtr& msg)
{
    cv_bridge::CvImageConstPtr cv_Ptr;
    cv_Ptr=cv_bridge::toCvCopy(msg,msg->encoding);
    QImage im = Mat2QImage((cv_Ptr->image));
    Q_EMIT image_val(im);
}

void QNode::sub_image(QString topic_name)
{
    ros::NodeHandle n;
    image_transport::ImageTransport it_(n);
    image_sub = it_.subscribe(topic_name.toStdString(),1000,&QNode::image_callBack,this);
}


QImage QNode::Mat2QImage(cv::Mat const& src)
 {
   QImage dest(src.cols, src.rows, QImage::Format_ARGB32);

   const float scale = 255.0;

   if (src.depth() == CV_8U) {
     if (src.channels() == 1) {
       for (int i = 0; i < src.rows; ++i) {
         for (int j = 0; j < src.cols; ++j) {
           int level = src.at<quint8>(i, j);
           dest.setPixel(j, i, qRgb(level, level, level));
         }
       }
     } else if (src.channels() == 3) {
       for (int i = 0; i < src.rows; ++i) {
         for (int j = 0; j < src.cols; ++j) {
           cv::Vec3b bgr = src.at<cv::Vec3b>(i, j);
           dest.setPixel(j, i, qRgb(bgr[2], bgr[1], bgr[0]));
         }
       }
     }
   } else if (src.depth() == CV_32F) {
     if (src.channels() == 1) {
       for (int i = 0; i < src.rows; ++i) {
         for (int j = 0; j < src.cols; ++j) {
           int level = scale * src.at<float>(i, j);
           dest.setPixel(j, i, qRgb(level, level, level));
         }
       }
     } else if (src.channels() == 3) {
       for (int i = 0; i < src.rows; ++i) {
         for (int j = 0; j < src.cols; ++j) {
           cv::Vec3f bgr = scale * src.at<cv::Vec3f>(i, j);
           dest.setPixel(j, i, qRgb(bgr[2], bgr[1], bgr[0]));
         }
       }
     }
   }
   return dest;
 }

链接信号

void on_update_image(QImage);
void on_sub_image();


connect(ui.pushButton_sub_image,SIGNAL(clicked()),this,SLOT(on_sub_image()));

void robot_hmi::MainWindow::on_update_image(QImage im)
{
    ui.label_image->setPixmap(QPixmap::fromImage(im));

}

void robot_hmi::MainWindow::on_sub_image()
{

    qnode.sub_image(ui.lineEdit_image->text());;
}

最后实现

没有图像话题,没有显示

技术图片

运行终端命令实现快捷指令

ui设计

技术图片

链接按钮信号和槽函数

void on_pushButton_turtlesim_clicked();//槽函数
void on_quick_output();

QProcess* turtlesim_cmd;

connect(ui.pushButton_turtlesim,SIGNAL(clicked()),this,SLOT(on_pushButton_turtlesim_clicked()));

ui添加回显

技术图片

void robot_hmi::MainWindow::on_pushButton_turtlesim_clicked()
{
    turtlesim_cmd = new QProcess;
    // 运行系统命令行
    turtlesim_cmd->start("bash");
    turtlesim_cmd->write(ui.textEdit_turtlesim->toPlainText().toLocal8Bit()+"
");
    connect(turtlesim_cmd,SIGNAL(readyReadStandardError()),this,SLOT(on_quick_output()));
    connect(turtlesim_cmd,SIGNAL(readyReadStandardOutput()),this,SLOT(on_quick_output()));
}


void robot_hmi::MainWindow::on_quick_output()
{
    ui.textEdit_quick_output->append("<font color="#FF0000">" + turtlesim_cmd->readAllStandardError() + "</font>");
    ui.textEdit_quick_output->append("<font color="#FFFFFF">" + turtlesim_cmd->readAllStandardOutput() + "</font>");
}

最后效果:

技术图片



以上是关于ROS机器人GUI程序开发(蒋程杨)-一的主要内容,如果未能解决你的问题,请参考以下文章

人机交互系统,ROS机器人操作系统

ROS:使用Qt Creator创建GUI程序

译丨Yarn - Javascript 新一代套件管理

ROS:使用Qt Creator创建GUI程序

ROS 常用GUI测试工具

在ROS中利用PyQt写GUI程序