您正在阅读的是旧版本但仍受支持的 ROS 2 文档。 Jazzy.
迁移 C++ 软件包
构建工具
而不是使用 catkin_make
, catkin_make_isolated
或 柔荑花序 构建
ROS 2 使用命令行工具 胶管 来构建和安装一组软件包。请参见 初学者教程 开始 胶管
.
构建系统
ROS 2 中的构建系统称为 动情.Ament 基于 CMake 生成: ament_cmake
提供了 CMake 函数,使编写 CMakeLists.txt
文件更容易。
更新 CMakeLists.txt 使用 ament_cmake
作出以下更改以使用 ament_cmake
而不是 柔荑花序
:
中设置构建类型
package.xml
文件导出部分:<export>; <构建类型>;ament_cmake</build_type>; </export>;
更换
查找软件包
调用柔荑花序
和组件
用:查找软件包(ament_cmake 要求) 查找软件包(组件1 要求) # ... 查找软件包(分量N 要求)
移动和更新
柔荑包装
调用:调用
ament_package
而是 后 所有目标都已注册。唯一有效的论据是 ament_package 是
CONFIG_EXTRAS
.所有其他参数都由单独的函数涵盖,这些函数都需要调用 之前ament_package
:而不是通过
CATKIN_DEPENDS ...
致电ament_export_dependencies(...)
之前。而不是通过
INCLUDE_DIRS ...
致电ament_export_include_directories(...)
之前。而不是通过
图书馆 ...
致电ament_export_libraries(...)
之前。
替换调用
添加信息文件
,添加服务文件
和生成消息
与 生成接口.第一个参数是
目标名称
.如果您只创建一个图书馆,那么${project_name} 项目名称
随后是相对于软件包根目录的信息文件名列表。
如果要多次使用文件名列表,建议编写一个信息文件列表,并将该列表传递给函数,这样会更清晰。
最后的多值关键字参数 fpr
生成消息
是依赖
需要依赖的信息包列表。生成接口(${项目名称} ${msg_files} 依赖 std_msgs )
删除任何出现的 开发空间.相关 CMake 变量,如
catkin_devel_prefix
不复存在。"(《世界人权宣言》)
CATKIN_DEPENDS
和取决于
参数传递给新函数 ament_export_dependencies.catkin_global_bin_destination
:箱柜
catkin_global_include_destination
:包括
catkin_global_lib_destination
:lib
catkin_global_libexec_destination
:lib
catkin_global_share_destination
:份额
catkin_package_bin_destination
:lib/${PROJECT_NAME}
catkin_package_include_destination
:include/${PROJECT_NAME}
catkin_package_lib_destination
:lib
柔荑包装共享目的地
:共享/${PROJECT_NAME}。
单元测试
如果使用的是 gtest:
更换 catkin_enable_testing
与 构建测试
.替换 catkin_add_gtest
与 添加测试
.
- 如果 (CATKIN_ENABLE_TESTING)
- find_package(GTest REQUIRED) # 或 rostest
- include_directories(${GTEST_INCLUDE_DIRS})
- catkin_add_gtest(${PROJECT_NAME}-some-test src/test/some_test.cpp)
- target_link_libraries(${PROJECT_NAME}-some-test
- ${PROJECT_NAME}_some_dependency
- ${catkin_LIBRARIES}
- ${gtest_libraries})
- endif()
+ if (BUILD_TESTING)
+ find_package(ament_cmake_gtest REQUIRED)
+ ament_add_gtest(${PROJECT_NAME}-some-test src/test/test_something.cpp)
+ ament_target_dependencies(${PROJECT_NAME)-some-test
+ "rclcpp";
+ "std_msgs")
+ target_link_libraries(${PROJECT_NAME}-some-test
+ ${PROJECT_NAME}_some_dependency)
+ endif()
添加 <test_depend>amment_cmake_gtest</test_depend>;
到您的 package.xml
.
- <test_depend>rostest</test_depend>;
+ <测试依赖>amment_cmake_gtest</test_depend>;
林特斯
在 ROS 2 中,我们正在努力使用衬垫来维护代码的整洁。不同语言的样式在我们的 开发人员指南.
如果你要从头开始一个项目,建议遵循样式指南,并通过添加下面这几行来开启自动林特单元测试 if(BUILD_TESTING)
:
查找软件包(自动 要求)
ament_lint_auto_find_test_dependencies()
您还需要将以下依赖项添加到您的 package.xml
:
<test_depend>;自动</test_depend>;
<test_depend>;ament_lint_common</test_depend>;
更新源代码
信息、服务和行动
ROS 2 消息、服务和操作的命名空间使用子命名空间 (信息
, 服务
或 行动
分别)放在软件包名称之后。因此,include 看起来像 #include <my_interfaces/msg/my_message.hpp>;
.然后对 C++ 类型进行命名: my_interfaces::msg::MyMessage
.
共享指针类型作为类型定义在消息结构体中提供: my_interfaces::msg::MyMessage::SharedPtr
以及 my_interfaces::msg::MyMessage::ConstSharedPtr
.
更多详情,请参阅有关 生成的 C++ 界面.
迁移要求通过以下方式进行更改:
插入子文件夹
信息
在软件包名称和消息数据类型之间将包含的文件名从驼峰字母分隔改为下划线分隔
从
*.h
至*.hpp
// ROS 1 的样式在注释中,ROS 2 紧随其后,未加注释。
// # include <geometry_msgs/PointStamped.h>;
#include <geometry_msgs/msg/point_stamped.hpp>;
// geometry_msgs::PointStamped point_stamped;
几何参数::信息::点戳 点戳记;
迁移需要代码插入 信息
命名空间到所有实例中。
服务对象的使用
ROS 2 中的服务回调没有布尔返回值。建议不要在失败时返回 false,而是抛出异常。
// ROS 1 的样式在注释中,ROS 2 紧随其后,未加注释。
// #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)
空白 服务回调(
缢 标准::共享_ptr<;nav_msgs::服务::获取地图::要求>; 要求,
标准::共享_ptr<;nav_msgs::服务::获取地图::回应>; 回应)
{
// ...
// 返回 true; // 失败则返回 false
}
ros::Time 的用法
用于 ros::时间
:
替换所有
ros::时间
与rclcpp::Time
如果您的信息或代码使用了 std_msgs::Time.Time,那么您就会发现
将 std_msgs::Time 的所有实例转换为 builtin_interfaces::msg::Time
转换所有
#include "std_msgs/time.h
至#include "builtin_interfaces/msg/time.hpp";
使用 std_msgs::Time 字段转换所有实例
nsec
到内置接口::msg::时间字段纳米
ros::Rate 的用法
有一个等价类型 rclcpp::Rate
对象,它基本上可以替代 ros::Rate
.
提升
之前由 Boost 提供的许多功能已被集成到 C++ 标准库中。因此,我们希望利用新的核心功能,尽可能避免对 boost 的依赖。
线程/特例
ROS 代码库中另一个常用的 boost 部分是 boost::线程
.
更换
boost::mutex::scoped_lock(锁定)
与std::unique_lock<std::mutex>;
更换
boost::mutex
与std::mutex
更换
#include <boost/thread/mutex.hpp>;
与#include mutex>;
无序地图
更换:
#include <boost/unordered_map.hpp>;
与#include <unordered_map>;
boost::unordered_map
与std::unordered_map
功能
更换:
#include <boost/function.hpp>;
与#include 功能强大;
boost::function
与std::function
示例:将现有的 ROS 1 软件包转换为 ROS 2
假设我们有一个简单的 ROS 1 软件包,名为 话匣子
使用 roscpp
在一个节点中,称为 话匣子
.该软件包在 catkin 工作区中,位于 ~/ros1_talker
.
ROS 1 代码
这是我们柔荑花序工作区的目录布局:
$ CD ~/ros1_talker $ 找到 ../src ./src/talker ./src/talker/package.xml ./src/talker/CMakeLists.txt ./src/talker/talker.cpp
以下是这三个文件的内容:
src/talker/package.xml
:
<package>;
<名称>;话匣子</名称>;
版本>;0.0.0</version>;
<描述>;话匣子</description>;
维护者 电子邮件="[email protected]";>;布莱恩 格基维护人员</maintainer>;
许可证阿帕奇 2.0</license>;
构建工具的依赖关系<buildtool_depend>;柔荑花序</buildtool_depend>;
构建依赖关系;roscpp</build_depend>;
构建依赖关系;std_msgs</build_depend>;
<run_depend>;roscpp</run_depend>;
<run_depend>;std_msgs</run_depend>;
</package>;
src/talker/CMakeLists.txt
:
cmake_minimum_required(版本 2.8.3)
项目(话匣子)
查找软件包(柔荑花序 要求 组件 roscpp std_msgs)
柔荑包装()
包含目录(${catkin_INCLUDE_DIRS})
添加可执行(话匣子 talker.cpp)
目标链接库(话匣子 ${柔荑_藏书})
安装(目标 话匣子
运行时间 目的地 ${catkin_package_bin_destination})
src/talker/talker.cpp
:
#include 流>;
#include "ros/ros.h";
#include "std_msgs/String.h";
int 主要(int 参数, 烧焦 **参数)
{
玫瑰::启动(参数, 参数, 健谈;);
玫瑰::节点句柄 n;
玫瑰::出版商 喋喋不休_pub = n.登广告<;std_msgs::字符串>;(唠叨";, 1000);
玫瑰::费率 循环速率(10);
int 计数 = 0;
std_msgs::字符串 信息;
虽然 (玫瑰::好的())
{
标准::字符串流 ss;
ss <<; 你好世界; <<; 计数++;
信息.数据 = ss.字符串();
ROS_INFO("%s";, 信息.数据.c_str());
喋喋不休_pub.发布(信息);
玫瑰::自旋一次();
循环速率.睡眠();
}
返回 0;
}
构建 ROS 1 代码
我们先创建一个环境设置文件(本例中使用 bash 为 Noetic 创建环境设置文件),然后使用 catkin_make 安装
:
. /opt/ros/noetic/setup.bash
CD ~/ros1_talker catkin_make 安装
运行 ROS 1 节点
如果还没有运行,我们会启动一个 玫瑰核
首先从我们的 柔荑花序
安装树(位于
/opt/ros/noetic/setup.bash
在这里也适用):
. ~/ros1_talker/install/setup.bash roscore
在另一个 shell 中,我们从 柔荑花序
使用安装空间
玫瑰
在这种情况下,它必须是我们工作区中的文件):
. ~/ros1_talker/install/setup.bash rosrun 话匣子 话匣子
迁移到 ROS 2
首先,让我们创建一个新的工作区:
mkdir ~/ros2_talker
CD ~/ros2_talker
我们将把 ROS 1 软件包中的源代码树复制到该工作区,然后对其进行修改:
mkdir src cp -a ~/ros1_talker/src/talker 来源
现在我们要修改节点中的 C++ 代码。ROS 2 C++ 库名为 rclcpp
提供了与 roscpp
.这两个库的概念非常相似,因此更改起来相当简单。
包括页眉
代替 ros/ros.h
这使我们能够访问 roscpp
库应用程序接口,我们需要将 rclcpp/rclcpp.hpp
这样我们就可以访问 rclcpp
库应用程序接口:
//#include "ros/ros.h";
#include "rclcpp/rclcpp.hpp";
要获得 std_msgs/String
消息定义,以代替
std_msgs/String.h
我们需要包括 std_msgs/msg/string.hpp
:
//#include "std_msgs/String.h";
#include "std_msgs/msg/string.hpp";
更改 C++ 库调用
我们不再将节点名称传递给库初始化调用,而是先进行初始化,然后将节点名称传递给节点对象的创建:
// ros::init(argc, argv, "talker");
// ros::NodeHandle n;
rclcpp::启动(参数, 参数);
汽车 网站 = rclcpp::节点::共享(健谈;);
发布者和费率对象的创建过程非常相似,只是名称空间和方法的名称有些变化。
// ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
// ros::Rate loop_rate(10);
汽车 喋喋不休_pub = 网站->;创建出版商<;std_msgs::信息::字符串>;(唠叨";,
1000);
rclcpp::费率 循环速率(10);
为了进一步控制信息传送的处理方式,服务质量 (服务质量
) 配置文件。默认配置文件是 rmw_qos_profile_default
.更多详情,请参阅
设计文件
和 概念概述.
在命名空间中,外发消息的创建是不同的:
// std_msgs::String msg;
std_msgs::信息::字符串 信息;
代替 ros::ok()
我们称之为 rclcpp::ok()
:
// while (ros::ok())
虽然 (rclcpp::好的())
在发布循环中,我们访问 数据
字段:
信息.数据 = ss.字符串();
要打印控制台信息,请不要使用 ROS_INFO()
我们使用
RCLCPP_INFO()
及其各种同类产品。主要区别在于 RCLCPP_INFO()
的第一个参数是一个日志记录器对象。
// ROS_INFO("%s", msg.data.c_str());
RCLCPP_INFO(网站->;get_logger(), "%s\n";, 信息.数据.c_str());
发布信息的方式和以前一样:
喋喋不休_pub->;发布(信息);
旋转(即让通信系统处理任何待处理的传入/传出信息)的不同之处在于,调用现在将节点作为参数:
// ros::spinOnce();
rclcpp::旋转(网站);
使用速率对象进行睡眠的情况不变。
综上所述,新的 talker.cpp
看起来是这样的
#include 流>;
// #include "ros/ros.h";
#include "rclcpp/rclcpp.hpp";
// #include "std_msgs/String.h";
#include "std_msgs/msg/string.hpp";
int 主要(int 参数, 烧焦 **参数)
{
// ros::init(argc, argv, "talker");
// ros::NodeHandle n;
rclcpp::启动(参数, 参数);
汽车 网站 = rclcpp::节点::共享(健谈;);
// ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
// ros::Rate loop_rate(10);
汽车 喋喋不休_pub = 网站->;创建出版商<;std_msgs::信息::字符串>;(唠叨";, 1000);
rclcpp::费率 循环速率(10);
int 计数 = 0;
// std_msgs::String msg;
std_msgs::信息::字符串 信息;
// while (ros::ok())
虽然 (rclcpp::好的())
{
标准::字符串流 ss;
ss <<; 你好世界; <<; 计数++;
信息.数据 = ss.字符串();
// ROS_INFO("%s", msg.data.c_str());
RCLCPP_INFO(网站->;get_logger(), "%s\n";, 信息.数据.c_str());
喋喋不休_pub->;发布(信息);
// ros::spinOnce();
rclcpp::旋转(网站);
循环速率.睡眠();
}
返回 0;
}
改变 package.xml
ROS 2 使用更新版本的 柔荑花序
称为 ament_cmake
我们在
buildtool_depend
标签
<!-- <buildtool_depend>catkin</buildtool_depend> -->;
构建工具的依赖关系<buildtool_depend>;ament_cmake</buildtool_depend>;
在我们的构建依赖项中,用 roscpp
我们使用 rclcpp
提供了我们使用的 C++ 应用程序接口。
<!-- <build_depend>roscpp</build_depend> -->;
构建依赖关系;rclcpp</build_depend>;
我们在运行依赖项中添加了同样的内容,并从
运行依赖
标记到 执行依赖
标记(软件包格式升级到第 2 版的一部分):
<!-- <run_depend>roscpp</run_depend> -->;
执行依赖关系;rclcpp</exec_depend>;
<!-- <run_depend>std_msgs</run_depend> -->;
执行依赖关系;std_msgs</exec_depend>;
在 ROS 1 中,我们使用 <依赖>;
来简化编译时和运行时的依赖关系。我们也可以在 ROS 2 中这样做:
<依赖>;rclcpp依赖</depend>;
<依赖>;std_msgs依赖</depend>;
我们还需要告诉构建工具 一种 这样它就知道如何构建我们。因为我们使用 动情
和 CMake,我们添加以下几行来声明我们的编译类型是 ament_cmake
:
<export>;
<构建类型>;ament_cmake</build_type>;
</export>;
综上所述,我们的 package.xml
现在看起来是这样的
<!-- <package> -->;
<package 格式="2";>;
<名称>;话匣子</名称>;
版本>;0.0.0</version>;
<描述>;话匣子</description>;
维护者 电子邮件="[email protected]";>;布莱恩 格基维护人员</maintainer>;
许可证阿帕奇 许可证 2.0</license>;
<!-- <buildtool_depend>catkin</buildtool_depend> -->;
构建工具的依赖关系<buildtool_depend>;ament_cmake</buildtool_depend>;
<!-- <build_depend>roscpp</build_depend> -->;
<!-- <run_depend>roscpp</run_depend> -->;
<!-- <run_depend>std_msgs</run_depend> -->;
<依赖>;rclcpp依赖</depend>;
<依赖>;std_msgs依赖</depend>;
<export>;
<构建类型>;ament_cmake</build_type>;
</export>;
</package>;
更改 CMake 代码
ROS 2 依赖于更高版本的 CMake:
#cmake_minimum_required(VERSION 2.8.3)
cmake_minimum_required(版本 3.5)
ROS 2 依赖于 C++17 标准。根据您使用的编译器,默认情况下可能不会启用对 C++17 的支持。请在文件顶部添加此行,明确启用对 C++17 的支持:
设置(cmake_cxx_standard 17)
在所有平台上的首选工作方式是这样的:
如果(不是 cmake_cxx_standard)
设置(cmake_cxx_standard 17)
endif()
如果(cmake_compiler_is_gnucxx 或 cmake_cxx_compiler_id 比赛 "Clang";)
添加编译选项(-墙 -Wextra -Wpedantic)
endif()
使用 柔荑花序
,我们以 组件
在最初发现 柔荑花序
它本身。与 ament_cmake
,我们逐个找到每个软件包,从 ament_cmake
:
#find_package(catkin 必要组件 roscpp std_msgs)
查找软件包(ament_cmake 要求)
查找软件包(rclcpp 要求)
查找软件包(std_msgs 要求)
可以像以前一样找到系统依赖项:
查找软件包(提升 要求 组件 系统 文件系统 线程)
我们称之为 catkin_package()
来自动生成诸如 CMake 配置文件之类的东西,供其他使用我们软件包的软件包使用。而调用 之前 我们现在调用类似的 ament_package()
后 目标:
# catkin_package()
# 在文件底部:
ament_package()
唯一需要手动包含的目录是本地目录和非软件包的依赖项:
#include_directories(${catkin_INCLUDE_DIRS})
包含目录(包括 ${Boost_INCLUDE_DIRS})
更好的办法是为每个目标分别指定包含目录,而不是包含所有目标的所有目录:
目标包含目录(目标 公众 包括 ${Boost_INCLUDE_DIRS})
与我们分别找到每个依赖包的方法类似,我们需要将每个依赖包链接到联编目标。要与作为附加软件包的依赖软件包链接,请不要使用
target_link_libraries()
, ament_target_dependencies()
是一种更简洁、更彻底的构建标记处理方法。它会自动处理在
_INCLUDE_DIRS
中定义的链接库 _图书
.
#target_link_libraries(talker ${catkin_LIBRARIES})
ament_target_dependencies(话匣子
rclcpp
std_msgs)
链接非附件软件包,如系统依赖包,如 提升
或在同一地点建造图书馆 CMakeLists.txt
使用
target_link_libraries()
:
目标链接库(目标 ${Boost_LIBRARIES})
用于安装、 柔荑花序
定义了如下变量 catkin_package_bin_destination
.与 ament_cmake
,我们只需给出一个相对于安装根目录的路径:
#install(TARGETS talker
# 运行时目的地 ${catkin_package_bin_destination})
安装(目标 话匣子
目的地 lib/${项目名称})
作为选项,我们可以为下游软件包安装和导出包含的目录:
安装(目录 包括
目的地 包括)
ament_export_include_directories(包括)
作为选项,我们可以导出下游软件包的依赖关系:
ament_export_dependencies(std_msgs)
综上所述,新的 CMakeLists.txt
看起来是这样的
#cmake_minimum_required(VERSION 2.8.3)
cmake_minimum_required(版本 3.5)
项目(话匣子)
如果(不是 cmake_cxx_standard)
设置(cmake_cxx_standard 17)
endif()
如果(cmake_compiler_is_gnucxx 或 cmake_cxx_compiler_id 比赛 "Clang";)
添加编译选项(-墙 -Wextra -Wpedantic)
endif()
#find_package(catkin 必要组件 roscpp std_msgs)
查找软件包(ament_cmake 要求)
查找软件包(rclcpp 要求)
查找软件包(std_msgs 要求)
#catkin_package()
#include_directories(${catkin_INCLUDE_DIRS})
包含目录(包括)
添加可执行(话匣子 talker.cpp)
#target_link_libraries(talker ${catkin_LIBRARIES})
ament_target_dependencies(话匣子
rclcpp
std_msgs)
#install(TARGETS talker
# 运行时目的地 ${catkin_package_bin_destination})
安装(目标 话匣子
目的地 lib/${项目名称})
安装(目录 包括
目的地 包括)
ament_export_include_directories(包括)
ament_export_dependencies(std_msgs)
ament_package()
构建 ROS 2 代码
我们将一个环境设置文件作为源文件(在本例中,环境设置文件是根据 ROS 2 安装教程生成的,它以 ~/ros2_ws
然后我们使用 胶管 构建
:
. ~/ros2_ws/install/setup.bash
CD ~/ros2_talker colcon 构建
运行 ROS 2 节点
因为我们安装了 话匣子
将可执行文件放到正确的目录中,然后从安装树中找到设置文件,运行即可调用:
. ~/ros2_ws/install/setup.bash ros2 运行 话匣子 话匣子