ROS2 Jazzy:在单个进程中组合多个节点

184 阅读7分钟

背景信息

了解《关于组合的概念》,并了解如何编写可组合节点的信息《编写可组合节点》。

先决条件

本教程使用rclcpp_componentsros2componentcompositionimage_tools包中的可执行文件。如果你按照对应平台的安装说明进行操作,这些应该已经安装好了。

运行Demo

发现可用组件

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

ros2 component types

终端将返回所有可用组件的列表:

(... 其他包的组件列表)
composition
  composition::Talker
  composition::Listener
  composition::NodeLikeListener
  composition::Server
  composition::Client
(... 其他包的组件列表)

使用 ROS 服务进行运行时组合(发布者和订阅者)

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

ros2 run rclcpp_components component_container

打开第二个终端,使用ros2命令行工具验证容器是否正在运行:

ros2 component list

你应该会看到组件的名称:

/ComponentManager

在第二个终端中加载talker组件(查看talker 源代码 github.com/ros2/demos/…

ros2 component load /ComponentManager composition composition::Talker

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

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

此时,第一个终端应该会显示组件已加载的消息,以及重复的消息发布信息。 在第二个终端中运行另一个命令,加载listener组件(查看 listener 源代码 github.com/ros2/demos/…

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 服务进行运行时组合(服务端和客户端)

服务端和客户端的示例与上述示例非常相似。

服务端源代码: github.com/ros2/demos/…

客户端源代码: github.com/ros2/demos/…)

在第一个终端中输入:

ros2 run rclcpp_components component_container

然后在第二个终端中输入:

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

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

编译时组合(硬编码节点)

这个Demo展示了共享库可以在不使用 ROS 接口的情况下,被重新用于编译一个运行了多个组件的单个可执行文件。该可执行文件包含上述的所有四个组件:talkerlistenerserverclient,这些组件在主函数中进行了硬编码。具体可查看源代码 github.com/ros2/demos/…

在终端中调用:

ros2 run composition manual_composition

这应该会显示talkerlistener以及serverclient这两对节点的不断重复的消息。

注意事项

手动组合的组件不会在ros2 component list命令行工具的输出中显示。

使用 dlopen 进行运行时组合

这里展示了一种运行时组合的替代方法,即创建一个通用的容器进程,并在不使用 ROS 接口的情况下显式地传递要加载的库。该进程将打开每个库,并在库中创建每个rclcpp::Node类的一个实例(源代码 github.com/ros2/demos/…

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

此时,终端应该会显示每次发送和接收消息的不断重复的消息。

注意事项

使用dlopen组合的组件同样不会在ros2 component list命令行工具的输出中显示。

使用启动动作进行组合

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

ros2 launch composition composition_demo_launch.py

组合进阶

现在我们已经了解了组件的基本操作,接下来可以讨论一些更高级的主题。

组件容器类型

有几种不同选项的组件容器类型。你可以根据自己的需求选择最合适的组件容器类型。

  • component_container(无可用选项/参数)
ros2 run rclcpp_components component_container
  • component_container_mt,使用由 4 个线程组成的MultiThreadedExecutor。可以使用thread_num参数选项指定MultiThreadedExecutor中的线程数。
ros2 run rclcpp_components component_container_mt --ros-args -p thread_num:=4
  • component_container_isolated,为每个组件使用MultiThreadedExecutor--use_multi_threaded_executor参数指定为每个组件使用的执行器类型为MultiThreadedExecutor
ros2 run rclcpp_components component_container_isolated --use_multi_threaded_executor

卸载组件

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

ros2 run rclcpp_components component_container

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

ros2 component list

你应该会看到组件的名称:

/ComponentManager

在第二个终端中,像之前一样加载talkerlistener组件:

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

在第一个终端中,验证talkerlistener的重复消息是否已经停止。

重映射容器名称和命名空间

可以通过标准命令行参数重映射组件管理器的名称和命名空间:

ros2 run rclcpp_components component_container --ros-args -r __node:=MyContainer -r __ns:=/ns

在第二个终端中,可以使用更新后的容器名称加载组件:

ros2 component load /ns/MyContainer composition composition::Listener
注意事项

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

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

可以通过load命令的参数调整组件的名称和命名空间。 在第一个终端中,启动组件容器:

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命令行支持向组件管理器传递特定选项,以便在构造节点时使用。 以下示例展示了如何使用额外参数use_intra_process_commsforward_global_arguments

ros2 component load /ComponentManager composition composition::Talker -e use_intra_process_comms:=true -e forward_global_arguments:=false

支持以下额外参数:

参数类型默认值描述
forward_global_arguments布尔值true加载组件节点时应用全局参数。
use_intra_process_comms布尔值false启用组件节点中的进程内通信。

可组合节点作为共享库

如果要将可组合节点从软件包导出为共享库,并在链接时组合的软件包中使用该节点,请将代码添加到 CMake 文件中,该文件将导入下游软件包中的实际目标

然后安装生成的文件并导出该文件。

可以在此处查看一个实际示例: ROS Discourse - Ament 共享库的最佳实践 (answers.ros.org/question/30…)

组合非节点派生组件

在 ROS2 中,组件允许更有效地使用系统资源,并提供了一个强大的功能,使你能够创建不依赖于特定节点的可重用功能。

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

要创建一个非节点派生的组件,请遵循以下准则:

  • 实现一个接受const rclcpp::NodeOptions&作为参数的构造函数。
  • 实现get_node_base_interface()方法,该方法应返回一个NodeBaseInterface::SharedPtr。你可以用构造函数创建的节点的get_node_base_interface()方法来提供此接口。

以下是一个非节点派生的组件示例,它监听一个 ROS topic: node_like_listener_component (github.com/ros2/demos/…

有关这个主题的更多信息,你可以参考这个讨论: github.com/ros2/rclcpp…


关注【智践行】,发送 【机器人】 获得机器人经典学习资料