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

news2024/11/23 13:44:15

前言     

        在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_set
  Talker
  Listener
examples_rclcpp_minimal_subscriber
  WaitSetSubscriber
  StaticWaitSetSubscriber
  TimeTriggeredWaitSetSubscriber
custom_action_cpp
  custom_action_cpp::FibonacciActionServer
  custom_action_cpp::FibonacciActionClient
logging_demo
  logging_demo::LoggerConfig
  logging_demo::LoggerUsage
composition
  composition::Talker
  composition::Listener
  composition::NodeLikeListener
  composition::Server
  composition::Client
demo_nodes_cpp_native
  demo_nodes_cpp_native::Talker
robot_state_publisher
  robot_state_publisher::RobotStatePublisher
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
teleop_twist_joy
  teleop_twist_joy::TeleopTwistJoy
demo_nodes_cpp
  demo_nodes_cpp::OneOffTimerNode
  demo_nodes_cpp::ReuseTimerNode
  demo_nodes_cpp::ServerNode
  demo_nodes_cpp::ClientNode
  demo_nodes_cpp::IntrospectionServiceNode
  demo_nodes_cpp::IntrospectionClientNode
  demo_nodes_cpp::ListParameters
  demo_nodes_cpp::ParameterBlackboard
  demo_nodes_cpp::SetAndGetParameters
  demo_nodes_cpp::ParameterEventsAsyncNode
  demo_nodes_cpp::EvenParameterNode
  demo_nodes_cpp::SetParametersCallback
  demo_nodes_cpp::ContentFilteringPublisher
  demo_nodes_cpp::ContentFilteringSubscriber
  demo_nodes_cpp::Talker
  demo_nodes_cpp::LoanedMessageTalker
  demo_nodes_cpp::SerializedMessageTalker
  demo_nodes_cpp::Listener
  demo_nodes_cpp::SerializedMessageListener
  demo_nodes_cpp::ListenerBestEffort
tf2_ros
  tf2_ros::StaticTransformBroadcasterNode
action_tutorials_cpp
  action_tutorials_cpp::FibonacciActionClient
  action_tutorials_cpp::FibonacciActionServer
image_tools
  image_tools::Cam2Image
  image_tools::ShowImage
joy
  joy::Joy
  joy::GameController
depthimage_to_laserscan
  depthimage_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.coloradmin.cn/o/1597137.html

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

相关文章

如何在MobaXterm上使用rz命令

1、首先输入命令和想下载的文件&#xff0c;如下图&#xff1a; 2、按住ctrl鼠标右键&#xff0c;选择如下选项&#xff1a; 上传命令是rz&#xff0c;选择Receive...... 下载命令是sz&#xff0c;选择Send...... 3、我这里是要把Linux上的文件下载到我的本地window磁盘&…

Django之rest_framework(三)

一、GenericAPIView的使用 rest_framework.generics.GenericAPIView 继承自APIVIew,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个Mixin扩展类 1.1、属性 serializer_class 指明视图使用的序列化器…

记录一下买了腾讯云服务器后如何第一次连MobaXterm

首先是你要用SwitchHost把hosts的映射地址改成你新买的服务器的&#xff08;如果你没这个软件&#xff0c;可以直接在etc/hosts里改 &#xff09; 再连MobaXterm 然后&#xff0c;关键的来了 成功&#xff01;

2024/4/15 网络编程day3

一、TCP机械臂测试 通过w(红色臂角度增大)s&#xff08;红色臂角度减小&#xff09;d&#xff08;蓝色臂角度增大&#xff09;a&#xff08;蓝色臂角度减小&#xff09;按键控制机械臂 注意&#xff1a;关闭计算机的杀毒软件&#xff0c;电脑管家&#xff0c;防火墙 1&#…

openGauss学习笔记-261 openGauss性能调优-使用Plan Hint进行调优-将部分Error降级为Warning的Hint

文章目录 openGauss学习笔记-261 openGauss性能调优-使用Plan Hint进行调优-将部分Error降级为Warning的Hint261.1 功能描述261.2 语法格式261.3 示例261.3.1 忽略非空约束261.3.2 忽略唯一约束261.3.3 忽略分区表无法匹配到合法分区261.3.4 更新/插入值向目标列类型转换失败 o…

3.MMD快捷键操作及人物绑定配饰

快捷键 1. 模型界面切换 按一下TAB键&#xff0c;就从人物模型切换到照明模型 再按一下TAB键&#xff0c;就能从照明模型切换回人物模型 2. 选中全部模型 当模型界面是人物模型时 而且电脑输入法时英文时 按一下A键&#xff0c;可以把人物骨骼全部选中&#xff0c;方便旋转…

互联网轻量级框架整合之MyBatis配置详解

MyBatis核心配置文件mybatis-config.xml里有诸多配置项&#xff0c;但常用的就无非就如下这么多 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTDConfig3.0//EN" "https://mybati…

【爬虫开发】爬虫从0到1全知识md笔记第5篇:Selenium课程概要,selenium的其它使用方法【附代码文档】

爬虫开发从0到1全知识教程完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;爬虫课程概要&#xff0c;爬虫基础爬虫概述,,http协议复习。requests模块&#xff0c;requests模块1. requests模块介绍,2. response响应对象,3. requests模块发送请求,4. request…

“成像光谱遥感技术中的AI革命:ChatGPT在遥感领域中的应用“

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境&#xff0c;是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型&#xff0c;在理解和生成人类语言方面表现出了非凡的能力。本文重点介绍ChatGPT在遥感中的应用&#xff0c;人工智能…

[lesson31]完善的复数类

完善的复数类 完善的复数类 复数类应该具有的操作 运算&#xff1a;&#xff0c;-&#xff0c;*&#xff0c;/比较&#xff1a;&#xff0c;!赋值&#xff1a;求模&#xff1a;modulus 利用操作符重载 统一复数与实数的运算方式统一复数与实数的比较方式 注意事项 C规定赋…

CLI举例:上行连接路由器(业务引流),下行连接交换机(VRRP引流)

CLI举例&#xff1a;上行连接路由器&#xff08;业务引流&#xff09;&#xff0c;下行连接交换机&#xff08;VRRP引流&#xff09; 介绍了设备上行连接路由器&#xff0c;下行连接交换机的集群配置举例。 组网需求 如图1所示&#xff0c;FW与路由器之间运行OSPF协议。 希望…

21、矩阵-搜索二维矩阵

思路&#xff1a; 这道题很有意思 从左到有升序&#xff0c;从上到下升序&#xff0c;斜边从左上到右下也是升序&#xff0c;从右上到做下降序。 如果是从左往右依次遍历&#xff0c;就会面临一个问题向右还是向下&#xff0c;因为都是大于当前值&#xff0c;不好决断&#x…

C#值类型和引用类型、赋值、区别、相同点

C#值类型和引用类型 **前言&#xff1a;**在C#中变量分为以下几种类型&#xff1a;值类型&#xff08;Value Types&#xff09;,引用类型&#xff08;Reference Types&#xff09;,指针类型&#xff08;Pointer Types&#xff09;;指针类型&#xff08;变量存储另一种类型变量…

【canvas】canvas基础使用(七):绘制图像

简言 学习canvas如何绘制图片或视频。 绘制图像 给定一个图像&#xff0c;一般使用drawImage()方法绘制。 drawImage 绘制图像 Canvas 2D API 中的 CanvasRenderingContext2D.drawImage() 方法提供了多种在画布&#xff08;Canvas&#xff09;上绘制图像的方式。 语法&…

租用境外服务器,越南服务器的优势有哪些

自从中国加入世界贸易组织之后&#xff0c;国内经济增加速度非常快&#xff0c;同时越来越多的人选择去东南亚国家发展&#xff0c;因为当地的中国人很多&#xff0c;所以中国企业在当地面临着更小的文化差异。东南亚地区也是最新的经济体&#xff0c;互联网正处于蓬勃发展的阶…

docker-compose部署RabbitMQ(一步到位)

docker-compose如下 version: 3.1 services:rabbitmq:restart: alwaysimage: rabbitmq:managementcontainer_name: rabbitmqhostname: rabbitports:- 5672:5672- 15672:15672environment:TZ: Asia/ShanghaiRABBITMQ_DEFAULT_USER: rabbitRABBITMQ_DEFAULT_PASS: 123456volumes…

(弟弟14)递归•按顺序打印一个整数的每一位

这里是目录哦 题目代码运行截图递归思路递归停止条件如何实现“按顺序”悟了✨加油&#x1f389; 题目 按顺序打印一个整数的每一位。 代码 #include<stdio.h> void Print(int n) {if (n > 9)//递归停止条件{Print(n / 10);//不断趋近递归停止条件}printf("%d…

如何在Linux通过docker搭建Plik文件系统并实现无公网IP管理内网文件

文章目录 1. Docker部署Plik2. 本地访问Plik3. Linux安装Cpolar4. 配置Plik公网地址5. 远程访问Plik6. 固定Plik公网地址7. 固定地址访问Plik 本文介绍如何使用Linux docker方式快速安装Plik并且结合Cpolar内网穿透工具实现远程访问&#xff0c;实现随时随地在任意设备上传或者…

宝塔面板部署腾讯云的域名

一、腾讯云&#xff0c;搜索我的证书&#xff0c;点击打开如图所示&#xff0c;点击下砸 二、点击宝塔的证书&#xff0c;然后下载到桌面 三、解压 四、打开宝塔&#xff0c;网站》自己的项目列表中要绑定的ssl 五、对应的文件内容复制进去&#xff0c;保存并启用证书 六、有了…