【ROS2】中级-在单个进程中组合多个节点

 目录

  •  背景

  •  先决条件

  •  运行演示

    • 发现可用组件

    • 运行时使用 ROS 服务的发布者和订阅者组合

    • 运行时使用 ROS 服务的服务器和客户端组合

    • 编译时与硬编码节点组合

    • 运行时使用 dlopen 进行组合

    • 使用启动动作的组合

  •  高级主题

    •  卸载组件

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

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

    • 传递参数值到组件中

    • 向组件传递额外的参数

  • 可组合节点作为共享库

  • 组成非节点派生组件

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

教程级别:中级

 时间:20 分钟

 背景

查看概念性文章 https://docs.ros.org/en/jazzy/Concepts/Intermediate/About-Composition.html 。

有关如何编写可组合节点的信息,请查看本教程 https://docs.ros.org/en/jazzy/Tutorials/Intermediate/Writing-a-Composable-Node.html 。

 先决条件

本教程使用来自 rclcpp_components、ros2component、composition 和 image_tools 包的可执行文件。如果您已经按照平台的安装说明进行了操作,这些应该已经安装好了。

运行演示

发现可用组件 

要查看工作区中注册和可用的组件,请在 shell 中执行以下操作:

ros2 component types

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

cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 component types
rosbag2_transport
  rosbag2_transport::Player
  rosbag2_transport::Recorder
robot_state_publisher
  robot_state_publisher::RobotStatePublisher
tf2_ros
  tf2_ros::StaticTransformBroadcasterNode
quality_of_service_demo_cpp
  quality_of_service_demo::MessageLostListener
  quality_of_service_demo::MessageLostTalker
  quality_of_service_demo::QosOverridesListener
  quality_of_service_demo::QosOverridesTalker
point_cloud_transport
  point_cloud_transport::Republisher
  point_cloud_transport::Republisher
image_transport
  image_transport::Republisher
image_tools
  image_tools::Cam2Image
  image_tools::ShowImage
logging_demo
  logging_demo::LoggerConfig
  logging_demo::LoggerUsage
examples_rclcpp_wait_set
  Talker
  Listener
examples_rclcpp_minimal_subscriber
  WaitSetSubscriber
  StaticWaitSetSubscriber
  TimeTriggeredWaitSetSubscriber
demo_nodes_cpp_native
  demo_nodes_cpp_native::Talker
composition
  composition::Talker
  composition::Listener
  composition::NodeLikeListener
  composition::Server
  composition::Client
action_tutorials_cpp
  action_tutorials_cpp::FibonacciActionClient
  action_tutorials_cpp::FibonacciActionServer

使用 ROS 服务进行运行时组合,包括一个发布者和一个订阅者

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

ros2 run rclcpp_components component_container

打开第二个 shell 并通过 ros2 命令行工具验证容器是否在运行:

ros2 component list

您应该看到组件的名称:

/ComponentManager

在第二个 shell 中加载 talker 组件(参见 talker 源代码 https://github.com/ros2/demos/blob/jazzy/composition/src/talker_component.cpp ):

ros2 component load /ComponentManager composition composition::Talker

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

cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 component load /ComponentManager composition composition::Talker
Loaded component 1 into '/ComponentManager' container node as '/talker'

现在第一个 shell 应该显示组件已加载的消息,以及发布消息的重复消息。

67ec9a68bbd84d187827ea08240d4f73.png

在第二个 shell 中运行另一个命令以加载监听器组件(参见监听器 https://github.com/ros2/demos/blob/jazzy/composition/src/listener_component.cpp 源代码):

 终端将返回:

cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 component load /ComponentManager composition composition::Listener
Loaded component 2 into '/ComponentManager' container node as '/listener'

ros2 ` 命令行工具现在可以用来检查容器的状态:

cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 component list
/ComponentManager
  1  /talker
  2  /listener

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

8c9a8aa42a7c36c1e3fe7e9a0626bcad.png

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

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

在第一个shell中:

ros2 run rclcpp_components component_container

在第二个壳中(见服务器https://github.com/ros2/demos/blob/jazzy/composition/src/server_component.cpp 和客户端https://github.com/ros2/demos/blob/jazzy/composition/src/client_component.cpp 源代码):

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

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

编译时与硬编码节点的组合

这个演示表明,相同的共享库可以重复使用,以编译一个单一的可执行文件,该文件运行多个组件,而不使用 ROS 接口。该可执行文件包含上述所有四个组件:讲述者和监听者以及服务器和客户端,这些都在主函数中硬编码。

在 shell 调用中(见源代码https://github.com/ros2/demos/blob/jazzy/composition/src/manual_composition.cpp ):

ros2 run composition manual_composition

这应该显示两对之间的重复消息,即说话者和听者以及服务器和客户端。

 便条

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

运行时使用 dlopen 进行组合

这个演示提供了一个运行时组合的替代方案,通过创建一个通用容器进程并显式传递要加载的库而不使用 ROS 接口。该过程将打开每个库并在库中创建一个“rclcpp::Node”类的实例(源代码 https://github.com/ros2/demos/blob/jazzy/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 应该会显示每个发送和接收消息的重复输出。

 便条

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

使用启动动作的组合

命令行工具对于调试和诊断组件配置很有用,但通常同时启动一组组件更为方便。为了自动化这个操作,我们可以使用一个启动文件https://github.com/ros2/demos/blob/jazzy/composition/launch/composition_demo_launch.py :

ros2 launch composition composition_demo_launch.py
# 版权归2019年开源机器人基金会所有。
#
# 根据Apache License 2.0("许可")授权;
# 除非遵守许可,否则您不得使用此文件。
# 您可以在以下位置获得许可的副本:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# 除非适用法律要求或书面同意,否则根据许可分发的软件
# 将按“原样”基础提供,不附带任何形式的保证或条件。
# 请参阅许可证以了解管理权限和限制的规定。


"""在组件容器中启动一个talker和一个listener。"""


import launch  # 导入launch模块,用于启动和管理ROS节点。
from launch_ros.actions import ComposableNodeContainer  # 从launch_ros.actions导入ComposableNodeContainer类。
from launch_ros.descriptions import ComposableNode  # 从launch_ros.descriptions导入ComposableNode类。




def generate_launch_description():  # 定义一个函数,用于生成启动描述。
    """生成具有多个组件的启动描述。"""
    container = ComposableNodeContainer(  # 创建一个ComposableNodeContainer实例。
            name='my_container',  # 容器的名称。
            namespace='',  # 容器的命名空间。
            package='rclcpp_components',  # 容器所属的包。
            executable='component_container',  # 容器的可执行文件。
            composable_node_descriptions=[  # 容器中包含的组件节点描述列表。
                ComposableNode(  # 创建一个ComposableNode实例,代表一个talker组件。
                    package='composition',  # 组件所属的包。
                    plugin='composition::Talker',  # 组件的插件名称。
                    name='talker'),  # 组件的名称。
                ComposableNode(  # 创建另一个ComposableNode实例,代表一个listener组件。
                    package='composition',  # 组件所属的包。
                    plugin='composition::Listener',  # 组件的插件名称。
                    name='listener')  # 组件的名称。
            ],
            output='screen',  # 输出到屏幕。
    )


    return launch.LaunchDescription([container])  # 返回一个LaunchDescription实例,包含上面定义的容器。

 高级主题 

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

 卸载组件

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

ros2 run rclcpp_components component_container

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

ros2 component list

您应该看到组件的名称:

/ComponentManager

在第二个 shell 中,像以前一样加载 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

在第一个 shell 中,验证 talker 和 listener 的重复消息是否已经停止。

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

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

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

6a1fe14a440ad2cf4b87a5c1abc02a59.png

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

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

d664a55696d54a60ef2f600b8e69c840.png

 便条

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

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

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

在第一个 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 /ComponentManager image_tools image_tools::Cam2Image -p burger_mode:=true

508439e08f41df1912fb985c0a7b05f7.png

将额外的参数传递到组件中

命令行支持向组件管理器传递特定选项,用于构建节点。目前,唯一支持的命令行选项是使用进程内通信来实例化一个节点。此功能可按以下方式使用:

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

59631d0d361456baad579e0c6fa6382e.png

可组合节点作为共享库

如果您想要从一个包中导出一个可组合节点作为共享库,并在另一个执行链接时组合的包中使用该节点,请添加代码到 CMake 文件中,以导入下游包中的实际目标。

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

实际的例子可以在这里看到:ROS 论坛 - Ament 共享库的最佳实践https://discourse.ros.org/t/ament-best-practice-for-sharing-libraries/3602

组成非节点派生组件

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

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

要创建一个不是从节点派生的组件,请遵循以下指南:

  1. 实现一个构造函数,它将 const rclcpp::NodeOptions& 作为其参数。

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

这是一个不是从节点派生的组件的例子,它监听一个 ROS 主题:node_like_listener_component。https://github.com/ros2/demos/blob/jazzy/composition/src/node_like_listener_component.cpp

有关此主题的更多信息,您可以参考这次讨论。https://github.com/ros2/rclcpp/issues/2110#issuecomment-1454228192

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值