我需要做一个Qt界面用来接收摇杆信息等,需要用到ROS和Qt相结合。进行开发需要两点,首先是需要将Qt界面作为节点加入到ROS中;然后,由于话题接收需要ros::spin()进行不断的循环接收,需要把话题订阅节点放入到一个单独线程中。首先实现对摇杆话题的接收,之后对其他话题接收也就同理了。
我的环境是:
操作系统:ubuntu16.04
ROS:kinetic
Qt:5.12.5
将Qt界面加入到ROS节点
这个网上有很多帖子,我主要参考基于ROS的QT界面开发史上最强教程和三种方法在ROS中加载Qt库进行GUI设计这两篇文章。
虽然上面两篇文章讲的很详细了,我还是讲一下我最后配置编程的过程。
1.ROS添加Qt界面节点
不用插件直接用Qt库来建立
$ mkdir -p ~/catkin_qt/src
$ cd ~catkin_qt/src
$ catkin_create_pkg qt_ui std_msgs rospy roscpp
然后打开qt,新建界面,
将创建的界面放到src/qt_ui/src下,会产生MainWindow.cpp,MainWindow.h,MainWindow.ui三个文件,还需要手动创建一个main.cpp文件,具体代码后面说,将qt界面加入到ROS中关键是对CMakeLists.txt的修改,如下:
cmake_minimum_required(VERSION 2.8.3)
project(qt_ui)
## Compile as C++11, supported in ROS Kinetic and newer
add_compile_options(-std=c++11)
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
)
find_package(Qt5 REQUIRED COMPONENTS Widgets)
###################################
## qt5 ##
###################################
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
qt5_wrap_cpp(MOC src/mainwindow.h)
qt5_wrap_ui(UIC src/mainwindow.ui)
###################################
## catkin specific configuration ##
###################################
catkin_package(
INCLUDE_DIRS include
CATKIN_DEPENDS roscpp rospy std_msgs
)
###########
## Build ##
###########
include_directories(
include
${catkin_INCLUDE_DIRS}
INCLUDE_DIRS include
${CMAKE_CURRENT_BINARY_DIR}/..
)
add_executable(${PROJECT_NAME}_node
src/mainwindow.cpp
src/main.cpp
src/StatusNode.cpp
src/mainwindow.ui
)
add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(${PROJECT_NAME}_node ${catkin_LIBRARIES} Qt5::Widgets)
然后就配置完成了,之后有要添加的文件或者库再在这个基础上对CMakeLists.txt进行修改。
在main.cpp中写入如下:
#include"mainwindow.h"
#include<QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc,argv);
MainWindow w;
w.show();
return a.exec();
}
返回到目录~/catkin_qt/,在终端打开
$ catkin_make
还有就是通过Qt来运行程序,打开qt然后打开项目,选择catkin_qt/src/CMakeLists.txt,
然后点击“项目”,将构件目录改到工程下的“build”目录。
然后在终端打开roscore,最后运行程序,qt界面就出来了
2.界面接收摇杆信息
首先要安装ros-joy包,
界面要在每次接收到话题都要进行处理,所以需要将消息的订阅放在一个单独的线程里面,参考ros_qtc_plugin插件自带的一个例子进行改写。
ros_qtc_plugin插件安装:
$ sudo apt-get install ros-kinetic-qt-ros
其中的kinetic根据所用ros版本不同进行更换即可
插件创建界面节点语句为
$ catkin_create _qt_pkg
这个plugin_test小例子这里就不贴了,可自行查看
然后写一个节点多线程类用于接收摇杆信息
StatusNode.h
#include <ros/ros.h>
#include <QThread>
#include<sensor_msgs/Joy.h>
#include <std_msgs/String.h>
#include<QString>
namespace Status_Node{
class StatusNode :public QThread{
Q_OBJECT
signals:
//void joystatus(const QString& str);
void joystatus(const QString& angluar, const QString& linear);
public:
StatusNode(int argc, char** argv);
virtual ~StatusNode();
bool init();
void run();
private:
int init_argc;
char** init_argv;
void chatterCallback(const sensor_msgs::Joy::ConstPtr &joy);
ros::Subscriber sub;
ros::Publisher pub;
};
}
StatusNode.cpp
#include"StatusNode.h"
namespace Status_Node{
StatusNode::StatusNode(int argc, char** argv):
init_argc(argc),
init_argv(argv){
}
StatusNode::~StatusNode(){
if(ros::isStarted()){
ros::shutdown();
ros::waitForShutdown();
}
wait();
}
bool StatusNode::init(){
ros::init(init_argc,init_argv,"status_node");
if(!ros::master::check()){
return false;
}
ros::start();
ros::NodeHandle n;
pub = n.advertise<std_msgs::String>("zx", 1000);
sub = n.subscribe<sensor_msgs::Joy>("joy",1000,&StatusNode::chatterCallback,this);
start();
return true;
}
void StatusNode::chatterCallback(const sensor_msgs::Joy::ConstPtr &joy){
double angular;
angular = joy->axes[0];
double linear;
linear =joy->axes[1];
emit joystatus(QString::number(angular,'g',6),QString::number(linear,'g',6));
}
void StatusNode::run(){
ros::spin();
}
}
这里StatusNode类有两个参数,因此需要对main.cpp和mainwindow.h、mainwindow.cpp进行修改
main.cpp修改:
int main(int argc, char *argv[])
{
QApplication a(argc,argv);
MainWindow w(argc,argv);//修改
w.show();
return a.exec();
}
mainwindow.h修改:
#include <QMainWindow>
#include"StatusNode.h"
#include<QMessageBox>
#include<QString>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(int argc, char** argv,QWidget *parent = nullptr);//修改
~MainWindow();
void showNoMasterMessage();
public slots:
void joystatus(const QString& angluar, const QString& linear);
private:
Ui::MainWindow *ui;
Status_Node::StatusNode node;
};
mainwindow.cpp修改:
MainWindow::MainWindow(int argc, char** argv,QWidget *parent) : //修改
QMainWindow(parent),
ui(new Ui::MainWindow),
node(argc,argv)
{
ui->setupUi(this);
ui->joy_speed->setText("0");
if(!node.init()){
showNoMasterMessage();
}
connect(&node,&Status_Node::StatusNode::joystatus,this,&MainWindow::joystatus);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::joystatus(const QString& angluar, const QString& linear){
ui->joy_angluar->setText(angluar);
ui->joy_speed->setText(linear);
}
void MainWindow::showNoMasterMessage(){
QMessageBox msgBox;
msgBox.setText("Couldn't find the ros master.");
msgBox.exec();
close();
}
这样就可以在qt界面中通过QLabel控件显示摇杆信息了,
首先打开master,
$ roscore
然后打开joy节点
$ rosrun joy joy_node
最后再通过qt运行程序就ok了
记得修改代码需要 caktin_make 从新编译。
话题接收实现后,就剩下话题的发布了。