ROS 2边学边练(25)-- 将多个节点组合到一个进程

前言     

        在ROS 2中,将多个节点(Nodes)组合到一个单独的进程(Process)中通常指的是使用“Composable Nodes”的特性。这个特性允许你定义可复用的组件(Components),然后将这些组件加载到一个或多个ROS 2节点中,而这些节点可以运行在同一个进程中。

这样做有几个好处:

  1. 资源优化:通过将多个节点组合到一个进程中,可以减少进程间通信的开销,并更有效地利用系统资源。

  2. 简化部署:对于需要多个节点协同工作的应用程序,将它们组合到一个进程中可以简化部署和配置。

  3. 代码重用:组件化的设计使得代码重用变得更加容易,因为你可以在不同的节点或应用程序中重用相同的组件。

在ROS 2中实现这一功能,通常涉及以下步骤:

  1. 定义组件:使用ROS 2的组件API定义可复用的组件。每个组件都封装了特定的功能,并且可以在运行时加载到节点中。

  2. 创建节点:创建一个或多个ROS 2节点,这些节点将作为组件的容器。这些节点可以配置为接受组件的加载。

  3. 加载组件:使用ROS 2的组件加载器(Component Loader)将组件加载到节点中。这通常涉及指定组件的类型和参数,并告诉ROS 2在何处找到这些组件的实现。

  4. 运行节点:启动包含组件的节点,这些节点现在将在一个共同的进程中运行,并共享相同的资源。

        需要注意的是,尽管将多个节点组合到一个进程中可以提高效率和简化部署,但这也可能引入额外的复杂性。例如,需要确保进程内的所有节点都能够正确地处理并发操作和资源共享。因此,在决定是否将节点组合到一个进程时,应该仔细考虑应用程序的具体需求和约束。

        简而言之,ROS 2中的“Composable Nodes”特性提供了一种灵活的方式来构建和管理ROS 2应用程序,通过组件化和进程内组合的方式,可以实现更高效、可重用和可配置的机器人和自动化系统。

动动手

运行例子

        这些例子使用了rclcpp_components、ros2component和composition包中的可执行文件,并且可以使用以下命令运行。

发现可用的组件

        想要查看当前工作空间中已注册且能用的组件列表,我们可以利用下面的命令进行查看:

$ros2 component types

返回符合条件的组件列表:

mike@mike-virtual-machine:~/Desktop/ros2_ws$ ros2 component types
examples_rclcpp_wait_setTalkerListener
examples_rclcpp_minimal_subscriberWaitSetSubscriberStaticWaitSetSubscriberTimeTriggeredWaitSetSubscriber
custom_action_cppcustom_action_cpp::FibonacciActionServercustom_action_cpp::FibonacciActionClient
logging_demologging_demo::LoggerConfiglogging_demo::LoggerUsage
compositioncomposition::Talkercomposition::Listenercomposition::NodeLikeListenercomposition::Servercomposition::Client
demo_nodes_cpp_nativedemo_nodes_cpp_native::Talker
robot_state_publisherrobot_state_publisher::RobotStatePublisher
quality_of_service_demo_cppquality_of_service_demo::MessageLostListenerquality_of_service_demo::MessageLostTalkerquality_of_service_demo::QosOverridesListenerquality_of_service_demo::QosOverridesTalker
teleop_twist_joyteleop_twist_joy::TeleopTwistJoy
demo_nodes_cppdemo_nodes_cpp::OneOffTimerNodedemo_nodes_cpp::ReuseTimerNodedemo_nodes_cpp::ServerNodedemo_nodes_cpp::ClientNodedemo_nodes_cpp::IntrospectionServiceNodedemo_nodes_cpp::IntrospectionClientNodedemo_nodes_cpp::ListParametersdemo_nodes_cpp::ParameterBlackboarddemo_nodes_cpp::SetAndGetParametersdemo_nodes_cpp::ParameterEventsAsyncNodedemo_nodes_cpp::EvenParameterNodedemo_nodes_cpp::SetParametersCallbackdemo_nodes_cpp::ContentFilteringPublisherdemo_nodes_cpp::ContentFilteringSubscriberdemo_nodes_cpp::Talkerdemo_nodes_cpp::LoanedMessageTalkerdemo_nodes_cpp::SerializedMessageTalkerdemo_nodes_cpp::Listenerdemo_nodes_cpp::SerializedMessageListenerdemo_nodes_cpp::ListenerBestEffort
tf2_rostf2_ros::StaticTransformBroadcasterNode
action_tutorials_cppaction_tutorials_cpp::FibonacciActionClientaction_tutorials_cpp::FibonacciActionServer
image_toolsimage_tools::Cam2Imageimage_tools::ShowImage
joyjoy::Joyjoy::GameController
depthimage_to_laserscandepthimage_to_laserscan::DepthImageToLaserScanROS
运行时组合示例(ROS的发布者/订阅者)

        应用程序运行时动态加载/卸载组件,灵活性好。

        打开第一个终端,启动组件容器:

$ros2 run rclcpp_components component_container

        打开第二个终端,检查确认组件容器是否正常运行中:

$ros2 component list

如果返回了/ComponentManager,则说明OK。再在此终端中加载talker组件(源码):

$ros2 component load /ComponentManager composition composition::Talker

返回的结果中包含了该组件的名称以及其对应的ID号:

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

 

现在我们再切换到第一个终端窗口(加载组件容器),看看里面有些什么内容出来:

可以看到,talker组件加载成功并且在不断发布消息。

        我们再在第二个终端加载listener组件(源码),命令类似于talker,如下:

$ros2 component load /ComponentManager composition composition::Listener

也返回了类似的内容,组件名称listener以及ID号码:

Loaded component 2 into '/ComponentManager' container node as '/listener'

 

我们再检查一下当前的组件列表:

$ros2 component list

运行时组合示例(ROS的服务端和客户端)

        同上面的发布者/订阅者例子,先开启一个终端加载组件容器:

$ros2 run rclcpp_components component_container

在第二个终端依次加载服务端组件(源码)和客户端组件(源码):

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

我们看看第一个终端会有什么内容:

编译时组合示例(硬编码)

        编译ROS 2应用程序时,通过硬编码的方式将节点组合在一起,编译时组合通常意味着在源代码中直接定义和实例化节点对象,并将它们组合成一个或多个进程。这种方式的优点包括简单性和确定性——编译后的应用程序结构是固定的,没有运行时的不确定性。然而,它也降低了灵活性,因为一旦应用程序被编译,节点的组合方式就不能轻易改变。

        这个示例显示,可以复用相同的共享库来编译运行多个组件的单个可执行文件,而无需使用ROS接口。可执行文件包含上面的所有四个组件:发送器和侦听器以及服务器和客户端,它们在主函数中进行了硬编码。

        我们可以通过下面的命令调用:

$ros2 run composition manual_composition

我们可以看看终端返回的内容:

编译组合的方式的组件是没法通过ros2 component list命令查看的。

运行时组合示例(使用dlopen)

        在运行时组合示例中,当我们运行组件容器后并启动服务端/客户端时,可以看到在容器的终端输出中有打印出加载对应的so库并且找到对应的类最终实例化。

        这个利用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

  

可以与上面的截图比较一下,是不是差不多。

dlopen-composed组件通过ros2 component list命令也是发现不了的。

launch方式启动组合

        上面的例子都是逐个命令运行组合,可以比较方便地对过程进行调试、诊断,但是我们也可以一条命令启动多个组件(源码):

$ros2 launch composition composition_demo_launch.py
mike@mike-virtual-machine:~/Desktop/ros2_ws$ ros2 launch composition composition_demo_launch.py
[INFO] [launch]: All log files can be found below /home/mike/.ros/log/2024-04-15-21-18-04-617692-mike-virtual-machine-30402
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [component_container-1]: process started with pid [30417]
[component_container-1] [INFO] [1713187085.127445213] [my_container]: Load Library: /opt/ros/iron/lib/libtalker_component.so
[component_container-1] [INFO] [1713187085.128648991] [my_container]: Found class: rclcpp_components::NodeFactoryTemplate<composition::Talker>
[component_container-1] [INFO] [1713187085.128759649] [my_container]: Instantiate class: rclcpp_components::NodeFactoryTemplate<composition::Talker>
[INFO] [launch_ros.actions.load_composable_nodes]: Loaded node '/talker' in container '/my_container'
[component_container-1] [INFO] [1713187085.143832047] [my_container]: Load Library: /opt/ros/iron/lib/liblistener_component.so
[component_container-1] [INFO] [1713187085.144882187] [my_container]: Found class: rclcpp_components::NodeFactoryTemplate<composition::Listener>
[component_container-1] [INFO] [1713187085.144981253] [my_container]: Instantiate class: rclcpp_components::NodeFactoryTemplate<composition::Listener>
[INFO] [launch_ros.actions.load_composable_nodes]: Loaded node '/listener' in container '/my_container'
[component_container-1] [INFO] [1713187086.141280013] [talker]: Publishing: 'Hello World: 1'
[component_container-1] [INFO] [1713187086.141662411] [listener]: I heard: [Hello World: 1]
[component_container-1] [INFO] [1713187087.141013738] [talker]: Publishing: 'Hello World: 2'
[component_container-1] [INFO] [1713187087.141367812] [listener]: I heard: [Hello World: 2]
[component_container-1] [INFO] [1713187088.141100795] [talker]: Publishing: 'Hello World: 3'
[component_container-1] [INFO] [1713187088.141427367] [listener]: I heard: [Hello World: 3]
[component_container-1] [INFO] [1713187089.141335387] [talker]: Publishing: 'Hello World: 4'
[component_container-1] [INFO] [1713187089.141511508] [listener]: I heard: [Hello World: 4]

更多用法

        上面都是关于组合的一些基础用法,下面我们来show几个高级用法。

卸载组件

        再来一遍上面的组合示例,方便用来演示卸载的步骤。

        1.启动组件容器

$ros2 run rclcpp_components component_container

        2.检查确认容器是否正常运行

$ros2 component list

        3.启动talker和listener

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

 可以看到每个组件成功加载后都会有一个对应的ID,上面1对应Listener,2对应Talker。

        4.卸载组件

$ros2 component unload /ComponentManager 1 2

 

根据对应的ID来卸载组件,干净明了。

重映射容器及命名空间名字

        我们可以通过下面的命令对组件容器和命名空间的名字进行重映射:

$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 component list

将参数值传递到组件

        ros2组件加载命令行支持在构建节点时将任意参数传递给节点。此功能可按如下方式使用:

$ros2 component load /ComponentManager image_tools image_tools::Cam2Image -p burger_mode:=true
将附加参数传递到组件

        ros2组件加载命令行支持将特定选项传递给组件管理器,以便在构建节点时使用。到目前为止,唯一支持的命令行选项是使用进程内通信实例化节点。此功能可按如下方式使用:

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

作为共享库的组合节点

        如果要从包中将可组合节点导出为共享库,并在另一个进行链接时组合的包中使用该节点,请将代码添加到CMake文件中,该文件将导入下游包中的实际目标。然后安装生成的文件并导出生成的文件。一个参考例子:ROS Discourse - Ament best practice for sharing libraries

合成非节点衍生组件

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

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

  1. 要创建不是从节点派生的组件,我们要遵循以下准则:
  2. 构造函数带const rclcpp::NodeOptions&参数;
  3. 实现get_node_base_interface()方法,该方法应返回NodeBaseInterface::SharedPtr。我们可以使用在构造函数中创建的节点的get_node_base_interface()方法来提供此接口。

        可以参考这个不是从节点派生的组件示例,该组件侦听ROS主题:node_like_listener_component。

        内容有点多,慢慢消化。

本篇完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/818400.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WPF-基础及进阶扩展合集(持续更新)

目录 一、基础 1、GridSplitter分割线 2、x:static访问资源文件 3、wpf触发器 4、添加xaml资源文件 5、Convert转换器 6、多路绑定与多路转换器 二、进阶扩展 1、HierarchicalDataTemplate 2、XmlDataProvider从外部文件获取源 3、TextBox在CellTemplate中的焦点问题…

系统边界图

系统边界图的定义&#xff1a; 系统边界图是系统工程和软件工程中的一种图形化工具&#xff0c;用于描述系统与外部世界之间的交互和界限。它展示了系统的组成部分以及这些组件如何与外部实体进行通信和交互。系统边界图通常包括系统内部的各个组件、外部实体以及它们之间的通信…

考研数学究竟有多难?基础差该如何复习?

考研数学的难度是相对的&#xff0c;它取决于考生的数学基础、备考时间、复习效率和解题技巧等多个因素。从历年的考试情况来看&#xff0c;考研数学确实具有一定的挑战性&#xff0c;主要体现在以下几个方面。 首先是知识覆盖面广&#xff0c;考研数学涵盖了高等数学、线性代…

集群伸缩简介

4.2.1.小结 Redis如何判断某个key应该在哪个实例&#xff1f; 将16384个插槽分配到不同的实例根据key的有效部分计算哈希值&#xff0c;对16384取余余数作为插槽&#xff0c;寻找插槽所在实例即可 如何将同一类数据固定的保存在同一个Redis实例&#xff1f; 这一类数据使用…

1042: 中缀表达式转换为后缀表达式

解法&#xff1a;直接给算法 创建一个栈和一个空的后缀表达式字符串。 遍历中缀表达式中的每个字符。 如果当前字符是操作数&#xff0c;直接将其添加到后缀表达式字符串中。 如果当前字符是操作符&#xff0c;需要将其与栈顶的操作符进行比较&#xff1a; 如果栈为空&#…

Mac下载的软件显示文件已损坏,如何解决文件已损坏问题

当在Mac上下载的软件显示文件已损坏时&#xff0c;这可能是因为多种原因导致的&#xff0c;包括网络问题、下载中断、软件未完整下载、文件传输错误等。解决这个问题需要采取一些步骤来排除可能的原因&#xff0c;并尝试修复文件。下面将详细介绍一些常见的解决方法&#xff1a…

Qt-绘制多边形、椭圆、多条直线

1、说明 所有的绘图操作是在绘图事件中进行。mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWi…

【详解算法流程+程序】DBSCAN基于密度的聚类算法+源码-用K-means和DBSCAN算法对银行数据进行聚类并完成用户画像数据分析课设源码资料包

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一个比较有代表性的基于密度的聚类算法。 与划分和层次聚类方法不同&#xff0c;它将簇定义为密度相连的点的最大集合&#xff0c;能够把具有足够高密度的区域划分为簇&#xff0c; 并可在噪声的空间数据…

MES管理系统中生产物料管理的设计

在数字化工厂建设的浪潮中&#xff0c;MES管理系统作为执行层的核心管理系统&#xff0c;其重要性日益凸显。特别是在生产物料管理方面&#xff0c;MES管理系统不仅承担物料计划指令的接收&#xff0c;还负责物料派工及使用反馈的数据收集&#xff0c;其业务流程的设计对数字化…

树莓派点亮双色LED

双色LED灯准确来说叫双基色LED灯,是指模块只能显示2种颜色,一般是红色和绿色,可以有三种状态 :灭,颜色1亮,颜色2亮,根据颜色组合的不同,分为红蓝双色,黄蓝双色,红绿双色等等。 接线:将引脚S(绿色)和中间引脚(红色)连接到Raspberry Pi的GPIO接口上,对Raspberry…

文献速递:深度学习胰腺癌诊断--胰腺肿瘤的全端到端深度学习诊断

Title 题目 Fully end-to-end deep-learning-based diagnosis of pancreatic tumors 胰腺肿瘤的全端到端深度学习诊断 01 文献速递介绍 胰腺癌是最常见的肿瘤之一&#xff0c;预后不良且通常是致命的。没有肿瘤的患者只需要进一步观察&#xff0c;而胰腺肿瘤的诊断需要紧…

【数据结构与算法】递推

来源&#xff1a;《信息学奥赛一本通》 所谓递推&#xff0c;是指从已知的初始条件出发&#xff0c;依据某种递推关系&#xff0c;逐次推出所要求的各中间结果及最后结果。其中初始条件或是问题本身已经给定&#xff0c;或是通过对问题的分析与化简后确定。 从已知条件出发逐…

浏览器工作原理与实践--跨站脚本攻击(XSS):为什么Cookie中有HttpOnly属性

通过上篇文章的介绍&#xff0c;我们知道了同源策略可以隔离各个站点之间的DOM交互、页面数据和网络通信&#xff0c;虽然严格的同源策略会带来更多的安全&#xff0c;但是也束缚了Web。这就需要在安全和自由之间找到一个平衡点&#xff0c;所以我们默认页面中可以引用任意第三…

web前端框架设计第四课-条件判断与列表渲染

web前端框架设计第四课-条件判断与列表渲染 一.预习笔记 1.条件判断 1-1&#xff1a;v-if指令&#xff1a;根据表达式的值来判断是否输出DOM元素 1-2&#xff1a;template中使用v-if 1-3&#xff1a;v-else 1-4&#xff1a;v-else-if 1-5&#xff1a;v-show&#xff08;不支…

【快捷部署】017_MongoDB(6.0.14)

&#x1f4e3;【快捷部署系列】017期信息 编号选型版本操作系统部署形式部署模式复检时间017MongoDB6.0.14Ubuntu 20.04apt单机2024-04-11 一、快捷部署 #!/bin/bash ################################################################################# # 作者&#xff1a;…

SQL注入sqli_labs靶场第十七题

B站教学视频很详细 【sql注入之sqli-labs系列教程(less11-17)】sqli-labs_33_less17_哔哩哔哩_bilibili 我将SQL语句在页面中显示&#xff0c;以便更深入学习。 1.寻找注入点 修改密码的一个页面。 输入正确的账号密码&#xff0c;可以看到&#xff0c;账号为admin&#xf…

PHP Storm 2024.1使用

本文讲的是phpstorm 2024.1最新版本激活使用教程&#xff0c;本教程适用于windows操作系统。 1.先去idea官网下载phpstorm包&#xff0c;我这里以2023.2最新版本为例 官网地址&#xff1a;https://www.jetbrains.com/zh-cn/phpstorm/ 2.下载下来后安装&#xff0c;点下一步 …

Qt5 编译oracle数据库驱动

库文件 1、Qt源码目录&#xff1a;D:\Qt5\5.15.2\Src\qtbase\src\plugins\sqldrivers\oci 2、oracle客户端SDK: https://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html 下载各版本中的如下压缩包&#xff0c;一定要版本相同的 将两个压缩包…

性能升级,INDEMIND机器人AI Kit助力产业再蜕变

随着机器人进入到越来越多的生产生活场景中&#xff0c;作业任务和环境变得更加复杂&#xff0c;机器人需要更精准、更稳定、更智能、更灵敏的自主导航能力。 自主导航技术作为机器人技术的核心&#xff0c;虽然经过了多年发展&#xff0c;取得了长足进步&#xff0c;但在实践…

Python的国际化和本地化【第162篇—国际化和本地化】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 随着全球化的发展&#xff0c;多语言支持在软件开发中变得越来越重要。Python作为一种流行的…