Ubuntu 22.04 + ROS 2 Humble 项目通过 TRAE 改成 Ubuntu 202.04 + ROS 1 Noetic 项目的SKLL技能
此SKILL是通过实例适配后生成的SKLL
bash
---
name: "ros2-to-ros1-adaptation"
description: "Adapts ROS2 C++ projects to ROS1 Noetic. Invoke when user requests ROS1 adaptation or needs to convert ROS2 code to ROS1."
---
# ROS2 to ROS1 Noetic Adaptation Skill
This skill provides a comprehensive guide for adapting ROS2 C++ projects to ROS1 Noetic, ensuring algorithmic logic remains unchanged.
## 适用场景
当用户需要:
- 将 ROS2 项目适配到 ROS1 Noetic
- 修复 ROS2 相关的编译错误
- 转换 ROS2 API 到 ROS1 API
- 保持算法逻辑不变,仅做框架适配
## 适配清单
### 1. 头文件修改
| ROS2 头文件 | ROS1 替换 |
|------------|----------|
| `#include <rclcpp/rclcpp.hpp>` | `#include <ros/ros.h>` |
| `#include <rclcpp/logging.hpp>` | 移除(使用 ros/ros.h) |
| `#include <sensor_msgs/msg/point_cloud2.hpp>` | `#include <sensor_msgs/PointCloud2.h>` |
**示例修复:**
```cpp
// ROS2
#include <rclcpp/logging.hpp>
RCLCPP_INFO(get_logger(), "Value: %d", value);
// ROS1
#include <ros/ros.h>
ROS_INFO("Value: %d", value);
```
### 2. 日志宏修改
| ROS2 | ROS1 |
|------|------|
| `RCLCPP_INFO(logger, ...)` | `ROS_INFO(...)` |
| `RCLCPP_WARN(logger, ...)` | `ROS_WARN(...)` |
| `RCLCPP_ERROR(logger, ...)` | `ROS_ERROR(...)` |
| `RCLCPP_DEBUG(logger, ...)` | `ROS_DEBUG(...)` |
| `RCLCPP_INFO_STREAM(logger << x)` | `ROS_INFO("format", args)` |
**重要:ROS1 使用 printf 格式,不支持流式操作符 `<<`**
```cpp
// ROS2 流式日志(错误示例)
RCLCPP_WARN(logger, "丢弃帧:时间=" << time << ",数量=" << count);
// ROS1 printf 格式(正确)
ROS_WARN("丢弃帧:时间=%.3f,数量=%lu", time, (unsigned long)count);
```
### 3. 消息类型修改
| ROS2 | ROS1 |
|------|------|
| `sensor_msgs::msg::PointCloud2::SharedPtr` | `sensor_msgs::PointCloud2ConstPtr` |
| `sensor_msgs::msg::PointCloud2::ConstSharedPtr` | `sensor_msgs::PointCloud2ConstPtr` |
| `nav_msgs::msg::Odometry::SharedPtr` | `nav_msgs::OdometryConstPtr` |
| `geometry_msgs::msg::PoseStamped` | `geometry_msgs::PoseStamped` |
### 4. Ceres Solver API 修改
ROS1 Noetic 使用 Ceres Solver 1.x,ROS2 Humble 使用 Ceres Solver 2.x:
| ROS2 (Ceres 2.x) | ROS1 (Ceres 1.x) |
|-----------------|------------------|
| `ceres::EigenQuaternionManifold` | `ceres::EigenQuaternionParameterization` |
| `ceres::AutoDiffManifold` | `ceres::AutoDiffCostFunction` |
```cpp
// ROS2
auto *parameterization = new ceres::EigenQuaternionManifold();
problem.SetManifold(&quat[0], parameterization);
// ROS1
auto *parameterization = new ceres::EigenQuaternionParameterization();
problem.AddParameterBlock(&quat[0], 4, parameterization);
```
### 5. 格式字符串修改
`size_t` 类型在不同平台大小不同,ROS1 日志需要显式转换:
```cpp
// ROS2(可能警告)
ROS_INFO("帧数=%zu", frame_count);
// ROS1(跨平台兼容)
ROS_INFO("帧数=%lu", (unsigned long)frame_count);
```
**所有 `%zu` → `%lu` + `(unsigned long)` 类型转换**
### 6. TF 广播器修改
| ROS2 | ROS1 |
|------|------|
| `tf2_ros::TransformBroadcaster` | `tf::TransformBroadcaster` 或保持 `tf2_ros` |
| `tf2_ros::Buffer` | `tf::TransformListener` 或保持 `tf2_ros` |
**注意:ROS1 Noetic 也支持 tf2_ros,可选择性使用**
```cpp
// 方案一:使用 tf(ROS1 传统)
#include <tf/transform_broadcaster.h>
tf::TransformBroadcaster tf_broadcaster_;
// 方案二:保持 tf2_ros(ROS1 Noetic 也支持)
#include <tf2_ros/transform_broadcaster.h>
tf2_ros::TransformBroadcaster tf_broadcaster_;
```
### 7. 时间相关修改
| ROS2 | ROS1 |
|------|------|
| `node->now()` | `ros::Time::now()` |
| `rclcpp::Time` | `ros::Time` |
| `rclcpp::Duration` | `ros::Duration` |
| `RCLCPP_SECONDS` | `ros::Duration(1.0)` |
### 8. 参数获取修改
| ROS2 | ROS1 |
|------|------|
| `node->declare_parameter("name", default)` | `nh.param("name", var, default)` |
| `node->get_parameter("name").as_double()` | `nh.param<double>("name", var, default)` |
```cpp
// ROS2
node->declare_parameter("threshold", 0.5);
double threshold = node->get_parameter("threshold").as_double();
// ROS1
ros::NodeHandle nh("~");
double threshold;
nh.param("threshold", threshold, 0.5);
```
### 9. RViz 配置文件修改
RViz 配置文件(`.rviz`)需要修改插件类名和移除 ROS2 QoS 属性:
| ROS2 类名 | ROS1 类名 |
|-----------|----------|
| `rviz_common/Displays` | `rviz/Displays` |
| `rviz_common/Selection` | `rviz/Selection` |
| `rviz_common/Tool Properties` | `rviz/Tool Properties` |
| `rviz_common/Views` | `rviz/Views` |
| `rviz_common/Time` | `rviz/Time` |
| `rviz_default_plugins/Grid` | `rviz/Grid` |
| `rviz_default_plugins/Axes` | `rviz/Axes` |
| `rviz_default_plugins/Path` | `rviz/Path` |
| `rviz_default_plugins/PointCloud2` | `rviz/PointCloud2` |
| `rviz_default_plugins/TF` | `rviz/TF` |
| `rviz_default_plugins/Odometry` | `rviz/Odometry` |
| `rviz_default_plugins/MarkerArray` | `rviz/MarkerArray` |
| `rviz_default_plugins/Interact` | `rviz/Interact` |
| `rviz_default_plugins/MoveCamera` | `rviz/MoveCamera` |
| `rviz_default_plugins/Select` | `rviz/Select` |
| `rviz_default_plugins/FocusCamera` | `rviz/FocusCamera` |
| `rviz_default_plugins/Measure` | `rviz/Measure` |
| `rviz_default_plugins/SetInitialPose` | `rviz/SetInitialPose` |
| `rviz_default_plugins/SetGoal` | `rviz/SetGoal` |
| `rviz_default_plugins/PublishPoint` | `rviz/PublishPoint` |
| `rviz_default_plugins/Orbit` | `rviz/Orbit` |
**移除 ROS2 QoS 属性(删除以下行):**
- `Durability Policy: Volatile`
- `Reliability Policy: Reliable`
- `History Policy: Keep Last`
### 10. Launch 文件修改
| ROS2 (.launch.py) | ROS1 (.launch) |
|------------------|----------------|
| Python 脚本 | XML 文件 |
| `LaunchDescription()` | `<launch>` |
| `Node(package='pkg', ...)` | `<node pkg="pkg" .../>` |
| `DeclareLaunchArgument` | `<arg name="..." default="..."/>` |
**ROS1 XML Launch 示例:**
```xml
<launch>
<arg name="config" default="$(find pkg_name)/config/default.yaml" />
<arg name="rviz" default="true" />
<node pkg="pkg_name" type="node_name" name="node_name" output="screen">
<param name="config_file_path" value="$(arg config)" />
</node>
<node if="$(arg rviz)" pkg="rviz" type="rviz" name="rviz"
args="-d $(find pkg_name)/launch/config.rviz" />
</launch>
```
### 11. CMakeLists.txt 修改
| ROS2 (ament_cmake) | ROS1 (catkin) |
|--------------------|---------------|
| `find_package(ament_cmake REQUIRED)` | `find_package(catkin REQUIRED)` |
| `ament_target_dependencies(target)` | `catkin_package()` |
| `install(TARGETS target)` | `catkin_install_python()` |
| `ament_package()` | 移除 |
**ROS1 CMakeLists.txt 模板:**
```cmake
cmake_minimum_required(VERSION 3.0.2)
project(package_name)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
sensor_msgs
nav_msgs
geometry_msgs
pcl_conversions
message_filters
)
find_package(Eigen3 REQUIRED)
find_package(PCL REQUIRED)
find_package(Ceres REQUIRED)
catkin_package(
CATKIN_DEPENDS roscpp std_msgs sensor_msgs
)
include_directories(
${catkin_INCLUDE_DIRS}
${EIGEN3_INCLUDE_DIRS}
${PCL_INCLUDE_DIRS}
)
add_executable(node_name src/main.cpp)
target_link_libraries(node_name
${catkin_LIBRARIES}
${CERES_LIBRARIES}
${PCL_LIBRARIES}
)
```
### 12. package.xml 修改
| ROS2 | ROS1 |
|------|------|
| `<build_type>ament_cmake</build_type>` | 移除 |
| `<depend>rclcpp</depend>` | `<depend>roscpp</depend>` |
| format="3" | format="2" 或 format="3" |
**ROS1 package.xml 模板:**
```xml
<?xml version="1.0"?>
<package format="2">
<name>package_name</name>
<version>1.0.0</version>
<description>Description</description>
<maintainer email="user@example.com">User</maintainer>
<license>MIT</license>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>std_msgs</build_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>std_msgs</exec_depend>
<depend>eigen</depend>
<depend>libpcl-all-dev</depend>
</package>
```
## 执行步骤
1. **检查项目结构**:确认是 ROS2 项目
2. **修改头文件**:替换所有 `rclcpp` 头文件
3. **修改日志宏**:转换所有 `RCLCPP_*` 到 `ROS_*`,使用 printf 格式
4. **修改消息类型**:替换 `::msg::` 类型为 ROS1 类型
5. **修改 Ceres API**:如使用 Ceres,替换 Manifold 为 Parameterization
6. **修复格式字符串**:所有 `%zu` 改为 `%lu` 并添加类型转换
7. **修改 TF 库**:根据需要选择 tf 或 tf2_ros
8. **修改 RViz 配置**:替换插件类名,移除 QoS 属性
9. **创建 Launch 文件**:将 `.launch.py` 转换为 `.launch` XML
10. **修改 CMakeLists.txt**:适配 catkin 构建系统
11. **修改 package.xml**:适配 ROS1 依赖格式
12. **编译验证**:`catkin_make_isolated --pkg package_name`
## 注意事项
- **不要修改算法逻辑**:仅做 ROS API 层面的适配
- **保持代码风格一致**:不添加不必要的注释或格式化
- **测试编译**:每步修改后尝试编译,及时发现错误
- **参考已适配项目**:如有成功案例,可参考其修改方式
## 常见错误
| 错误信息 | 解决方案 |
|---------|---------|
| `rclcpp/logging.hpp: No such file` | 移除头文件,使用 `ros/ros.h` |
| `invalid operands to binary 'operator<<'` | 日志宏改用 printf 格式 |
| `EigenQuaternionManifold not found` | 改用 `EigenQuaternionParameterization` |
| `format '%zu' expects 'size_t'` | 改用 `%lu` + `(unsigned long)` |
| `rviz_default_plugins not found` | RViz 配置改用 `rviz/*` 类名 |
| `PGOConfig does not name a type` | 检查结构体定义,确保类型名正确 |
| `pose_time is not a member` | 检查实际成员名,可能是 `.time` |
| `std::filesystem not declared` | 添加 `#include <filesystem>` |
## 快速检查脚本
检查项目中的 ROS2 依赖:
```bash
# 检查 rclcpp 使用
grep -rn "rclcpp" src/
grep -rn "RCLCPP" src/
# 检查 ROS2 消息类型
grep -rn "::msg::" src/
# 检查 Ceres Manifold
grep -rn "Manifold" src/
# 检查格式字符串
grep -rn "%zu" src/
# 检查 RViz 配置
grep -rn "rviz_common" *.rviz
grep -rn "rviz_default_plugins" *.rviz
grep -rn "Durability Policy" *.rviz
grep -rn "Reliability Policy" *.rviz
```
## 成功标志
- 编译无错误:`catkin_make_isolated --pkg package_name` 成功
- RViz 正常加载:无插件加载错误
- 节点正常运行:`roslaunch` 启动无报错
- 数据正常订阅:`rostopic echo` 可查看话题数据
---
此技能基于 ii_nvm_ros1_noetic 项目实际适配经验总结。