Bootstrap

QT下的ROS安装配置与开发笔记(附带简单的ROS界面发布器与订阅器例子)

1. 两种安装方式介绍

这种方式使用的是qt的pro工程,因此没有ros的配置(CMakeLists.txt和package.xml),所以通信的消息只能使用std标准的消息(例如std_msgs::String),如果要使用自定义的ros的.msg文件,那么可以预先编译好.msg,然后在devel/include/下面有对应的msg的.h文件。直接拿出来使用即可。

  • 方法二:Qt Creator Plugin for ROS(方法简单,供参考)

2. 安装配置(方法一)

2.1 安装qt_ros

这一步主要是为了下载模板,不影响配置,本人安装环境为ubuntu 18.04。运行命令如下:

sudo apt-get install ros-melodic-qt-ros

然后就可以使用catkin_create_qt_pkg命令创建模板package了。这个模板可以使用catkin_make进行编译使用。

cd catkin_for_qt/src/
catkin_create_qt_pkg test_for_qt
catkin_make

模板的文件如下:
在这里插入图片描述

在终端编译完成后,可以执行lib下的可执行文件,但是这种运行方式不是重点,下面介绍qt界面的运行方式。

2.2 配置qt

测试了一下,这一步好像可以省略。。。

  • 打开文本文件
sudo gedit ~/.local/share/applications/DigiaQt-qtcreator-community.desktop
  • 修改如下

只需要增加:bash-i -c

[Desktop Entry]
Type=Application
Exec=bash-i -c /home/liqiang/Qt5.10.1/Tools/QtCreator/bin/qtcreator
Name=Qt Creator (Community)
GenericName=The IDE of choice for Qt development.
Icon=QtProject-qtcreator
StartupWMClass=qtcreator
Terminal=false
Categories=Development;IDE;Qt;
MimeType=text/x-c++src;text/x-c++hdr;text/x-xsrc;application/x-designer;application/vnd.qt.qmakeprofile;application/vnd.qt.xml.resource;text/x-qml;text/x-qt.qml;text/x-qt.qbs;

2.3 新建工程运行

  • 1)在QT中创建一个新的mainwindow,并删除原来的.h .cpp .ui文件
  • 2)把ROS中的四个文件夹复制到mainwindow下;
  • 3)使用add existing directory方式添加这四个文件夹,并删除原来的文件;
  • 4)在pro文件中添加(末尾添加就行):
INCLUDEPATH += /opt/ros/melodic/include
DEPENDPATH +=   /opt/ros/melodic/include
#LIBS +=  -l:/usr/lib/x86_64-linux-gnu/libboost_thread.so
#LIBS +=  -l:/usr/lib/x86_64-linux-gnu/libpthread.so

LIBS += -L/opt/ros/melodic/lib -lroscpp -lrospack -lpthread -lrosconsole -lrosconsole_log4cxx -lrosconsole_backend_interface -lxmlrpcpp -lroscpp_serialization -lrostime  -lcpp_common  -lroslib -ltf  -lyaml-cpp -lkdl_conversions

其实这一步的重点在于pro的配置,不用模板的话,自己写mainwindow也是可以的。

模板其实就是一个参考。运行效果如如下:

在这里插入图片描述

使用这种方式同样可以使用rostopic list查看相应的话题,也可以使用rqt和rviz。

在这里插入图片描述

3. 无界面的发布器与订阅器

这种方式比较简单,只需要配置pro文件(参考上面),然后创建一个Console Application,它的形式和ROS的发布器与订阅器一致,有兴趣可以参考:ROS的发布器与订阅器

发布器代码如下:

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
#include<iostream>
using namespace std;
int main(int argc, char **argv)
{
  ros::init(argc, argv, "node_1");//初始化ROS,并指定节点名称“node_1”,
  ros::NodeHandle n;//实例化节点
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);//消息队列为1000,也就是缓冲区,超过1000后,删除旧的

  ros::Rate loop_rate(10);//发布消息的频率10hz,每秒10次,与sleep来协作控制时间

  int count = 0;
  while (ros::ok())//按下ctrl-c后,ros::ok会返回false
  {
    std_msgs::String msg;

    std::stringstream ss;
    ss << "hello world " << count;
    msg.data = ss.str();
    ROS_INFO("%s", msg.data.c_str());
    chatter_pub.publish(msg);//开始发布
    ros::spinOnce();//如有订阅,则必须加,不然回调函数不起作用,会往下执行,
    loop_rate.sleep();//控制时间
    ++count;
  }
  return 0;
}

程序输出如下:
在这里插入图片描述
订阅器形式一样,新建一个工程,然后把给的链接中的代码复制过去运行即可。不过这种方式不用QT也行。

4. 带界面的发布器与订阅器

使用QT是想利用它设计界面的便捷性,所以我们接下来介绍QT如何创建发布器与订阅器。pro文件的内容和上面一致,其他说明情况如下。

  • qnode.h和qnode.cpp使用线程实现,因为ros需要一个单独的时钟策略,区别于主函数界面的循环。
  • 新建的mainwindow需要把argc和argv传递过去,因此需要改动一点点,具体参考代码。

以下代码经过测试,可以作为一个模板。

main.cpp:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w(argc,argv);//把参数传过去
    w.show();

    return a.exec();
}

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "qnode.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(int argc,char** argv, QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QNode node;
};

#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(int argc, char** argv, QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    node(argc, argv, "node_test")
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

qnode.h:

#ifndef QNODE_H
#define QNODE_H

#include <ros/ros.h>
#include <std_msgs/String.h>
#include <QThread>

class QNode : public QThread
{
    Q_OBJECT
public:
    QNode(int argc, char** argv,char* node_name);//构造函数,节点名字传过去
    virtual ~QNode();
    bool init();
    void run();//循环
    void Callback(const std_msgs::StringConstPtr &msg);  //回调函数
private:
    int init_argc;
    char** init_argv;
    char* init_node_name;
    ros::Publisher chatter_publisher;//发布器
    ros::Subscriber chatter_subscriber; //订阅器
};

#endif // QNODE_H

qnode.cpp:

#include "qnode.h"

QNode::QNode(int argc, char** argv,char* node_name )
{
    init_argc = argc;
    init_argv = argv;
    init_node_name = node_name;
    init();
}

QNode::~QNode() {
    if(ros::isStarted()) {
        ros::shutdown(); // explicitly needed since we use ros::start();
        ros::waitForShutdown();
    }
    wait();//线程等待
}
bool QNode::init() {
    ros::init(init_argc,init_argv,init_node_name);
    if ( ! ros::master::check() ) {
        return false;
    }
    ros::start();
    ros::NodeHandle n;
    chatter_publisher = n.advertise<std_msgs::String>("chatter", 1000);
    chatter_subscriber = n.subscribe("chatter", 1000, &QNode::Callback, this); //add
    start();//线程开启
    return true;
}

void QNode::run()
{
    ros::Rate loop_rate(10);//频率
    int count = 0;
    while(ros::ok())
    {
        std_msgs::String msg;
        std::stringstream ss;
        ss << "hello world " << count;
        msg.data = ss.str();
        chatter_publisher.publish(msg);//发布

        ++count;

        ros::spinOnce();
        loop_rate.sleep();
    }
}
void QNode::Callback(const std_msgs::StringConstPtr &msg)
{
}

我自己把订阅器和发布器的消息(一个程序既是发布也订阅),在mainwindow上面用List View显示出来了,效果如下:

在这里插入图片描述

;