【ROS2】Intermediate - 单个进程中组合多个节点
在单个进程中组合多个节点
目录
- 背景
- 运行演示
- 发现可用组件
- 使用ROS服务的运行时组合(发布者和订阅者)
- 使用ROS服务的运行时组合(服务端和客户端)
- 使用ROS服务的编译时组合
- 使用dlopen的运行时组合
- 使用launch动作的组合
- 高级主题
- 卸载组件
- 重映射容器名称和命名空间
- 重映射组件名称和命名空间
- 向组件传递参数值
- 向组件传递额外参数
- 作为共享库的可组合节点
- 组合非节点派生的组件
目标
将多个节点组合到单个进程中。
难度级别:中级
预计时间:20分钟
背景
参见概念文章。
运行演示
演示使用来自 rclcpp_components
、ros2component
和 composition
包的可执行文件,可以用以下命令运行。
发现可用组件
要查看工作空间中已注册且可用的组件,请在终端执行:
ros2 component types
终端会返回所有可用组件的列表:
(... 其他包的组件在此)
compositioncomposition::Talkercomposition::Listenercomposition::Servercomposition::Client
使用ROS服务的运行时组合(发布者和订阅者)
在第一个终端中,启动组件容器:
ros2 run rclcpp_components component_container
在第二个终端中,使用ros2
命令行工具验证容器是否正在运行:
ros2 component list
你应该会看到组件名称:
/ComponentManager
在第二个终端中加载talker组件(见talker源代码):
ros2 component load /ComponentManager composition composition::Talker
该命令会返回已加载组件的唯一ID以及节点名称:
Loaded component 1 into '/ComponentManager' container node as '/talker'
现在第一个终端应该会显示组件已加载的消息,以及不断发布消息的输出。
在第二个终端中运行另一个命令来加载listener组件(见listener源代码):
ros2 component load /ComponentManager composition composition::Listener
终端会返回:
Loaded component 2 into '/ComponentManager' container node as '/listener'
现在可以使用ros2
命令行工具检查容器状态:
ros2 component list
你会看到以下结果:
/ComponentManager1 /talker2 /listener
第一个终端现在应该会显示每条接收消息的重复输出。
使用ROS服务的运行时组合(服务端和客户端)
服务端和客户端的例子非常类似。
在第一个终端中:
ros2 run rclcpp_components component_container
在第二个终端中(见服务端和客户端源代码):
ros2 component load /ComponentManager composition composition::Server
ros2 component load /ComponentManager composition composition::Client
在这种情况下,客户端会向服务端发送请求,服务端处理请求并回复响应,客户端则打印收到的响应。
使用ROS服务的编译时组合
这个演示展示了相同的共享库可以被重用来编译一个运行多个组件的单一可执行文件。该可执行文件包含了上述所有四个组件:talker、listener以及server和client。
在终端中调用(见源代码):
ros2 run composition manual_composition
这应该会同时显示talker/listener对和server/client对的重复消息。
注意:手动组合的组件不会在ros2 component list
命令行工具的输出中显示。
使用dlopen的运行时组合
这个演示提供了运行时组合的另一种方式,通过创建一个通用容器进程并显式传递要加载的库,而不使用ROS接口。该进程会打开每个库并创建库中每个"rclcpp::Node"类的一个实例(见源代码)。
Linux:
ros2 run composition dlopen_composition `ros2 pkg prefix composition`/lib/libtalker_component.so `ros2 pkg prefix composition`/lib/liblistener_component.so
macOS:
ros2 run composition dlopen_composition `ros2 pkg prefix composition`/lib/libtalker_component.dylib `ros2 pkg prefix composition`/lib/liblistener_component.dylib
Windows:
ros2 pkg prefix composition
获取composition的安装路径,然后调用:
ros2 run composition dlopen_composition <path_to_composition_install>\bin\talker_component.dll <path_to_composition_install>\bin\listener_component.dll
现在终端应该会显示每条发送和接收消息的重复输出。
注意:dlopen组合的组件不会在ros2 component list
命令行工具的输出中显示。
使用launch动作的组合
虽然命令行工具对于调试和诊断组件配置很有用,但通常更方便的是同时启动一组组件。为了自动化这个操作,我们可以使用launch文件:
ros2 launch composition composition_demo.launch.py
高级主题
现在我们已经了解了组件的基本操作,可以讨论一些更高级的主题。
卸载组件
在第一个终端中,启动组件容器:
ros2 run rclcpp_components component_container
使用ros2
命令行工具验证容器是否正在运行:
ros2 component list
你应该会看到组件名称:
/ComponentManager
在第二个终端中(见talker源代码),加载组件:
ros2 component load /ComponentManager composition composition::Talker
ros2 component load /ComponentManager composition composition::Listener
使用唯一ID从组件容器中卸载节点:
ros2 component unload /ComponentManager 1 2
终端应该会返回:
Unloaded component 1 from '/ComponentManager' container
Unloaded component 2 from '/ComponentManager' container
在第一个终端中,验证talker和listener的重复消息已经停止。
重映射容器名称和命名空间
可以通过标准命令行参数重映射组件管理器的名称和命名空间:
ros2 run rclcpp_components component_container --ros-args -r __node:=MyContainer -r __ns:=/ns
在第二个终端中,可以使用更新后的容器名称加载组件:
ros2 component load /ns/MyContainer composition composition::Listener
注意:容器的命名空间重映射不会影响已加载的组件。
重映射组件名称和命名空间
可以通过加载命令的参数来调整组件的名称和命名空间。
在第一个终端中,启动组件容器:
ros2 run rclcpp_components component_container
以下是一些重映射名称和命名空间的例子:
重映射节点名称:
ros2 component load /ComponentManager composition composition::Talker --node-name talker2
重映射命名空间:
ros2 component load /ComponentManager composition composition::Talker --node-namespace /ns
同时重映射两者:
ros2 component load /ComponentManager composition composition::Talker --node-name talker3 --node-namespace /ns2
现在使用ros2
命令行工具:
ros2 component list
在控制台中你应该会看到相应的条目:
/ComponentManager1 /talker22 /ns/talker3 /ns2/talker3
注意:容器的命名空间重映射不会影响已加载的组件。
向组件传递参数值
ros2 component load
命令行支持在节点构造时向其传递任意参数。使用方法如下:
ros2 component load /ComponentManager image_tools image_tools::Cam2Image -p burger_mode:=true
向组件传递额外参数
ros2 component load
命令行支持向组件管理器传递特定选项,用于构造节点时使用。目前唯一支持的命令行选项是使用进程内通信实例化节点。使用方法如下:
ros2 component load /ComponentManager composition composition::Talker -e use_intra_process_comms:=true
作为共享库的可组合节点
如果你想从一个包中导出一个可组合节点作为共享库,并在另一个包中使用该节点进行链接时组合,需要在CMake文件中添加代码,以便在下游包中导入实际的目标。
然后安装生成的文件并导出该文件。
实际示例可参见:ROS Discourse - Ament最佳实践:共享库
组合非节点派生的组件
在ROS 2中,组件允许更高效地使用系统资源,并提供了一个强大的特性,使你能够创建不依赖于特定节点的可重用功能。
使用组件的一个优势是,你可以创建非节点派生的功能作为独立的可执行文件或共享库,根据需要加载到ROS系统中。
要创建一个非节点派生的组件,请遵循以下指南:
- 实现一个接受
const rclcpp::NodeOptions&
作为参数的构造函数。 - 实现
get_node_base_interface()
方法,该方法应返回NodeBaseInterface::SharedPtr
。你可以在构造函数中创建一个节点,并使用该节点的get_node_base_interface()
方法来提供此接口。
以下是一个非节点派生组件的示例,它监听一个ROS话题:node_like_listener_component。
https://github.com/ros2/demos/blob/foxy/composition/src/node_like_listener_component.cpp