Ubuntu 22.04 + ROS 2 Humble 项目通过 TRAE 改成 Ubuntu 202.04 + ROS 1 Noetic 项目的SKLL技能

Ubuntu 22.04 + ROS 2 Humble 项目通过 TRAE 改成 Ubuntu 202.04 + ROS 1 Noetic 项目的SKLL技能

此SKILL是通过实例适配后生成的SKLL

SKILL.md

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 项目实际适配经验总结。
相关推荐
小顿的企业观察2 小时前
中企出海战略规划,正在从“走出去”转向“走进去”
大数据·运维·人工智能·产品运营·制造
数智化转型推荐官3 小时前
2026统一身份管理系统五大发展趋势解读
java·运维·微服务
看菜鸡互3 小时前
探索用 SlideML 让大模型生成 PPT 的实验方法
java·运维
大飞记Python4 小时前
Linux命令速查手册(测试开发4年实战总结,附PDF)
linux·网络·pdf
liguojun20255 小时前
篮球馆自动计时收费系统:从规则配置到自动结算的全流程拆解
java·大数据·运维·人工智能·物联网·1024程序员节
Promise微笑5 小时前
红外分辨率 160×120、320×240、384×288 与 640×480实战选型指南
大数据·运维·人工智能·物联网
拥抱太阳06165 小时前
HarmonyOS 应用开发《掌上英语》第2篇:ArkTS 模块化开发:Index.ets 统一导出模式深度解析
pytorch·ubuntu·harmonyos
顾喵6 小时前
PetaLinux 2017.4 实操指南:PREEMPT_RT实时补丁移植+Zynq PL中断UIO用户态配置(超完整避坑版)
linux
小樱花的樱花6 小时前
Linux 线程的创建
linux·c语言·开发语言
云飞云共享云桌面6 小时前
单机建模卡顿运维繁琐!中小型机械制造厂云飞云 3D 云桌面落地
运维·服务器·网络·3d·自动化·电脑·负载均衡