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
--- 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 项目实际适配经验总结。