前言

阅读本章之前,确保已经了解ROS2中构件(Component)的概念,如果不了解,欢迎移步ROS2 Composition

一个小问题自测:构件与节点之间的区别?在容器进程中运行的单元是构件还是节点?

执行demo

本文通过执行几个demo,来直观展示composition的常见使用方式。

run-time 动态加载

foxy源码demos中的composition已经实现了一些构件,不妨确认一下:

$ ros2 component type
compositioncomposition::Talkercomposition::Listenercomposition::Servercomposition::Client

我们将多个节点加载到单个进程的步骤是:首先创建一个容器进程,然后通过ros2 的api向这个容器进程中动态加载。

创建容器进程:ros2 run rclcpp_components component_container

确认容器进程正在运行:ros2 component list

在另一个终端中,输入命令加载构件:ros2 component load /ComponentManager composition composition::<component_name>

我们此时可以看到容器进程终端有相应的内容输出。

编译期加载

同样的shared lib也可以在代码中在编译期加载,可以看一下manual_composition.cpp的源码:

// 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

运行时从静态库加载

大致的思路是通过静态库新建一个node实例,从而达到加载的目的。代码如下:

// 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 <string>
#include <vector>#include "class_loader/class_loader.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_components/node_factory.hpp"#define DLOPEN_COMPOSITION_LOGGER_NAME "dlopen_composition"int main(int argc, char * argv[])
{// Force flush of the stdout buffer.setvbuf(stdout, NULL, _IONBF, BUFSIZ);if (argc < 2) {fprintf(stderr, "Requires at least one argument to be passed with the library to load\n");return 1;}rclcpp::init(argc, argv);rclcpp::Logger logger = rclcpp::get_logger(DLOPEN_COMPOSITION_LOGGER_NAME);rclcpp::executors::SingleThreadedExecutor exec;rclcpp::NodeOptions options;std::vector<class_loader::ClassLoader *> loaders;std::vector<rclcpp_components::NodeInstanceWrapper> node_wrappers;std::vector<std::string> libraries;for (int i = 1; i < argc; ++i) {libraries.push_back(argv[i]);}for (auto library : libraries) {RCLCPP_INFO(logger, "Load library %s", library.c_str());auto loader = new class_loader::ClassLoader(library);auto classes = loader->getAvailableClasses<rclcpp_components::NodeFactory>();for (auto clazz : classes) {RCLCPP_INFO(logger, "Instantiate class %s", clazz.c_str());auto node_factory = loader->createInstance<rclcpp_components::NodeFactory>(clazz);auto wrapper = node_factory->create_node_instance(options);auto node = wrapper.get_node_base_interface();node_wrappers.push_back(wrapper);exec.add_node(node);}loaders.push_back(loader);}exec.spin();for (auto wrapper : node_wrappers) {exec.remove_node(wrapper.get_node_base_interface());}node_wrappers.clear();rclcpp::shutdown();return 0;
}

需要注意的是我们需要在命令行中显式地指出使用的静态库:

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

这样就把talker和listener组件加载到了容器进程中。

我们看到从代码中加载的构件也是需要新起一个线程作为容器进程。

使用launch实现Composition

可以通过ros2 launch指令批量加载构件:ros2 launch composition composition_demo.launch.py

Advanced Topic

上面介绍的是一些基础用法,现在来看一些进阶的做法。

Unloading Component

假设我们在容器中加载了listener和talker,我们可以通过构件的id来卸载他们:

$ ros2 component unload /ComponentManager 1 2
Unloaded component 1 from '/ComponentManager' container
Unloaded component 2 from '/ComponentManager' container

Remapping container name and namespace

我们可以通过命令行参数重命名容器进程:

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

那么在加载构件的终端中,我们就可以使用:

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

Remapping component name and namespace

类似地,我们可以remapping构件的名字和命名空间:

$ 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
/ComponentManager1  /talker22  /ns/talker3  /ns2/talker3

ROS2--在单进程中加入多个节点相关推荐

  1. ROS2承上启下【05】:在单个进程中布置多个节点

    一.关于组件的背景知识 1.1 ROS 1 - 节点与 Nodelets 在 ROS 1 中,您可以将代码编写为 ROS 节点或 ROS nodelet. ROS 1 节点被编译成可执行文件.另一方面 ...

  2. asp.net中web.config配置节点大全详解

    web.config 文件查找规则:        (1)如果在当前页面所在目录下存在web.config文件,查看是否存在所要查找的结点名称,如果存在返回结果并停止查找.        (2)如果当 ...

  3. 【 C 】在单链表中插入一个新节点的尝试(二)

    在上篇博文中:[ C ]在单链表中插入一个新节点的尝试(一),我们最后提到了如果向单链表的开头(起始位置)插入一个节点,上篇博文中给出的程序显然完成不了这任务. 这篇博文中,我们将解决这个问题,给出一 ...

  4. 【 C 】在单链表中插入一个新节点的尝试(一)

    根据<C和指针>中讲解链表的知识,记录最终写一个在单链表中插入一个新节点的函数的过程,这个分析过程十分的有趣,准备了两篇博文,用于记录这个过程. 链表是以结构体和指针为基础的,所以结构体和 ...

  5. [转]NS2仿真过程中解决动画仿真节点未定义问题

    原文地址:http://blog.myspace.cn/e/400266384.htm 其实,这个问题已经出现很长时间了,但是直到昨天问题才得到解决. 问题描述 用NS2运行无线仿真,然后运行动画程序 ...

  6. 二叉树中如何求根节点到任意节点的路径?

    二叉树中如何求任一节点的路径呢? 思路 使用先序遍历,处理的时候让节点入栈,并且加上标志位即可. 使用另外的result保存最终的路径. 函数 void pre_order(TreeNode * no ...

  7. 【深度学习】网络中隐含层神经元节点的个数(需要学习的特征数目)

    http://blog.csdn.net/zouxy09/article/details/9983399 1.网络中隐含层神经元节点的个数(需要学习的特征数目),采集的密度(也就是convolutio ...

  8. Elasticsearch 集群中增加专用master节点

    Elasticsearch 集群中增加专用master节点 文章目录 Elasticsearch 集群中增加专用master节点 1.增加master节点 2.排除原来的节点的选举权 3.data节点 ...

  9. 数据结构与算法--两个链表中第一个公共节点

    链表中第一个公共节点 公节点定义:同一个节点在两个链表中,并不是节点值相同 题目:输入两个节点,找出他们的第一个公共节点,节点定义如需 /*** 链表元素节点** @author liaojiamin ...

最新文章

  1. 【微信小程序企业级开发教程】微信小程序目录结构
  2. python turtle画彩虹-Python turtle 绘制彩色螺旋线
  3. 在C语言中以编程的方式获取函数名
  4. sql语句多个表补齐四位_SQL学习笔记 - CTE通用表表达式和WITH用法
  5. 【REST SOAP】REST和SOAP Web Service的区别比较
  6. ASP.NET CORE 项目实战 ---图形验证码的实现
  7. 如何才能做出一个顶级的客户端
  8. 整个计算机系统结构图,计算机系统结构复习(16页)-原创力文档
  9. libcmtd.lib(wincrt0.obj) : error LNK2019: 无法解析的外部符号 WinMain,该符号在函数 __tmainCRTStartup 中被引用
  10. yuv420转yuv422(yuyv)
  11. css判断手机端还是pc端,JavaScript判断设备是手机端还是PC端,并加载不同的css/js文件...
  12. 24点计算器问题[C++实现]
  13. php微信公众号模板消息主动推送
  14. vue 菜单路由重复点击报错
  15. 职业“小三劝退师”,真的能拯救你的婚姻吗?
  16. DB2 SQL错误查询 LOAD时报的日志特别好用
  17. 成为PHP大牛的绝招 —— 君子生非异也,善假于物也
  18. 个人读书思维导图笔记之mysql-innodb之操作函数相关
  19. 有限元(FEM)基本知识速阅
  20. 全球大健康领袖集聚上海浦东,上海燕博会又要有大动作?

热门文章

  1. 为什么要实现Serializable
  2. 服务sql server(MSSQLSERVER)意外停止。这发生了2次。
  3. Xcode Command Line Tools命令
  4. 鸿蒙手机壁纸有条纹,百变图标(鸿蒙)
  5. 上海PHICOMM斐讯短短五年时间市场为何能扩大成这样?
  6. 【第80篇】Lion:优化算法的符号发现
  7. linux:作业控制 jobs命令 kill命令 bg和fg命令
  8. Leetcode-搜索-130.被围绕的区域(中等)
  9. 汉子编码比字母编码长_编码比您想象的更具创意
  10. python 类中的 __getitem__方法