ROS2承上启下【05】:在单个进程中布置多个节点
一、关于组件的背景知识
1.1 ROS 1 - 节点与 Nodelets
在 ROS 1 中,您可以将代码编写为 ROS 节点或 ROS nodelet。 ROS 1 节点被编译成可执行文件。另一方面,ROS 1 nodelet 被编译成一个共享库,然后在运行时由容器进程加载。
1.2 ROS 2 - 统一 API
在 ROS 2 中,推荐的编写代码的方式类似于 nodelet——我们称之为组件。这使得向现有代码添加通用概念变得容易,例如生命周期。 ROS 2 避免了不同 API 的最大缺点,因为两种方法在 ROS 2 中使用相同的 API。
仍然可以使用类似节点的样式“编写自己的 main”,但对于常见情况不建议这样做。
通过使流程布局成为部署时的决策,用户可以选择:
- 在单独的进程中运行多个节点,具有进程/故障隔离的好处以及更容易调试单个节点和
- 在单个进程中运行多个节点,具有更低的开销和更有效的通信(请参阅进程内通信)。
此外,ros2 启动可用于通过专门的启动操作自动执行这些操作。
1.3 编写组件
由于组件仅内置在共享库中,因此它没有 main 函数(参见 Talker 源代码)。组件通常是 rclcpp::Node 的子类。由于它不受线程控制,因此不应在其构造函数中执行任何长时间运行或阻塞的任务。相反,它可以使用计时器来获得定期通知。此外,它还可以创建发布者、订阅者、服务器和客户端。
使某个类成为组件的一个重要方面是该类使用包 rclcpp_components 中的宏注册自己(参见源代码中的最后一行)。这使得组件在其库被加载到正在运行的进程中时可被发现 - 它充当一种入口点。
此外,一旦创建了组件,就必须使用索引注册它才能被工具发现。
add_library(talker_component SHAREDsrc/talker_component.cpp)
rclcpp_components_register_nodes(talker_component "composition::Talker")
# To register multiple components in the same shared library, use multiple calls
# rclcpp_components_register_nodes(talker_component "composition::Talker2")
为了让 component_container 能够找到所需的组件,它必须从提供了相应工作空间的 shell 执行或启动。
1.4 使用组件
组合包包含一些关于如何使用组件的不同方法。最常见的三种是:
启动一个(通用容器进程)并调用容器提供的 ROS 服务 load_node。然后,ROS 服务将加载由传递的包名和库名指定的组件,并开始在正在运行的进程中执行它。除了以编程方式调用 ROS 服务,您还可以使用命令行工具通过传递的命令行参数调用 ROS 服务
创建包含多个在编译时已知的节点的自定义可执行文件。这种方法要求每个组件都有一个头文件(第一种情况并不严格需要)。
创建一个launch文件,使用ros2 launch创建一个加载了多个组件的容器进程。
二、在单个进程中组合多个节点
2.1 运行demo
官方资料有三个demo:
- rclcpp_components
- ros2component
- composition
可以下载后使用,并且可以使用以下命令运行。
2.2 如何发现可用组件
要查看工作区中已注册和可用的组件,请在 shell 中执行以下命令:
ros2 component types
终端将返回所有可用组件的列表:
(... components of other packages here)
composition
composition::Talker
composition::Listener
composition::NodeLikeListener
composition::Server
composition::Client
(... components of other packages here)
2.3 使用具有发布者和订阅者的 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 应该显示一个组件已加载的消息以及用于发布消息的重复消息。
在第二个 shell 中运行另一个命令以加载侦听器组件(请参阅侦听器源代码):
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
现在第一个 shell 应该显示每个收到的消息的重复输出。
2.4 使用带有服务器和客户端的 ROS 服务的运行时组合
具有服务器和客户端的示例非常相似。
在第一个shell中:
ros2 run rclcpp_components component_container
在第二个 shell 中(参见服务器和客户端源代码):
ros2 component load /ComponentManager composition composition::Server
ros2 component load /ComponentManager composition composition::Client
在这种情况下,客户端向服务器发送请求,服务器处理请求并回复响应,客户端打印收到的响应。
2.5 使用 ROS 服务的编译时组合
该演示表明,可以重用相同的共享库来编译运行多个组件的单个可执行文件。可执行文件包含上面的所有四个组件:talker 和 listener 以及 server 和 client。(参考代码如下)
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.#include <memory>#include "composition/client_component.hpp"
#include "composition/listener_component.hpp"
#include "composition/talker_component.hpp"
#include "composition/server_component.hpp"
#include "rclcpp/rclcpp.hpp"int main(int argc, char * argv[])
{// Force flush of the stdout buffer.setvbuf(stdout, NULL, _IONBF, BUFSIZ);// Initialize any global resources needed by the middleware and the client library.// This will also parse command line arguments one day (as of Beta 1 they are not used).// You must call this before using any other part of the ROS system.// This should be called once per process.rclcpp::init(argc, argv);// Create an executor that will be responsible for execution of callbacks for a set of nodes.// With this version, all callbacks will be called from within this thread (the main one).rclcpp::executors::SingleThreadedExecutor exec;rclcpp::NodeOptions options;// Add some nodes to the executor which provide work for the executor during its "spin" function.// An example of available work is executing a subscription callback, or a timer callback.auto talker = std::make_shared<composition::Talker>(options);exec.add_node(talker);auto listener = std::make_shared<composition::Listener>(options);exec.add_node(listener);auto server = std::make_shared<composition::Server>(options);exec.add_node(server);auto client = std::make_shared<composition::Client>(options);exec.add_node(client);// spin will block until work comes in, execute work as it becomes available, and keep blocking.// It will only be interrupted by Ctrl-C.exec.spin();rclcpp::shutdown();return 0;
}
ros2 run composition manual_composition
这应该显示来自publisher和acception以及server和client的重复消息。
注意:手动编写的组件不会反映在 ros2 组件列表命令行工具输出中。
2.6 使用 dlopen 的运行时组合
该演示通过创建通用容器进程并显式传递要加载的库而不使用 ROS 接口,提供了运行时组合的替代方案。该过程将打开每个库并在库源代码中为每个“rclcpp::Node”类创建一个实例)。
linux指令
ros2 run composition dlopen_composition `ros2 pkg prefix composition`/lib/libtalker_component.so `ros2 pkg prefix composition`/lib/liblistener_component.so
windows指令
> ros2 pkg prefix composition
获得路径后
> ros2 run composition dlopen_composition <path_to_composition_install>\bin\talker_component.dll <path_to_composition_install>\bin\listener_component.dll
现在,shell 应该为每个发送和接收的消息显示重复的输出。
注意:dlopen-composed 组件不会反映在 ros2 组件列表命令行工具输出中。
2.7 使用launch-action的组合
虽然命令行工具对于调试和诊断组件配置很有用,但同时启动一组组件通常更方便。要自动执行此操作,我们可以使用 ros2 启动中的功能。
ros2 launch composition composition_demo.launch.py
三、高级话题
现在我们已经了解了组件的基本操作,我们可以讨论一些更高级的主题。
3.1 如何卸载组件
在第一个 shell 中,启动组件容器:
ros2 run rclcpp_components component_container
通过 ros2 命令行工具验证容器是否正在运行:
ros2 component list
您应该会看到组件的名称:
/ComponentManager
在第二个 shell 中,像之前一样加载谈话者和听众:
ros2 component load /ComponentManager composition composition::Talker
ros2 component load /ComponentManager composition composition::Listener
使用唯一 ID 从组件容器中卸载节点。
ros2 component unload /ComponentManager 1 2
The terminal should return:
Unloaded component 1 from '/ComponentManager' container
Unloaded component 2 from '/ComponentManager' container
在第一个 shell 中,验证来自 talker 和 listener 的重复消息是否已停止。
3.2 重新映射容器名称和命名空间
组件管理器名称和命名空间可以通过标准命令行参数重新映射:
ros2 run rclcpp_components component_container --ros-args -r __node:=MyContainer -r __ns:=/ns
在第二个 shell 中,可以使用更新的容器名称加载组件:
ros2 component load /ns/MyContainer composition composition::Listener
注意:容器的命名空间重新映射不会影响加载的组件。
3.3 重新映射组件名称和命名空间
组件名称和命名空间可以通过加载命令的参数进行调整。 在第一个 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
在控制台中,您应该会看到相应的条目:
/ComponentManager1 /talker22 /ns/talker3 /ns2/talker3
注意:容器的命名空间重新映射不会影响加载的组件。
3.4 如何将参数值传递给组件ros2
组件加载命令行支持在构建节点时将任意参数传递给节点。此功能可按如下方式使用:
ros2 component load /ComponentManager image_tools image_tools::Cam2Image -p burger_mode:=true
3.5 将附加参数传递给组件
ros2 组件加载命令行支持将特定选项传递给组件管理器,以便在构建节点时使用。到目前为止,唯一受支持的命令行选项是使用进程内通信来实例化节点。此功能可按如下方式使用:
ros2 component load /ComponentManager composition composition::Talker -e use_intra_process_comms:=true
3.6 作为共享库的可组合节点
如果要将可组合节点作为共享库从包中导出并在另一个执行链接时组合的包中使用该节点,请将代码添加到 CMake 文件中,该文件会在下游包中导入实际目标。
然后安装生成的文件并导出生成的文件。
四、后记
ROS2比ROS1最大不同,在于ROS2用更高明、更专业的程序开发,必备的专业知识有:
- 面向对象
- C++
- 组件编程
- 动态库
- 泛型编程
- 多线程、进程
等诸多的近代程序开发技术,因此,对开发者的要求更高,面对更高的门槛,开发者需要沉下心思,平心静气、排除万难,才能修成正果。
Composing multiple nodes in a single process — ROS 2 Documentation: Foxy documentation
ROS2承上启下【05】:在单个进程中布置多个节点相关推荐
- python去重复元素_python 去除单个list中的重复元素
原博文 2020-02-23 08:31 − python中使用set 的方法,去除单个list中重复的元素并且保持元素的先后顺序不变. ``` list1 = ['a','a','b','c','d ...
- 32位应用程序单个进程最大占用内存是4GB
32位应用程序单个进程最大占用内存是4GB左右,这个问题是因为需要测试大数据里才能体现出来的问题,所以反反复复花了一周多的时间来搞定它,希望大家在对它有一个印象.64位的应用程序的时候就不会有这个限制 ...
- linux查看进程中的线程名,linux 怎么样查看一个进程的线程
一.linux系统支持的最大进程数 限制1:既然系统使用pid_t表示进程号,那么最大进程数不能超过pid_t类型的最大值吧 限制2:使用命令ulimit -u查看系统中限制的最大进程数,我的机器上是 ...
- 找出Java进程中大量消耗CPU
问题分析: 1,程序属于CPU密集型,和开发沟通过,排除此类情况. 2,程序代码有问题,出现死循环,可能性极大. Java程序很耗CPU是比较好分析的,有这么几步: 1.通过top命令(top之后再按 ...
- linux内核如何支持多核cpu,现在的多核CPU,Linux操作系统是否能够实现单个进程(多线程)的多核调度(跨CPU核心调度)?...
现在的多核CPU,Linux操作系统是否能够实现单个进程(多线程)的多核调度(跨CPU核心调度)? 关注:106 答案:2 mip版 解决时间 2021-02-02 01:11 提问者你說.你愛我 ...
- 【工具】linux中用top、ps命令查看进程中的线程
在Linux上显示某个进程的线程的几种方式. 方法一:PS 在ps命令中,"-T"选项可以开启线程查看.下面的命令列出了由进程号为<pid>的进程创建的所有线程. $ ...
- 查看基于Android 系统单个进程内存 CPU使用情况的几种方法
首先给大家分享一个巨牛巨牛的人工智能教程,是我无意中发现的.教程不仅零基础,通俗易懂,而且非常风趣幽默,还时不时有内涵段子,像看小说一样,哈哈-我正在学习中,觉得太牛了,所以分享给大家!点这里可以跳转 ...
- 单个进程监听多个端口及多个进程监听同一个端口
单个进程监听多个端口 单个进程创建多个 socket 绑定不同的端口,TCP, UDP 都行 多个进程监听同一个端口(multiple processes listen on same port) 方 ...
- 守护进程中创建的对象php,在PHP中生成守护进程(Daemon Process)
前两天看到一篇文章<如何使用PHP编写daemon process>,其中对核心代码却没有细说,我又查了一些资料,还看了一本<理解Unix进程>,才搞明白生成守护进程的时候发生 ...
最新文章
- 深度学习编译器综述The Deep Learning Compiler
- 分享13个帮助你简化开发的jQuery插件
- adprw指令通讯案例_S7-1200与S7-300傻瓜式通讯
- linux dup用法,Unix_Linux
- 教你以 4G 的速度克隆 Github 项目!
- lisp文字上标源码_创建文本/标注样式源码 - AutoLISP/Visual LISP 编程技术 - CAD论坛 - 明经CAD社区 - Powered by Discuz!...
- linux input输入子系统分析《三》:S3C2440的触摸屏驱动实例
- Ubuntu16.04下实时监控CPU/GPU内存的使用情况
- 北京可以备案什么域名
- Eclipse-maven项目不将resources下的文件打到classpath下
- 伺服驱动器cn1引脚定义_伺服驱动器CN1引脚定义,和面板操作设置,跪求高手指点。...
- java中级程序员面试题_51CTO下载-JAVA中级程序员面试题
- 集成智能小车(二...2)整体设计之谋
- LINUX下 ssdp 实现
- Java从零开始实现导出excel(一)
- 文献阅读——How to give an Academic Talk
- pandas学习笔记—agg()函数详解
- 机器学习笔记 - 什么是条件随机场?
- windows用alist部署webdav服务挂载阿里云盘,再用rclone将阿里云盘挂载到电脑本地
- 资深互联网运营经理分析微信公众号如何通过内容运营来打动用户