移植源码:koide3/hdl_localization: Real-time 3D localization using a (velodyne) 3D LIDAR
移植后的代码:https://github.com/XzLuckh/hdl_localization
一、ROS2介绍
ROS 1 和 ROS 2 是机器人操作系统(Robot Operating System)的两个主要版本,ROS 2 是对 ROS 1 的全面升级,解决了 ROS 1 的许多架构缺陷,并引入了现代机器人开发所需的新特性。下面对ROS1和ROS2进行对比:
|------|------------------|--------------------------|
| | ROS1 | ROS2 |
| 通信机制 | 依赖 Master,TCPROS | DDS,去中心化 |
| 实时性 | 较差 | 支持 QoS,更实时 |
| 跨平台 | 主要 Linux | Linux/Windows/macOS/RTOS |
| 编程语言 | C++03/Python 2 | C++17/Python 3 |
| 构建系统 | catkin | ament/colcon |

我在ROS1中部署了该项目,实现效果如下:

二、hdl_localization代码移植
首先得安装一个ROS2系统(可参考以下博客):
Ubuntu安装ROS(2) ------ 安装ROS2 humble(最新、超详细图文教程,包含配置rosdep)_ros2安装-CSDN博客
首先查看hdl_localization包的结构,直接查看其CMakeLists.txt文件

我们需要在ROS2 系统中创建一个新的工作空间
bash
mkdir -p ros2_ws_test/src
将hdl_localization项目克隆到src文件目录下
bash
git clone https://github.com/koide3/hdl_localization
准备工作完成后,首先应该对CMakeLists.txt文件进行修改。下面先给出ROS1和ROS2的主要区别。
ROS1与ROS2的CMakeLists.txt文件在构建系统设计上存在显著差异,主要体现在架构理念和实现方式上。ROS1基于传统的catkin构建系统,采用集中式的依赖管理方式,通过catkin_package()宏统一处理包信息和依赖关系,其语法相对简单但灵活性较低。而ROS2采用现代化的ament_cmake构建系统,遵循更模块化的设计原则,每个依赖项需独立声明,使用面向目标的现代CMake特性(如命名空间目标和接口库),使得依赖关系更清晰、包隔离性更好。在安装部署方面,ROS2简化了安装规则但增加了导出控制的显式声明,同时引入标准化的测试框架集成方式。这些改进使ROS2的构建系统在跨平台支持、依赖管理和可维护性方面具有明显优势,但也要求开发者掌握更复杂的CMake编写规范。总体而言,从ROS1到ROS2的CMake配置转变反映了构建系统从简单统一向灵活精确的设计演进趋势。
基础框架对比:
ROS1
cs
cmake_minimum_required(VERSION 2.8.3)
project(my_pkg)
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
)
catkin_package()
ROS2
cs
cmake_minimum_required(VERSION 3.5)
project(my_pkg)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
ament_package()
对于package.xml:ROS1 采用分层次的依赖声明(如 build_depend、exec_depend),需单独处理消息生成的编译/运行时依赖(message_generation 和 message_runtime),格式支持 version 1 或 2;而 ROS2 强制使用 version 3 格式,大幅简化依赖分类,用统一的 <depend> 替代 ROS1 的多类依赖,合并消息生成工具为 rosidl_default_generators,并强制显式声明构建工具(如 ament_cmake)。此外,ROS2 更频繁使用 <export> 标签定义组件插件,体现其模块化设计理念。这些改动使 ROS2 的依赖管理更简洁,但要求更严格的规范遵循。
依赖包的区别
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ROS1 | ROS2 |
| find_package(catkin REQUIRED COMPONENTS nodelet tf2 tf2_ros tf2_eigen tf2_geometry_msgs eigen_conversions pcl_ros roscpp rospy sensor_msgs geometry_msgs message_generation ndt_omp fast_gicp hdl_global_localization ) | find_package(rclcpp REQUIRED) find_package(rcl REQUIRED) find_package(std_msgs REQUIRED) find_package(sensor_msgs REQUIRED) find_package(geometry_msgs REQUIRED) find_package(tf2 REQUIRED) find_package(tf2_ros REQUIRED) find_package(tf2_eigen REQUIRED) find_package(tf2_geometry_msgs REQUIRED) find_package(pluginlib REQUIRED) find_package(pcl_conversions REQUIRED) find_package(statistics_msgs REQUIRED) |
rclcpp 替代了 roscpp 作为 C++ 客户端库,tf2 系列包(如 tf2_ros)在 ROS2 中保留了相同功能但实现更现代化,而 pluginlib 仍然用于插件机制但集成更标准化。此外,ROS2 新增了 rcl 作为底层客户端库,不再需要 message_generation,并将 nodelet 替换为 Component 机制。对于点云处理,pcl_ros 的功能在 ROS2 中主要通过 pcl_conversions 和原生 PCL 实现。整体上,ROS2 的依赖管理更模块化,许多 ROS1 包在 ROS2 中仍保持同名但接口可能略有调整,部分功能包(如 ndt_omp)则需要寻找 ROS2 兼容版本或自行移植。
下面给出部分ROS2版本的CMakeLists.txt文件代码:

然后对package.xml进行修改

对于hdl_localization包中的.msg文件,ROS2与ROS1不同:
- ROS 1 允许将 .msg 文件放在功能包的 msg/ 目录下,通过 add_message_files() 生成。
- ROS 2 要求自定义消息必须放在 独立的接口包(如 ros2_msgs)中,通过 rosidl_generate_interfaces() 生成。
因此需要在ROS2中创建一个ros2_msgs接口包。


ros2_msg编译成功
接下来要对cpp文件进行修改:
1.头文件更改
#include <ros/ros.h> 改为 #include "rclcpp/rclcpp.hpp"
#include<ros/time.h> 改为 #include "rclcpp/time.hpp"
#include <pcl_ros/point_cloud.h>
改为:
#include <pcl_conversions/pcl_conversions.hpp> // 用于PCL和ROS2消息的转换
#include <sensor_msgs/msg/point_cloud2.hpp> // ROS2的PointCloud2消息
- 显式转换
ROS1的pcl_ros/point_cloud.h 定义了pcl::Pointcloud<T>到sensor_msgs::PointCloud2的便捷转换,而 ROS2 需要显式转换:
ROS1风格:
cs
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::Pointcloud<pcl::PointXYZ>);
sensor_msgs::PointCloud2 output;
pcl::toROSMsg(*cloud, output):
ROS2风格:
cs
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl: :PointXYZ>);
auto output = std::make_shared<sensor_msgs::msg::Pointcloud2>();
pcl::toRoSMsg(*cloud, *output);
- 更换时间API
hd1_1ocalization 中的代码使用了ROS 1的时间API(ros::Time),但ROS 2中使用的是rclcpp::Time ,API有所不同
替换所有的time.is_zero()为:time == rclcpp::Time(0, 0, time.get_clock_type())
替换所有的duration.toSec()为:duration.seconds()
ROS1和ROS2的启动launch文件也存在较大的差异:
ROS1的.launch文件是基于XML格式,使用<launch>作为根标签,通过标签定义节点和参数。典型示例如下:
cs
<launch>
<node pkg="turtlesim" type="turtlesim_node" name="sim"/>
<node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
<param name="scale_linear" value="2" type="double"/>
<param name="scale_angular" value="2" type="double"/>
<rosparam file="$(find pkg)/config/params.yaml" command="load"/>
</launch>
ROS2的launch.py文件是基于Python脚本使用LaunchDescription类,通过函数和对象定义启动行为。典型示例如下:
cs
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='turtlesim',
executable='turtlesim_node',
name='sim'
),
Node(
package='turtlesim',
executable='turtle_teleop_key',
name='teleop',
output='screen'
)
])
所有的cpp文件和hpp文件修改完成后,还需要修改对应的CMakeList.txt和Package.xml文件然后对工作空间进行编译。在此编译过程间会遇到很多报错问题,需要逐个解决,这里没有记录,不再叙述。

最终改完所有报错信息。如上图所示,编译成功
导入data/map.pcd文件启动rviz,如下图所示(应该是移植成功):
