1. 两种安装方式介绍
- 方法一:QtCreator配置版本安装方式(本人使用):http://wiki.ros.org/IDEs#Qt_Creator_Plugin_for_ROS
这种方式使用的是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显示出来了,效果如下: