在单个进程中组合多个节点

目标: 将多个节点组合成单个进程。

教程级别: 中级

**时间:**20分钟

背景

请查看 概念文章

运行演示

演示使用`rclcpp_components <https://github.com/ros2/rclcpp/tree/humble/rclcpp_components>`__、`ros2component <https://github.com/ros2/ros2cli/tree/humble/ros2component>`__和`composition <https://github.com/ros2/demos/tree/humble/composition>`__包中的可执行文件,并可以使用以下命令运行。

发现可用组件

要查看在工作空间中注册和可用的组件,执行以下命令行命令:

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 组件(请参阅 talker 源代码):

ros2 component load /ComponentManager composition composition::Talker

该命令将返回加载组件的唯一 ID 和节点名称:

Loaded component 1 into '/ComponentManager' container node as '/talker'

现在第一个 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

现在,第一个终端应该显示每个接收到的消息的重复输出。

使用ROS服务进行运行时组合,包括服务器和客户端

带有服务器和客户端的示例非常相似。

在第一个shell中:

ros2 run rclcpp_components component_container

在第二个shell中(参见 serverclient 的源代码):

ros2 component load /ComponentManager composition composition::Server
ros2 component load /ComponentManager composition composition::Client

在这种情况下,客户端向服务器发送请求,服务器处理请求并回复响应,客户端打印接收到的响应。

使用ROS服务进行编译时组合

该演示表明相同的共享库可以被重复使用以编译一个包含多个组件的单个可执行文件。该可执行文件包含上述的所有四个组件:talker和listener以及server和client。

在shell调用中(参见 源代码):

ros2 run composition manual_composition

这应该显示来自两个对的重复消息,talker和listener以及server和client。

注解

手动组合的组件不会在``ros2 component list``命令行工具的输出中反映出来。

使用dlopen进行运行时组合

此演示通过创建一个通用容器进程并显式传递要加载的库来呈现一种运行时组合的替代方法,而不使用ROS接口。该进程将打开每个库并在库中创建每个"rclcpp::Node"类的一个实例(源代码 <https://github.com/ros2/demos/blob/humble/composition/src/dlopen_composition.cpp>`__)。

ros2 run composition dlopen_composition `ros2 pkg prefix composition`/lib/libtalker_component.so `ros2 pkg prefix composition`/lib/liblistener_component.so

现在,shell应该显示每个发送和接收消息的重复输出。

注解

不会在``ros2 component list``命令行工具输出中反映dlopen组合的组件。

使用启动动作进行组合

虽然命令行工具对于调试和诊断组件配置非常有用,但通常更方便的是同时启动一组组件。为了自动化此操作,我们可以使用一个 启动文件

ros2 launch composition composition_demo.launch.py

高级主题

既然我们已经看到了组件的基本操作,我们可以讨论一些更高级的主题。

卸载组件

在第一个 shell 中,启动组件容器:

ros2 run rclcpp_components component_container

通过 ros2 命令行工具验证容器是否正在运行:

ros2 component list

您应该看到组件的名称:

/ComponentManager

在第二个终端中加载之前的 talker 和 listener:

ros2 component load /ComponentManager composition composition::Talker
ros2 component load /ComponentManager composition composition::Listener

使用唯一标识符从组件容器卸载节点。

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

在第二个 shell 中,可以通过更新后的容器名称来加载组件:

ros2 component load /ns/MyContainer composition composition::Listener

注解

容器的命名空间重新映射不会影响已加载的组件。

重新映射组件名称和命名空间

可以通过加载命令的参数来调整组件的名称和命名空间。

在第一个 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

注解

容器的命名空间重新映射不会影响已加载的组件。

将参数值传递给组件

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讨论区 - Ament共享库的最佳实践

组合非节点派生组件

在ROS 2中,组件允许更高效地使用系统资源,并提供了一个强大的功能,使您能够创建可重用的功能,而这些功能不会与特定节点绑定。

使用组件的一个优点是,它们允许您创建非节点派生功能,作为独立的可执行文件或共享库,可以根据需要加载到ROS系统中。

要创建一个不是从节点派生的组件,请按照以下准则进行操作:

  1. 实现一个以 const rclcpp::NodeOptions& 作为参数的构造函数。

  2. 实现 get_node_base_interface() 方法,该方法应返回 NodeBaseInterface::SharedPtr。您可以在构造函数中创建一个节点并使用其 get_node_base_interface() 方法来提供此接口。

下面是一个不是从节点派生而来的组件的示例,它监听一个ROS话题:node_like_listener_component

有关此主题的更多信息,您可以参考这个 讨论