很多时候我们开发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之类的库,直接设置路径才能编译通过。
最后,就是编译程序,运行结果如下: