第一部分:项目背景
目前已经在ROS1上跑通了全卷积点云神经网络基于caffe,需要将其全部转移到ROS2上去。
这个功能包分为以下几个部分
由图上结构可以看出,这份功能包中有common、detected_objects_visualizer、lidar_cnn_seg_detect、custom_msgs,它们的作用如下:
common | 基础数学库 |
detected_objects_visualizer | 可视化配置 |
lidar_cnn_seg_detect | 核心 |
custom_msgs | 自定义消息 |
可以看出代码的结构层次还是很明显的,下面将一步步来介绍。
第二部分:准备工作
首先需要创建一个功能包
mkdir-p lidar_cnn/src
cd lidar_cnn
colcon build
上述指令执行完毕,将创建lidar_cnn目录,且该目录下包含build(存储中间文件的目录,该目录下会为每一个功能包创建一个单独子目录)、install(安装目录,该目录下会为每一个功能包创建一个单独子目录)、log(日志目录,用于存储日志文件)、src共四 个子级目录。 工作空间创建完毕后,我们可以在工作空间下的src目录中编写C++程序,且流程如下:
创建功能包;编辑源文件;编辑配置文件;编译;执行
创建功能包
首先要注意的是,我们需要注意原先ros1中功能包有哪些依赖(可以在package.xml中查看),我们在创建ros2过程中可以直接添加上,这样可以省去后面再重复添加的麻烦。
例如,在ros1中的依赖如下
<build_depend>roscpp</build_depend>
<build_depend>tf</build_depend>
<build_depend>tf_conversions</build_depend>
<build_depend>pcl_ros</build_depend>
<build_depend>sensor_msgs</build_depend>
<build_depend>geometry_msgs</build_depend>
<build_depend>rockauto_msgs</build_depend>
那么我们在创建功能包时就可以直接添加
ros2 pkg create lidar_cnn_seg_detect--build-type ament_cmake--dependencies rclcpp tf tf_conversions pcl sensor_msgs geometry_msgs
你可能已经发现了,我没有添加pcl_ros,这是因为ROS2并不支持pcl_ros,所以后面在改源代码的时候再解决,这里先用pcl替代。
在CMAKELists文件中也需要修改,详细可参考详解使用ament_cmake构建ROS2功能包_ament_package-CSDN博客
add_executable(lidar_cnn src/cluster2d.cpp)
src/cnn_segmentation.cpp
src/feature_generator.cpp
src/lidar_cnn_seg_detect_node.cpp)
ament_target_dependencies(
lidar_cnn
"rclcpp"
"std_msgs"
"sensor_msgs"
"message_filters"
"pcl_conversions"
"pcl_ros"
)
install(TARGETS
lidar_cnn
DESTINATION lib/${PROJECT_NAME})
第三部分:修改源文件
从上面文件目录可以看到,最简单的功能包当然是自定义消息,因此先构建他。
custom_msgs
同上文一样,构建一个功能包,然后再添加一个msg文件夹,在其中添加你需要的.msg文件
尤其注意
ROS2在构建自定义msg时,.msg文件的首字母必须要大写,如要写ObjectDetect.msg
举个例子,下面在custom_msgs/mg中创建一个新的文件ObjectDetect.msg
std_msgs/Header header # Ros header
float32 speed # 车速(km/h)
float32 steerAngle # 转角
这里也要注意在ROS2中的Header必须要加std_msgs,不然就会报如下错误
fatal error: more_interfaces/msg/detail/header__struct.h: 没有那个文件或目录
22 | #include "more_interfaces/msg/detail/header__struct.h"
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [CMakeFiles/more_interfaces__rosidl_generator_c.dir/build.make:124:CMakeFiles/more_interfaces__rosidl_generator_c.dir/rosidl_generator_c/more_interfaces/msg/detail/A__functions.c.o] 错误 1
make[1]: *** [CMakeFiles/Makefile2:206:CMakeFiles/more_interfaces__rosidl_generator_c.dir/all] 错误 2
make[1]: *** 正在等待未完成的任务....
#在CMAKELists中添加如下代码
find_package(rosidl_default_generators REQUIRED)
find_package(std_msgs REQUIRED)
set(msg_files
"msg/ObjectDetect.msg"
)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/ObjectDetect.msg"
DEPENDENCIES std_msgs
)
#在package中添加如下代码
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
#在package.xml中添加以下行
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<build_depend>std_msgs</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<exec_depend>std_msgs</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
这些是为了将.msg文件转换成对应的C++头文件
然后编译就行了
colcon build--packages-select custom_msgs
lidar_cnn_seg_detect
然后进入核心部分,首先观察功能包结构
我们首先将头文件逐个进行修改
比如在cnn_segmentation.h中修改
#原版本
#include <chrono>
#include <ros/ros.h>
#include "caffe/caffe.hpp"
#include <pcl/point_types.h>
#include <pcl_ros/point_cloud.h>
#include <pcl/filters/extract_indices.h>
#include <custom_msgs/DetectedObjectArray.h>
#include <visualization_msgs/MarkerArray.h>
#include <jsk_recognition_msgs/BoundingBoxArray.h>
#include <std_msgs/Header.h>
#include "cluster2d.h"
#include "feature_generator.h"
#ROS2版本
#include <chrono>
#include <rclcpp/rclcpp.h>
#include "caffe/caffe.hpp"
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/filters/extract_indices.h>
#include <custom_msgs/msg/DetectedObjectArray.h>
#include <visualization_msgs/msg/marker_array.h>
#include <jsk_recognition_msgs/msg/bounding_box_array.h>
#include <std_msgs/msg/header.h>
#include "cluster2d.h"
#include "feature_generator.h"
然后根据ROS2的特点,通常推荐以继承Node的方式来创建节点对象,因此需要将CNNSegmantation类修改成如下
#要修改的地方
class CNNSegmentation : public rclcpp::Node
{
public:
CNNSegmentation:Node(cnn_seg_node){}
void run();
void test_run();
private:
#将发布方和订阅方修改,在代码中也需要修改
rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr points_sub_;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr points_pub_;
}
详细可参考:ROS1到ROS2迁移教程(以rslidar_to_velodyne功能包为例)+ ROS2版本LIO-SAM试跑_ros1转ros2-CSDN博客
然后就可以编译这个包了,但是出现了如下问题
很明显这是找不到自定义msg生成的hpp
于是去install/custom_msgs/include/custom_msgs/msg找发现确实没有这个hpp
于是先验证自定义msg是否正常生成
ros2 interface show custom_msgs/msg/DetectedObject
这样的格式说明没有问题,于是反思是不是在ros2生成自定义msg头文件与ros1不同,于是尝试
#include <custom_msgs/msg/detected_object.hpp>
#include <custom_msgs/msg/detected_object_array.hpp>
竟然成了,这些文件名和install/custom_msgs/include/custom_msgs/msg相同
这么奇怪吗??这里确实没搞懂,但是编译这部分没报错了。
然后就开始各种问题,下面一一列举
问题1
于是在PCL库中去找看真的是不是没有,因为我只包含了
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
发现这两个头文件中的确没有,突然发现PCL库中还有一个包就叫PointIndices.h,于是直接加上,问题解决
问题2
这里是忘记改了,要把所有格式都改成ROS2版本的
问题3
很明显这个函数需要两个参数,把它补齐即可
RCLCPP_INFO_STREAM(this->get_logger(),"process takes "<<elapsed<<" ms.");
问题4
这个地方需要尤为注意,在ROS2中最好不要这么写
subscription_ = this->create_subscription<Student>("topic_stu", 10,
std::bind(&MinimalSubscriber::topic_callback, this, _1));
因为尝试将某个类型的回调函数赋值给 callback_variant_,但是类型不匹配。可能的原因之一是 std::function 无法正确地将某个类型的回调函数或函数对象进行包装。确保回调函数的类型与 std::function 的模板参数匹配: 确保你的回调函数的参数和返回类型与 std::function 所期望的类型相匹配。包括确保回调函数接受 constsensor_msgs::msg::PointCloud2_<std::allocator<void>> & 作为参数。
最好使用 Lambda 表达式而不是 std::bind。Lambda 表达式的类型通常更容易与 std::function 匹配。
subscription_ = this->create_subscription<std_msgs::msg::String>(
"topic", 10,
[this](const std_msgs::msg::String::SharedPtr msg) {
this->topic_callback(msg);
});
问题5
这个错误表明你正在尝试使用 const 修饰的 Header 对象进行某些操作,但是这个操作要求对象是可修改的。该错误通常发生在你尝试在一个 const 成员函数内修改对象的成员时。
在C++中,const 成员函数是指被声明为 const 的类成员函数。这样的函数表明它不会修改调用它的对象的状态。在 const 成员函数内,不能修改类的非静态成员变量,也不能调用非 const 成员函数,这有助于确保在使用 const 对象时不会意外地修改其状态。
因此把const类回调函数改为普通回调函数,去掉const即可
void CNNSegmentation::pointsCallback(const sensor_msgs::msg::PointCloud2::SharedPtr msg)
问题6
请一定按照教程走完,因为PCL库对ROS2支持不全,很多地方没有更改,所以会报很多warning,需要耐心查找问题。