在单个进程中组合多个节点
目标: 将多个节点组合成一个进程。
教程级别: 中级
时间: 20 分钟
背景
请参阅 概念文章。
有关如何编写可组合节点的信息,请参阅 本教程。
先决条件
本教程使用 rclcpp_components、ros2component、composition 和 image_tools 包中的可执行文件。如果您已按照适用于您平台的 安装说明 进行操作,则这些应该已经安装。
运行演示
发现可用组件
要查看工作区中已注册且可用的组件,请在 shell 中执行以下操作:
ros2 component types
终端将返回所有可用组件的列表:
(... components of other packages here)
composition
composition::Talker
composition::Listener
composition::NodeLikeListener
composition::Server
composition::Client
(... components of other packages here)
使用带有发布者和订阅者的 ROS 服务进行运行时组合
在第一个 shell 中,启动组件容器:
ros2 run rclcpp_components component_container
打开第二个shell并通过“ros2”命令行工具验证容器是否正在运行:
ros2 component list
您应该看到该组件的名称:
/ComponentManager
在第二个 shell 中加载 talker 组件 (see talker source code):
ros2 component load /ComponentManager composition composition::Talker
该命令将返回已加载组件的唯一 ID 以及节点名称:
Loaded component 1 into '/ComponentManager' container node as '/talker'
现在第一个 shell 应该显示组件已加载的消息以及发布消息的重复消息。
在第二个 shell 中运行另一个命令来加载侦听器组件(参见 listener 源代码):
ros2 component load /ComponentManager composition composition::Listener
终端将返回:
Loaded component 2 into '/ComponentManager' container node as '/listener'
现在可以使用“ros2”命令行实用程序来检查容器的状态:
ros2 component list
您将看到以下结果:
/ComponentManager
1 /talker
2 /listener
现在,第一个 shell 应该显示每个收到的消息的重复输出。
使用 ROS 服务与服务器和客户端进行运行时组合
带有服务器和客户端的示例非常相似。
在第一个 shell 中:
ros2 run rclcpp_components component_container
在第二个 shell 中 (see server and client source code):
ros2 component load /ComponentManager composition composition::Server
ros2 component load /ComponentManager composition composition::Client
在这种情况下,客户端向服务器发送请求,服务器处理该请求并回复响应,客户端打印收到的响应。
使用硬编码节点进行编译时组合
此演示表明,可以重复使用相同的共享库来编译运行多个组件的单个可执行文件,而无需使用 ROS 接口。 可执行文件包含上述所有四个组件:talker 和 listener 以及服务器和客户端,它们在主函数中被硬编码。
在 shell 中调用 (see source code):
ros2 run composition manual_composition
这应该显示来自两对、讲话者和听众以及服务器和客户端的重复消息。
Note
手动组成的组件不会反映在“ros2 组件列表”命令行工具输出中。
使用 dlopen 进行运行时组合
此演示通过创建通用容器进程并明确传递要加载的库(不使用 ROS 接口)来介绍运行时组合的替代方案。 该进程将打开每个库并在库中为每个“rclcpp::Node”类创建一个实例(源代码)。
ros2 run composition dlopen_composition `ros2 pkg prefix composition`/lib/libtalker_component.so `ros2 pkg prefix composition`/lib/liblistener_component.so
ros2 run composition dlopen_composition `ros2 pkg prefix composition`/lib/libtalker_component.dylib `ros2 pkg prefix composition`/lib/liblistener_component.dylib
> ros2 pkg prefix composition
to get the path to where composition is installed. Then call
> ros2 run composition dlopen_composition <path_to_composition_install>\bin\talker_component.dll <path_to_composition_install>\bin\listener_component.dll
现在 shell 应该显示每个发送和接收消息的重复输出。
Note
dlopen-composed 组件不会反映在“ros2 组件列表”命令行工具输出中。
使用启动操作进行组合
虽然命令行工具对于调试和诊断组件配置很有用,但同时启动一组组件通常更方便。 为了自动执行此操作,我们可以使用`启动文件<https://github.com/ros2/demos/blob/rolling/composition/launch/composition_demo.launch.py>`__:
ros2 launch composition composition_demo.launch.py
高级主题
现在我们已经了解了组件的基本操作,我们可以讨论几个更高级的主题。
卸载组件
在第一个 shell 中,启动组件容器:
ros2 run rclcpp_components component_container
通过“ros2”命令行工具验证容器是否正在运行:
ros2 component list
您应该看到该组件的名称:
/ComponentManager
在第二个 shell 中,像之前一样加载说话者和监听者:
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
在第一个 shell 中,验证来自 talker 和 listener 的重复消息是否已停止。
重新映射容器名称和命名空间
可以通过标准命令行参数重新映射组件管理器名称和命名空间:
ros2 run rclcpp_components component_container --ros-args -r __node:=MyContainer -r __ns:=/ns
在第二个 shell 中,可以使用更新后的容器名称加载组件:
ros2 component load /ns/MyContainer composition composition::Listener
Note
容器的命名空间重新映射不会影响已加载的组件。
重新映射组件名称和命名空间
组件名称和命名空间可以通过 load 命令的参数进行调整。
在第一个 shell 中,启动组件容器:
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
您应该在控制台中看到相应的条目:
/ComponentManager
1 /talker2
2 /ns/talker
3 /ns2/talker3
Note
容器的命名空间重新映射不会影响已加载的组件。
将参数值传递给组件
“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/rclcpp/issues/2110#issuecomment-1454228192>`__。