很多时候我们开发ROS程序的时候,会遇到GUI的需求。有几种方法可以在ROS中开发GUI程序,比如使用rqt_qt。若基于Python语言,还可以使用pyqt、thinker等GUI库。若基于C++,最好的选择是QT。
ROS官方是支持QT4的,比如可以使用catkin_create_qt_pkg创建qt功能包,而ROS中很多著名的工具都是基于QT4。但是2020年,谁还用QT4,当然是拥抱QT5了。此外,ROS并没有一个官方的IDE,虽然使用编辑器+命令行也能满足需求,但是带界面的IDE更加赏心悦目是吧。故本文首先介绍如何使用Qt Creator这个IDE开发ROS程序,然后介绍如何在ROS程序中使用QT5库来开发GUI界面,最后介绍如何在QT中显示PCL库的点云。
Qt Creator开发ROS程序
Levi-Armstrong大佬已经开发了对应的qt插件:ros_qtc_plugin使我们能在qt creator上编译ROS程序。这个插件以前使用ppa的方式进行安装,现在大佬已经把该插件集成到qt creator里面了,安装和使用文档。大致流程是:
1.安装Qt Creator for ROS
下载对应系统的Qt安装包:分为在线和离线安装两种方式,然后运行.run安装包完成带插件的qt creator的安装。如果你之前使用ppa安装过的话,要先删除ppa。
2.配置Qt Creator for ROS
教程里面配置了Qt Creator的各种选项,但其实只有第一项:配置Ubuntu允许debugging/ptrace是必要的。过程为:
(1.Open file: sudo gedit /etc/sysctl.d/10-ptrace.conf
(2.Change the value of kernel.yama.ptrace_scope to 0
(3.Reload the kernel configuration with sudo systemctl restart procps.service
其它的都是可选的。
3.ROS程序创建
在Qt Creator中创建catkin工作空间、创建功能包和节点。
A.首先创建catkin工作空间
打开 “新建文件或项目”,选择ROS Workspace:
下面填写工作空间名称和路径,ps:创建工作空间不会独立创建文件夹,因此最好先新建一个与该工作空间同名的文件夹作为路径。
B.创建功能包
在工作空间下有一个src目录(默认是隐藏的,先设置显示空目录),右键该目录新建文件打开对话框,选择Package。
填入功能包的名称、和依赖的功能包。
C.创建节点
右键功能包目录下的src文件夹,新建文件打开对话框。选择对应节点类型。
然后填入节点名称,比如test.cpp。
4.配置CMakeLists.txt
跟普通的ROS程序一样,需要配置功能包的CMakeLists.txt文件才能编译。一个典型的CMakeLists.txt文件如下:
cmake_minimum_required(VERSION 2.8.3) project(ros_basic) find_package(catkin REQUIRED COMPONENTS geometry_msgs move_base_msgs nav_msgs roscpp rospy sensor_msgs std_msgs tf ) catkin_package( ) include_directories( ${catkin_INCLUDE_DIRS} ) add_executable(time_test src/time_test.cpp) target_link_libraries(time_test ${catkin_LIBRARIES} )
5.配置ROS程序
点击项目,首先配置编译选项,使用CatkinMake编译系统。
配置运行选项,设置运行的功能包和节点。
配置完成后,编译程序,然后就可以运行了。
ROS程序使用QT5库
在前面的基础上,添加Qt的GUI界面是很简单的。右键功能包新建文件,打开对话框,选择QT界面类:
然后选择主窗口类:
最后填入类名、头文件名、源文件名、界面文件名等完成创建。
上面创建了一个qt窗口,但是只是增加了几个文件,并没有添加到编译系统。下面修改CMakeLists.txt使得catkin可以编译qt组件。一个典型的配置如下:
cmake_minimum_required(VERSION 2.8.3) project(test_ros_qt) add_compile_options(-std=c++11) find_package(catkin REQUIRED COMPONENTS roscpp std_msgs ) find_package(Qt5 REQUIRED COMPONENTS Widgets) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(SOURCES src/test_node.cpp src/mainwindow.cpp ) set(FORMS src/mainwindow.ui ) catkin_package( ) include_directories( include/test_node_qt ${catkin_INCLUDE_DIRS} ) add_executable(${PROJECT_NAME}_node ${SOURCES} ${FORMS}) target_link_libraries(${PROJECT_NAME}_node ${catkin_LIBRARIES} ) target_link_libraries(${PROJECT_NAME}_node Qt5::Widgets )
重要语句解释:
set(CMAKE_AUTOMOC ON) #QT中使用moc元对象编译器分析QT语句,然后才交给标准的C++编译器。 set(CMAKE_AUTOUIC ON) #QT中使用uic分析ui代码 set(CMAKE_INCLUDE_CURRENT_DIR ON) #设置工程包含当前目录
然后编译运行即可。
QT中显示点云
熟悉PCL的朋友应该知道它自带了可视化点云的类,这些组件基于VTK。因此若想在QT中显示点云,一个直接的方法是使用VTK进行显示。当然也可以使用OpenGL。下面介绍基于VTK的点云可视化方法:
安装VTK
首先从官网上下载VTK的源码:https://vtk.org/download/ 。我这里选择VTK8.2,解压后进行目录,执行:
mkdir build cd build cmake -DVTK_QT_VERSION:STRING=5 \ -DQT_QMAKE_EXECUTABLE:PATH=/home/chen/QtCreator/latest/bin \ -DVTK_Group_Qt:BOOL=ON \ -DCMAKE_PREFIX_PATH:PATH=/home/chen/QtCreator/latest/lib/Qt/lib \ -DBUILD_SHARED_LIBS:BOOL=ON \ make -j4 #编译 sudo make install #安装
PS:正常Qt应该是这么配置的,
cmake -DVTK_QT_VERSION:STRING=5 \ #QT版本 -DQT_QMAKE_EXECUTABLE:PATH=/path/to/qt5.2.1-install/5.2.1/gcc_64/bin/qmake \ #qmake的路径 -DVTK_Group_Qt:BOOL=ON \ -DCMAKE_PREFIX_PATH:PATH=/path/to/qt.5.2.1-install/5.2.1/gcc_64/lib/cmake \ #qt中cmake的路径 -DBUILD_SHARED_LIBS:BOOL=ON .. #VTK目录
编译安装完成后,将 build/lib/libQVTKWidgetPlugin.so 复制到 /home/chen/QtCreator/latest/lib/Qt/plugins/designer/目录下。ps:正常Qt应该复制到:/home/chen/Qt5.5/Tools/QtCreator/lib/Qt/plugins/designer/目录。这样使用Qt designer的时候,就出现该vtk控件了。
下面通过一个综合实例来了解一下如何使用VTK组件显示点云。
综合实例:QT点云显示
根据前面的介绍,建立的工程如下:
其中ui设计文件如下:
mainwindow.h的代码如下:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include<QLabel> #include <QVTKWidget.h> #include <vtkRenderWindow.h> #include <pcl/point_cloud.h> #include <pcl/point_types.h> #include <pcl/visualization/pcl_visualizer.h> typedef pcl::PointXYZRGB PointT; typedef pcl::PointCloud<PointT> PointCloud; namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); boost::shared_ptr<pcl::visualization::PCLVisualizer> densePointCloudViewer; PointCloud::Ptr densePointCloud; private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
这里面定义一个点云指针,和一个PCL可视化对象,并设置为智能指针。
mainwindow.cpp的代码如下:
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); densePointCloudViewer.reset (new pcl::visualization::PCLVisualizer ("DensePointMap", false)); densePointCloudViewer->setBackgroundColor (0, 0, 0); densePointCloudViewer->addCoordinateSystem (0.5);//设置坐标轴大小 densePointCloudViewer->initCameraParameters (); ui->qvtkWidget->SetRenderWindow (densePointCloudViewer->getRenderWindow()); densePointCloudViewer->setupInteractor (ui->qvtkWidget->GetInteractor (), ui->qvtkWidget->GetRenderWindow ()); ui->qvtkWidget->update (); densePointCloud.reset (new PointCloud); densePointCloud->points.resize (1000); for (size_t i = 0; i < densePointCloud->points.size (); ++i) { densePointCloud->points[i].x = 2 * rand () / (RAND_MAX + 1.0f); densePointCloud->points[i].y = 2 * rand () / (RAND_MAX + 1.0f); densePointCloud->points[i].z = 2 * rand () / (RAND_MAX + 1.0f); densePointCloud->points[i].r = 255; densePointCloud->points[i].g = 255; densePointCloud->points[i].b = 255; } densePointCloudViewer->addPointCloud (densePointCloud, "cloud"); ui->qvtkWidget->update (); } MainWindow::~MainWindow() { delete ui; }
对熟悉PCL的朋友来说上面的代码还是很简单的,唯一需要注意的点是每次更新PCLVisualizer对象,都要将绑定的VTKWidget更新。
test_node.cpp是节点主程序,代码如下:
#include <ros/ros.h> #include <QApplication> #include "mainwindow.h" int main(int argc, char **argv) { ros::init(argc, argv, "test_node"); ros::NodeHandle nh; ROS_INFO("Hello world!"); ROS_INFO("Hello OK!klk"); QApplication a(argc,argv); MainWindow w; w.show(); return a.exec(); }
接下来是重头戏,CMakeLists.txt如下:
cmake_minimum_required(VERSION 2.8.3) project(test_ros_qt) add_compile_options(-std=c++11) find_package(catkin REQUIRED COMPONENTS roscpp std_msgs ) find_package( Qt5 REQUIRED COMPONENTS Widgets ) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) catkin_package( # INCLUDE_DIRS include # LIBRARIES test_ros_qt # CATKIN_DEPENDS roscpp # DEPENDS system_lib ) include_directories( include/test_node_qt include ${catkin_INCLUDE_DIRS} /usr/local/include /usr/include/pcl-1.7 /usr/include/vtk-6.2 ) set(LIBS -lpcl_common -lpcl_search -lpcl_features -lpcl_segmentation -lpcl_recognition -lpcl_visualization -L/usr/lib/x86_64-linux-gnu/ -L/usr/lib/x86_64-linux-gnu/ -lboost_system -lboost_thread -lvtkRenderingCore-6.2 -lvtkCommonDataModel-6.2 -lvtkCommonMath-6.2 -lvtkCommonCore-6.2 -lvtkGUISupportQt-6.2 Qt5::Widgets ) set(SOURCES src/test_node.cpp src/mainwindow.cpp ) set(FORMS src/mainwindow.ui ) add_executable(${PROJECT_NAME}_node ${SOURCES} ${FORMS}) target_link_libraries( ${PROJECT_NAME}_node ${catkin_LIBRARIES} ${LIBS} )
我尝试了一下,好像不能直接find_package找到PCL之类的库,直接设置路径才能编译通过。
最后,就是编译程序,运行结果如下:
博主在这上面花了好几天时间,血泪之作。