C++ 软件包迁移参考
本页面介绍了如何将 C++ 软件包的各部分从 ROS 1 迁移到 ROS 2。 如果是首次迁移 C++ 软件包,建议先阅读 C++ 迁移示例,再以本页面作为参考来迁移自己的软件包。
ROS 2 不再使用 catkin_make、catkin_make_isolated 或 catkin build,而是使用命令行工具 colcon 来构建和安装一组软件包。
请参阅入门教程了解 colcon 的基本用法。
更新 CMakeLists.txt 以使用 ament_cmake
Section titled “更新 CMakeLists.txt 以使用 ament_cmake”ROS 2 C++ 软件包使用 CMake 以及 ament_cmake 提供的便利函数。
以下是改用 ament_cmake 替代 catkin 所需的更改。
要求使用更新版本的 CMake
Section titled “要求使用更新版本的 CMake”ROS 2 依赖于比 ROS 1 更高版本的 CMake。
在 REP 2000 中查找目标 ROS 发行版的最低 CMake 版本要求,并在 CMakeLists.txt 顶部指定该版本。
例如,3.14.4 是 ROS Humble 的最低推荐支持版本。
cmake_minimum_required(VERSION 3.20)将构建类型设置为 ament_cmake
Section titled “将构建类型设置为 ament_cmake”从 package.xml 中删除对 catkin 的任何依赖:
# Remove this!<buildtool_depend>catkin</buildtool_depend>添加对 ament_cmake_ros 的新依赖(示例):
<buildtool_depend>ament_cmake_ros</buildtool_depend>如果 package.xml 中还没有 <export> 部分,则添加一个。
将 <build_type> 设置为 ament_cmake(示例)
<export> <build_type>ament_cmake</build_type></export>添加 ament_package() 调用
Section titled “添加 ament_package() 调用”在 CMakeLists.txt 底部插入 ament_package() 调用(示例)
# Add this to the bottom of your CMakeLists.txtament_package()更新 find_package() 调用
Section titled “更新 find_package() 调用”将 find_package(catkin COMPONENTS ...) 调用替换为单独的 find_package() 调用(示例)。
例如,将以下代码:
find_package(catkin REQUIRED COMPONENTS foo bar std_msgs)find_package(baz REQUIRED)改为:
find_package(ament_cmake_ros REQUIRED)find_package(foo REQUIRED)find_package(bar REQUIRED)find_package(std_msgs REQUIRED)find_package(baz REQUIRED)使用现代 CMake targets
Section titled “使用现代 CMake targets”建议优先使用 per-target 的 CMake 函数,以便软件包可以导出现代 CMake targets。
如果 CMakeLists.txt 中使用了 include_directories(),请删除这些调用。
# Delete calls to include_directories like this one!include_directories(include ${catkin_INCLUDE_DIRS})为软件包中的每个库添加 target_include_directories() 调用(示例)。
target_include_directories(my_library PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:include/${PROJECT_NAME}>")将所有 target_link_libraries() 调用改为使用现代 CMake targets。
例如,如果 ROS 1 软件包使用了旧式 CMake 变量,如下所示:
target_link_libraries(my_library ${catkin_LIBRARIES} ${baz_LIBRARIES})则改为使用具体的现代 CMake targets:
target_link_libraries(my_library PUBLIC foo::foo bar::bar std_msgs::std_msgs baz::baz)根据依赖项在库中的使用方式,选择 PUBLIC 或 PRIVATE(示例)。
- 如果下游用户也需要该依赖项(例如库的公共 API 使用了它),则选择
PUBLIC。 - 如果依赖项仅在库内部使用,则选择
PRIVATE。
用各种 ament_cmake 调用替换 catkin_package()
Section titled “用各种 ament_cmake 调用替换 catkin_package()”假设 CMakeLists.txt 中有如下 catkin_package 调用:
catkin_package( INCLUDE_DIRS include LIBRARIES my_library CATKIN_DEPENDS foo bar std_msgs DEPENDS baz)
install(TARGETS my_library ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION})替换 catkin_package(INCLUDE_DIRS ...)
Section titled “替换 catkin_package(INCLUDE_DIRS ...)”如果已经使用了现代 CMake targets 和 target_include_directories(),则无需再做其他处理。
下游用户通过依赖现代 CMake targets 即可获取 include 目录。
替换 catkin_package(LIBRARIES ...)
Section titled “替换 catkin_package(LIBRARIES ...)”使用 ament_export_targets() 和 install(TARGETS ... EXPORT ...) 来替换 LIBRARIES 参数。
安装 my_library target 时使用 EXPORT 关键字(示例)。
install(TARGETS my_library EXPORT export_my_package ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin)以上是库 targets 的推荐默认设置。
如果软件包使用了不同的 CATKIN_*_DESTINATION 变量,请按以下方式转换:
| catkin | ament_cmake |
|---|---|
| CATKIN_GLOBAL_BIN_DESTINATION | bin |
| CATKIN_GLOBAL_INCLUDE_DESTINATION | include |
| CATKIN_GLOBAL_LIB_DESTINATION | lib |
| CATKIN_GLOBAL_LIBEXEC_DESTINATION | lib |
| CATKIN_GLOBAL_SHARE_DESTINATION | share |
| CATKIN_PACKAGE_BIN_DESTINATION | lib/${PROJECT_NAME} |
| CATKIN_PACKAGE_INCLUDE_DESTINATION | include/${PROJECT_NAME} |
| CATKIN_PACKAGE_LIB_DESTINATION | lib |
| CATKIN_PACKAGE_SHARE_DESTINATION | share/${PROJECT_NAME} |
添加 ament_export_targets() 调用,名称与 EXPORT 关键字指定的名称相同(示例)。
ament_export_targets(export_my_package)替换 catkin_package(CATKIN_DEPENDS .. DEPENDS ..)
Section titled “替换 catkin_package(CATKIN_DEPENDS .. DEPENDS ..)”下游软件包的用户必须能够 find_package() 软件包公共 API 所使用的依赖项。
在 ROS 1 中,这是通过 CATKIN_DEPENDS 和 DEPENDS 参数为下游用户自动完成的。
在 ROS 2 中,请使用 ament_export_dependencies 来实现。
ament_export_dependencies( foo bar std_msgs baz)如果软件包同时包含 C++ 代码和 ROS 消息、服务或动作定义,建议将其拆分为两个软件包:
- 一个仅包含 ROS 消息、服务和/或动作定义的软件包
- 一个包含 C++ 代码的软件包
在包含 ROS 消息的软件包的 package.xml 中添加以下依赖项:
-
添加对
rosidl_default_generators的<buildtool_depend>(示例)<buildtool_depend>rosidl_default_generators</buildtool_depend> -
添加对
rosidl_default_runtime的<exec_depend>(示例)<exec_depend>rosidl_default_runtime</exec_depend> -
添加一个
<member_of_group>标签,组名为rosidl_interface_packages(示例)<member_of_group>rosidl_interface_packages</member_of_group>
在你的 CMakeLists.txt 中,用 rosidl_generate_interfaces 替换 add_message_files、add_service_files 和 generate_messages 的调用。
由于此 bug,第一个参数必须是 ${PROJECT_NAME}。
例如,如果你的 ROS 1 软件包如下所示:
add_message_files(DIRECTORY msg FILES FooBar.msg Baz.msg)add_service_files(DIRECTORY srv FILES Ping.srv)
add_action_files(DIRECTORY action FILES DoPong.action)generate_messages( DEPENDENCIES actionlib_msgs std_msgs geometry_msgs)则将其更改为以下内容(示例)
rosidl_generate_interfaces(${PROJECT_NAME} "msg/FooBar.msg" "msg/Baz.msg" "srv/Ping.srv" "action/DoPong.action" DEPENDENCIES actionlib_msgs std_msgs geometry_msgs )删除对 devel 空间的引用
Section titled “删除对 devel 空间的引用”删除对 devel 空间 的任何引用,例如 CATKIN_DEVEL_PREFIX。
ROS 2 中没有 devel 空间 的等效物。
如果你的软件包使用 gtest,则:
- 将
CATKIN_ENABLE_TESTING替换为BUILD_TESTING。 - 将
catkin_add_gtest替换为ament_add_gtest。 - 为
ament_cmake_gtest而非GTest添加find_package()
例如,如果你的 ROS 1 软件包如下添加测试:
if (CATKIN_ENABLE_TESTING) find_package(GTest REQUIRED) include_directories(${GTEST_INCLUDE_DIRS}) catkin_add_gtest(my_test src/test/some_test.cpp) target_link_libraries(my_test # ... ${GTEST_LIBRARIES}) endif()则将其更改为:
if (BUILD_TESTING) find_package(ament_cmake_gtest REQUIRED) ament_add_gtest(my_test src/test/test_something.cpp) target_link_libraries(my_test #... ) endif()在你的 package.xml 中添加 <test_depend>ament_cmake_gtest</test_depend>(示例)。
<test_depend>ament_cmake_gtest</test_depend>代码检查器(Linters)
Section titled “代码检查器(Linters)”ROS 2 代码的风格指南与 ROS 1 不同。
如果你选择遵循 ROS 2 风格指南,则通过在 if(BUILD_TESTING) 块中添加以下行来启用自动 linter 测试:
if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() # ...endif()在你的 package.xml 中添加以下依赖项:
<test_depend>ament_lint_auto</test_depend><test_depend>ament_lint_common</test_depend>消息、服务和动作
Section titled “消息、服务和动作”ROS 2 消息、服务和动作的命名空间在软件包名称之后使用子命名空间(分别为 msg、srv 或 action)。
因此 include 看起来像:#include <my_interfaces/msg/my_message.hpp>。
C++ 类型随后命名为:my_interfaces::msg::MyMessage。
共享指针类型在消息结构体中作为 typedef 提供:my_interfaces::msg::MyMessage::SharedPtr 以及 my_interfaces::msg::MyMessage::ConstSharedPtr。
有关更多详细信息,请参阅关于生成的 C++ 接口的文章。
迁移需要 include 做以下更改:
- 在软件包名称和消息数据类型之间插入子文件夹
msg - 将包含的文件名从 CamelCase 更改为下划线分隔
- 从
*.h更改为*.hpp
// ROS 1 style is in comments, ROS 2 follows, uncommented.// # include <geometry_msgs/PointStamped.h>#include <geometry_msgs/msg/point_stamped.hpp>
// geometry_msgs::PointStamped point_stamped;geometry_msgs::msg::PointStamped point_stamped;迁移需要代码在所有实例中插入 msg 命名空间。
服务对象的使用
Section titled “服务对象的使用”ROS 2 中的服务回调没有布尔返回值。 不是在失败时返回 false,而是建议抛出异常。
// ROS 1 style is in comments, ROS 2 follows, uncommented.// #include "nav_msgs/GetMap.h"#include "nav_msgs/srv/get_map.hpp"
// bool service_callback(// nav_msgs::GetMap::Request & request,// nav_msgs::GetMap::Response & response)void service_callback( const std::shared_ptr<nav_msgs::srv::GetMap::Request> request, std::shared_ptr<nav_msgs::srv::GetMap::Response> response){ // ... // return true; // or false for failure}ros::Time 的使用
Section titled “ros::Time 的使用”对于 ros::Time 的使用:
-
将所有
ros::Time实例替换为rclcpp::Time -
如果你的消息或代码使用了 std_msgs::Time:
-
将所有 std_msgs::Time 实例转换为 builtin_interfaces::msg::Time
-
将所有
#include "std_msgs/time.h"转换为#include "builtin_interfaces/msg/time.hpp" -
将所有使用 std_msgs::Time 字段
nsec的实例转换为 builtin_interfaces::msg::Time 字段nanosec
-
ros::Rate 的使用
Section titled “ros::Rate 的使用”有一个等效类型 rclcpp::Rate 对象,它基本上是 ros::Rate 的直接替代品。
Boost 以前提供的许多功能已被集成到 C++ 标准库中。 因此,我们希望利用新的核心功能,并尽可能避免对 boost 的依赖。
要将共享指针从 boost 切换到标准 C++,请替换以下实例:
#include <boost/shared_ptr.hpp>替换为#include <memory>boost::shared_ptr替换为std::shared_ptr
还可能有 weak_ptr 等变体也需要转换。
此外,推荐使用 using 而不是 typedef。
using 在模板化逻辑中能更好地工作。
详情请参见这里
线程/互斥锁
Section titled “线程/互斥锁”ROS 代码库中使用的 boost 的另一个常见部分是 boost::thread 中的互斥锁。
- 将
boost::mutex::scoped_lock替换为std::unique_lock<std::mutex> - 将
boost::mutex替换为std::mutex - 将
#include <boost/thread/mutex.hpp>替换为#include <mutex>
无序映射(Unordered Map)
Section titled “无序映射(Unordered Map)”替换:
#include <boost/unordered_map.hpp>替换为#include <unordered_map>boost::unordered_map替换为std::unordered_map
function
Section titled “function”替换:
#include <boost/function.hpp>替换为#include <functional>boost::function替换为std::function