需要一个工作空间、ros包,为了方便后面教程的铺开,做一个示例如下:
工作空间:
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
cd ..
catkin_make
在src目录下新建一个ros包,如:
catkin_create_pkg qt_ros roscpp std_msgs
在这个包下面任意一个文件夹,个人喜欢用ros文件夹,在里面先添加一个不为空的文件,比如main.cpp(原因在于qt creator不识别空的文件夹……)。
#include
int main(int argc, char *argv[])
{
return 0;
}
使用QT creator打开工作空间。(这个网上教程很多)
在qt_ros包的ros文件夹上,右键选择Add New...
,然后选择Qt的Qt Designer Form Class
:
然后在Choose a Form Template
下选择Main Window
(也可已选择其他,按照需求),其他的不用修改直接Next:
就可设置类的名字,在这里只需要修改Class name
即可,其他的会自动修改:
之后一路Next即可,就会进入到如下的界面,然后就可以开始编程了。
刚刚新建的main.cpp只是为了让目录能够被Qt Creator识别到,现在需要修改它,以便结合QT的程序进行编程。
#include "qttest.h"
#include
int main(int argc, char *argv[])
{
ros::init(argc, argv, "qt_ros_node");
QApplication a(argc, argv);
QtTest w;
w.show();
return a.exec();
}
结合ROS和Qt的编程需要注意到,QT有自己的循环机制,ROS也有一套循环机制,不能冲突了,所以需要使用QT的定时器来进行ROS消息的刷新。示例代码如下:
参考前一小节。
#ifndef QTTEST_H
#define QTTEST_H
#include
#include
#include
// ros
#include
#include
namespace Ui
{
class QtTest;
}
class QtTest : public QMainWindow
{
Q_OBJECT
public:
explicit QtTest(QWidget *parent = nullptr);
~QtTest();
void timerLoop();
void exampleCallback(const std_msgs::Int16Ptr &msg);
private:
Ui::QtTest *ui;
// ros
ros::NodeHandle n_;
ros::Subscriber example_sub_;
ros::Publisher example_pub_;
QTimer *mpTimer;
};
#endif // QTTEST_H
#include "qttest.h"
#include "ui_qttest.h"
QtTest::QtTest(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::QtTest)
{
ui->setupUi(this);
example_sub_ = n_.subscribe("/qt_ros/example1", 1, &QtTest::exampleCallback, this);
example_pub_ = n_.advertise<std_msgs::Int16>("/qt_ros/example2", 1);
// 定时器相关初始化
mpTimer = new QTimer(this);
mpTimer->setInterval(20); // 单位毫秒
connect(mpTimer, &QTimer::timeout, this, &QtTest::timerLoop);// 定时器信号槽函数
mpTimer->start();
}
QtTest::~QtTest()
{
delete ui;
}
void QtTest::timerLoop()
{
std_msgs::Int16 info;
info.data = 10;
example_pub_.publish(info);
ros::spinOnce();
}
void QtTest::exampleCallback(const std_msgs::Int16Ptr &msg)
{
qDebug() << msg->data;
}
cmake_minimum_required(VERSION 2.8.3)
project(qt_ros)
add_compile_options(-std=c++11)
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
)
######################
## QT configuration ##
######################
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Qt5 COMPONENTS Core Gui Widgets Charts REQUIRED)
###################################
## catkin specific configuration ##
###################################
catkin_package()
###########
## Build ##
###########
include_directories(
# include
${catkin_INCLUDE_DIRS}
)
###########
## Build ##
###########
add_executable(qt_ros_node ros/main.cpp ros/qttest.cpp ros/qttest.ui)
target_link_libraries(qt_ros_node ${catkin_LIBRARIES} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Charts)
<package format="2">
<name>qt_rosname>
<version>1.0.0version>
<description>The qt_ros packagedescription>
<maintainer email="[email protected]">emotionskymaintainer>
<license>TODOlicense>
<buildtool_depend>catkinbuildtool_depend>
<depend>roscppdepend>
<depend>std_msgsdepend>
package>
原单线程代码
多线程实现的代码
说明:单线程版本的代码就是博客中这样的代码实现,也没有什么可关注学习的,在本人的git仓库中已经进行了移除。对应的,给出了多线成版本的实现,但是由于本人已经逃离了机器人这个行业,所以这个代码也就是随手写了一下,并没有进行调试,如有需要,还请自行进行调试解决bug。
对于本博客中给出的这种混合编程的方法在处理的效率上是很难尽人意的,毕竟是同步的方式进行实现,更优的处理是使用多线程的方式进行实现。使用 QThread
创建一个线程,将ROS的循环部分放到这个线程中进行处理,并且使用QT的信号与槽的方式进行ROS所在线程
和主线程(也就是界面)
进行数据交互,这样的处理好处是保证了ROS的实时,毕竟QT界面的循环是很慢的……
由于博主已经脱离了ROS开发这个圈子,这部分的代码实现也就一直没写,QT的多线程开发也不是什么难事,网络上很多……
(或许某天有捣鼓的心情再来写一下也不一定,笑哭)