ROS2高效学习第十章 -- ros2 高级组件之 component 合并进程启动 其一

ros2 高级组件之 component 合并进程启动

  • 1 前言和资料
  • 2 正文
    • 2.1 component 引入
    • 2.2 component_demo
  • 3 总结

1 前言和资料

第十章我们将学习 ros2 的多个高级组件,包括 component,复杂 launch 文件,TF2,gazebo 和 rviz。本文我们学习 ros2 的 component 机制,其中会用到 ros2 intra-process 通信方式。
本文参考资料如下:
(1)ROS2高效学习第一章 – ros2整体介绍及DDS引入
(2)ROS2高效学习第七章 – ros2 plugins编程
(3)ROS高效进阶第四章 – 机器视觉处理之ros集成zbar实现二维码检测 第2.3节
(4)About-Composition
(5)Writing-a-Composable-Node
(6)Intra-Process-Communication
(7)humble-composition 样例

2 正文

2.1 component 引入

(1)两种节点启动方式:在前面几章的样例中,每个节点都被单独编译成一个可执行文件,启动后会是一个独立的进程,这些节点的 main 函数也都很类似(如下),本人称之为可执行程序启动方式

int main(int argc, char * argv[]) {rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<Subscriber>());rclcpp::shutdown();return 0;
}

由于 main 函数具有高度雷同性,很自然的就可以想到:用一个公共的启动程序,加载各个节点的动态库来启动,这种方式本人称之为动态库启动方式。ROS2高效学习第七章 – ros2 plugins编程 验证了这种思路的可行性。
通常情况下,小规模的程序,样例程序,使用可执行程序启动方式;大型的商业项目,比如智驾,都使用动态库启动方式。而动态库启动方式又通常支持两种启动模式,一是每个节点单独一个进程启动,方便调试,一般开发阶段使用;二是多个节点合并进程启动,可以大大降低负载,一般发版阶段使用。
(2)ros1 nodelet VS ros2 component:ros2 component 是 ros1 nodelet 的替代品,两者都是动态库启动方式,都支持每个节点单独一个进程启动和多个节点合并进程启动两种模式。两者的不同之处在于,ros1 nodelet 在合并节点模式时,默认通过指针实现消息传递,节约通信开销;ros2 component 在合并节点模式时,默认仍通过 DDS 中间件通信,只有在配置节点使用 intra-process 通信时,才通过指针实现消息传递。
关于 ros1 nodelet 参考 ROS高效进阶第四章 – 机器视觉处理之ros集成zbar实现二维码检测 第2.3节。
关于 DDS 和 intra-process 参考 ROS2高效学习第一章 – ros2整体介绍及DDS引入 ;

2.2 component_demo

(1)创建 component_demo 以及相关文件

cd ~/colcon_ws/src
ros2 pkg create --build-type ament_cmake --license Apache-2.0 component_demo --dependencies rclcpp rclcpp_components std_msgs
cd component_demo
mkdir launch
touch include/component_demo/pub_component.hpp 
touch include/component_demo/sub_component.hpp 
touch include/component_demo/visibility_control.h
touch launch/merge_node_launch.py
touch launch/separate_node_launch.py
touch src/pub_component.cpp src/sub_component.cpp

(2)编写 pub_component.hpp

#ifndef COMPONENT_DEMO__PUB_COMPONENT_HPP_
#define COMPONENT_DEMO__PUB_COMPONENT_HPP_// 必须配置可见头文件,主要是为了兼容 lunix 和 windows
#include "component_demo/visibility_control.h"
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
namespace component_demo
{
class PubComponent : public rclcpp::Node 
{
public:// 来自可见头文件COMPONENT_DEMO_PUBLIC// explicit 是为了防止实例化PubComponent时的隐式转换explicit PubComponent(const rclcpp::NodeOptions & options);protected:void on_timer();private:size_t count_;std::string msg_inner_;rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_msg_;rclcpp::TimerBase::SharedPtr timer_;
};
} // namespace component_demo#endif // COMPONENT_DEMO__PUB_COMPONENT_HPP_

(3)编写 sub_component.hpp

#ifndef COMPONENT_DEMO__SUB_COMPONENT_HPP_
#define COMPONENT_DEMO__SUB_COMPONENT_HPP_#include "component_demo/visibility_control.h"
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"namespace component_demo
{
// 注释请查看 pub_component.hpp
class SubComponent : public rclcpp::Node
{
public:COMPONENT_DEMO_PUBLICexplicit SubComponent(const rclcpp::NodeOptions & options);
private:rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_msg_;
};} // namespace component_demo#endif // COMPONENT_DEMO__SUB_COMPONENT_HPP_

(4)编写 visibility_control.h:让程序兼容 windows 和 linux,直接套模板改一下就行

#ifndef COMPONENT_DEMO__VISIBILITY_CONTROL_H_
#define COMPONENT_DEMO__VISIBILITY_CONTROL_H_#ifdef __cplusplus
extern "C"
{
#endif// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
//     https://gcc.gnu.org/wiki/Visibility#if defined _WIN32 || defined __CYGWIN__#ifdef __GNUC__#define COMPONENT_DEMO_EXPORT __attribute__ ((dllexport))#define COMPONENT_DEMO_IMPORT __attribute__ ((dllimport))#else#define COMPONENT_DEMO_EXPORT __declspec(dllexport)#define COMPONENT_DEMO_IMPORT __declspec(dllimport)#endif#ifdef COMPONENT_DEMO_BUILDING_DLL#define COMPONENT_DEMO_PUBLIC COMPONENT_DEMO_EXPORT#else#define COMPONENT_DEMO_PUBLIC COMPONENT_DEMO_IMPORT#endif#define COMPONENT_DEMO_PUBLIC_TYPE COMPONENT_DEMO_PUBLIC#define COMPONENT_DEMO_LOCAL
#else#define COMPONENT_DEMO_EXPORT __attribute__ ((visibility("default")))#define COMPONENT_DEMO_IMPORT#if __GNUC__ >= 4#define COMPONENT_DEMO_PUBLIC __attribute__ ((visibility("default")))#define COMPONENT_DEMO_LOCAL  __attribute__ ((visibility("hidden")))#else#define COMPONENT_DEMO_PUBLIC#define COMPONENT_DEMO_LOCAL#endif#define COMPONENT_DEMO_PUBLIC_TYPE
#endif#ifdef __cplusplus
}
#endif
#endif  // COMPONENT_DEMO__VISIBILITY_CONTROL_H_

(5)编写 pub_component.cpp

#include <chrono>
#include <iostream>
#include <memory>
#include <utility>#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include "component_demo/pub_component.hpp"#include "rclcpp_components/register_node_macro.hpp"
RCLCPP_COMPONENTS_REGISTER_NODE(component_demo::PubComponent)namespace component_demo
{PubComponent::PubComponent(const rclcpp::NodeOptions & options): Node("pub_component", options), count_(0) {msg_inner_ = "从前有位程序员,名叫杰克。杰克在一家知名科技公司工作,以出神入化的编程技巧而闻名。""可即便是杰克,也有他的死穴—他没有咖啡就无法编码。""某天早上,杰克来到了办公室,兴冲冲地准备开始新的一天。""他按下了咖啡机的开关,却发现这台终年不离不弃的老伙计竟然罢工了!""杰克惊慌失措,因为他知道没有咖啡,他的大脑就像没有安装操作系统的电脑一样毫无用处。""需要紧急解决方案!于是他做了什么每个程序员在遇到问题时都会做的事——Google搜索“没有咖啡如何生存”。""成百上千的建议涌现在屏幕上,但都不对味。就在此时,他注意到了一篇奇怪的文章,标题是:“用代码唤醒你的大脑”。""这篇文章提到了一种传说中的编程技术,可以让程序员通过编写代码来生成身体所需的能量。""虽然看起来很荒谬,但杰克决定尝试一下。""他迅速打开了他的编程环境,并开始编写一些非常复杂的函数。""随着每一个键击,他能感觉到一股虚拟的“能量”在体内流淌。""一小时后,杰克完成了他的作品,一个模拟咖啡因分子结构的程序。""他按下运行键,闭上眼睛,希望奇迹发生。""几秒钟后,他睁开眼睛,感觉... 完全一样。""显然,代码并不能真正取代咖啡。""杰克失望极了,但他还是决定去办公楼下的咖啡店买一杯咖啡来解决问题。。。。";pub_msg_ = create_publisher<std_msgs::msg::String>("hello_msg", 10);// 帧率设置为 200 hz,较高的帧率,可以比较明显的看出合并进程与分离进程负载的变化timer_ = create_wall_timer(std::chrono::milliseconds(5), std::bind(&PubComponent::on_timer, this));
}void PubComponent::on_timer() {// 如果想使用 ros2 的 intra-process 通信,最好使用 unique_ptr,然后发送时使用std::move()转移所有权// 这种方式可以明确告诉 ros2 , 这个消息你拥有唯一的所有权,可以使用 intra-process 直接传递指针进行通信auto msg = std::make_unique<std_msgs::msg::String>();msg->data = msg_inner_ + std::to_string(++count_);pub_msg_->publish(std::move(msg));
}
} // namespace component_demo

(6)编写 sub_component.cpp

#include <iostream>
#include <memory>#include "rclcpp/rclcpp.hpp"
#include "component_demo/sub_component.hpp"#include "rclcpp_components/register_node_macro.hpp"
RCLCPP_COMPONENTS_REGISTER_NODE(component_demo::SubComponent)namespace component_demo
{
SubComponent::SubComponent(const rclcpp::NodeOptions & options): Node("sub_component", options) {auto msg_callback = [this](std_msgs::msg::String::ConstSharedPtr msg) {RCLCPP_INFO(this->get_logger(), "I heard: [%s]", msg->data.c_str());};sub_msg_ = create_subscription<std_msgs::msg::String>("hello_msg", 10, msg_callback);
}};  // namespace component_demo

(7)编写 separate_node_launch.py

import launch
from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNodedef generate_launch_description():# 两个组件放在两个 container 里,也就是分别在两个进程,这种状况比较容易排查问题,当然负载也要高一些# 通常情况下,开发阶段使用这种方式,生产环境使用合并进程的方式pub_container = ComposableNodeContainer(name='pub_container',namespace='',package='rclcpp_components',# component_container_mtexecutable='component_container',composable_node_descriptions=[ComposableNode(package='component_demo',plugin='component_demo::PubComponent',name='pub_component')],output='screen',)sub_container = ComposableNodeContainer(name='sub_container',namespace='',package='rclcpp_components',executable='component_container',composable_node_descriptions=[ComposableNode(package='component_demo',plugin='component_demo::SubComponent',name='sub_component')],output='screen',)return launch.LaunchDescription([pub_container, sub_container])

(8)编写 merge_node_launch.py

import launch
from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNodedef generate_launch_description():# 两个组件放在一个 container 里,也就是共享一个进程,可以降低负载# 通常情况下,开发阶段使用分开进程的方式,生产环境使用这种方式container = ComposableNodeContainer(name='my_container',namespace='',package='rclcpp_components',# component_container 是单线程容器,容器内所有组件共享一个线程,没有并发问题,但是效率低# component_container_mt 是多线程容器,容器内每个组件都有自己的线程,可以并发处理,效率高,但需要考虑并发风险executable='component_container',composable_node_descriptions=[ComposableNode(package='component_demo',plugin='component_demo::PubComponent',name='pub_component',# 尽管多个组件合并在同一个进程,ros2 也是默认走 DDS 中间件通信# 下面的参数,可以设置组件之间通过 intra-process 通信# 理论上,intra-process 直接传递指针,效率更高# 但是本样例太小了,实际测试无法看出使用 intra-process 的优势extra_arguments=[{'use_intra_process_comms': True}]),ComposableNode(package='component_demo',plugin='component_demo::SubComponent',name='sub_component',# 接收端也要配置 intra-processextra_arguments=[{'use_intra_process_comms': True}])],output='screen',)return launch.LaunchDescription([container])

(9)编译并运行

cd ~/colcon_ws/src
colcon build --packages-select component_demo
source install/local_setup.bash
# 两个节点独立进程启动
ros2 launch component_demo separate_node_launch.py
# 两个节点合并进程启动
ros2 launch component_demo merge_node_launch.py

在这里插入图片描述
两个节点独立进程启动负载截图:
在这里插入图片描述
两个节点合并进程启动负载截图:整体几乎少了一半负载
在这里插入图片描述

3 总结

本文代码托管在本人的github上:component_demo

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

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

相关文章

鱼眼相机的测距流程及误差分析[像素坐标系到空间一点以及测距和误差分析]

由于最近在整理单目测距的内容&#xff0c;顺手也总结下鱼眼相机的测距流程和误差分析&#xff0c;如果有错误&#xff0c;还请不吝赐教。 参考链接: 鱼眼镜头的成像原理到畸变矫正&#xff08;完整版&#xff09; 相机模型总结&#xff08;针孔、鱼眼、全景&#xff09; 三维…

Linux安装redis(基于CentOS系统,Ubuntu也可参考)

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; 一、下载并解压Redis 1、执行下面的命令下载redis&#xff1a;wget https://download.redis.io/releases/redis-6.2.6.tar.gz 2、解压redis&#xff1a;tar xzf redis-6.2.6.tar.gz 3、移动redis目录&a…

深入了解高压电阻器的世界,探索其操作、类型和在各种高压应用中的关键作用

高压电阻器是高压条件下的专用元件&#xff0c;对于管理电压和散热至关重要 它们的工作原理是欧姆定律 类型包括线绕电阻、碳复合电阻、金属氧化物膜电阻、厚膜电阻和薄膜电阻这些电阻器在电力系统、医疗设备、汽车电子和电信设备中是必不可少的。 额定电压从600V到48KV 80p…

农村分散式生活污水分质处理及循环利用技术指南

标准已完成意见征集&#xff1a; 本文件给出了农村分散式生活污水分质处理及循环利用的总则、污水收集、污水分质处理、资源化利用、利用模式、运维管理等的指导。 本文件适用于农村分散式生活污水分质处理及循环利用的设施新建、扩建和改建工程的设计、施工与运维。 注:本文件…

Apache HBase(二)

目录 一、Apache HBase 1、HBase Shell操作 1.1、DDL创建修改表格 1、创建命名空间和表格 2、查看表格 3、修改表 4、删除表 1.2、DML写入读取数据 1、写入数据 2、读取数据 3、删除数据 2、大数据软件启动 一、Apache HBase 1、HBase Shell操作 先启动HBase。再…

学点Java_Day12_JDBC

1 JDBC 面向接口编程 在JDBC里面Java这个公司只是提供了一套接口Connection、Statement、ResultSet&#xff0c;每个数据库厂商实现了这套接口&#xff0c;例如MySql公司实现了&#xff1a;MySql驱动程序里面实现了这套接口&#xff0c;Java程序员只要调用实现了这些方法就可以…

C语言 C6031:返回值被忽略:“scanf“ 问题解决

我们在代码中 直接使用 scanf 就会出现这个错误 在最上面 加上 #define _CRT_SECURE_NO_WARNINGS//禁用安全函数警告 #pragma warning(disable:6031)//禁用 6031 的安全警告即可正常运行

Typora字数过多的时候造成卡顿现象如何解决?

Typora字数过多的时候造成卡顿现象如何解决&#xff1f; 点击 、切换、滚动、打字都有点卡顿&#xff0c;下面介绍三种方法&#xff0c;三种方法都可以尝试&#xff0c;建议先尝试方法一&#xff0c;效果不满意就用方法二&#xff0c;实在不行就最后一个取巧的办法。 方法1&a…

【大数据运维】minio 常见shell操作

文章目录 1. 安装2. 入门操作3. 命令帮助 1. 安装 下载 https://dl.min.io/client/mc/release/linux-amd64/ 赋权与使用 cp mc /usr/bin && chmod x /usr/bin/mc ./mc --help 2. 入门操作 # 添加minio到mc mc config host add minio_alias_name endpoint_adress …

R语言批量计算t检验,输出pvalue和均值

1.输入数据如下&#xff1a; 2.代码如下 setwd("E:/R/Rscripts/rG4相关绘图") # 读取CSV文件 data <- read.csv("box-cds-ABD-不同类型rg4-2.csv", stringsAsFactors FALSE)# 筛选出Type2列为指定五种类型的数据 filtered_data <- subset(data, …

电脑关机速度很慢怎么解决?

给电脑关机&#xff0c;总是要很久才完全关闭。这是因为计算机运行了太长时间&#xff0c;并且打开的程序太多&#xff0c;则关闭时间超过十秒钟&#xff0c;这是正常的现象。还有就是计算机升级或补丁程序更新也将导致计算机缓慢关闭。此时&#xff0c;建议耐心等待关闭完成。…

【C语言】预处理常见知识详解(宏详解)

文章目录 1、预定义符号2、define2.1 define 定义常量2.2 define 定义宏 3、#和##3.1 **#**3.2 **##** 4、条件编译&#xff08;开关&#xff09; 1、预定义符号 在C语言中内置了一些预定义符号&#xff0c;可以直接使用&#xff0c;这些符号实在预处理期间处理的&#xff0c;…

安卓国内ip代理app,畅游网络

随着移动互联网的普及和快速发展&#xff0c;安卓手机已经成为我们日常生活和工作中不可或缺的一部分。然而&#xff0c;由于地理位置、网络限制或其他因素&#xff0c;我们有时需要改变或隐藏自己的IP地址。这时&#xff0c;安卓国内IP代理App便成为了一个重要的工具。虎观代理…

局域网找不到共享电脑怎么办?

局域网找不到共享电脑是一种常见的问题&#xff0c;给我们的共享与合作带来一定的困扰。天联组网技术可以解决这个问题。本文将介绍天联组网的原理和优势&#xff0c;并探讨其在解决局域网找不到共享电脑问题中的应用。 天联组网的原理和优势 天联组网是一种基于加速服务器的远…

ES6 学习(三)-- es特性

文章目录 1. Symbol1.1 使用Symbol 作为对象属性名1.2 使用Symbol 作为常量 2. Iterator 迭代器2.1 for...of循环2.2 原生默认具备Interator 接口的对象2.3 给对象添加Iterator 迭代器2.4 ... 解构赋值 3. Set 结构3.1 初识 Set3.2 Set 实例属性和方法3.3 遍历3.4 相关面试题 4…

2024年天府杯A题论文免费分享,全网首发

天府杯免费分享资料&#xff08;A题论文代码&#xff09;链接&#xff1a;https://pan.baidu.com/s/17QtYt036ORk1xGIDi0JSew 提取码&#xff1a;sxjm 摘要 在近年来&#xff0c;随着科技的快速发展和社会经济的不断进步&#xff0c;科学研究的作用和地位日益凸显。本文基于…

springdata框架对es集成

什么是spring data框架 Spring Data是一个用于简化数据库、非关系型数据库、索引库访问&#xff0c;并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷&#xff0c;并支持 map-reduce框架和云计算数据服务。Spring Data可以极大的简化JPA(Elasticsearch…)的…

语音模块摄像头模块阿里云结合,实现垃圾的智能识别

语音模块&摄像头模块&阿里云结合 文章目录 语音模块&摄像头模块&阿里云结合1、实现的功能2、配置2.1 软件环境2.2 硬件配置 3、程序介绍3.1 程序概况3.2 语言模块SDK配置介绍3.3 程序文件3.3.1 开启摄像头的程序3.3.2 云端识别函数( Py > C ) & 串口程序…

Intellij IDEA安装配置Spark与运行

目录 Scala配置教程 配置Spark运行环境 编写Spark程序 1、包和导入 2、定义对象 3、主函数 4、创建Spark配置和上下文 5、定义输入文件路径 6、单词计数逻辑 7、输出结果 8、完整代码&#xff1a; Scala配置教程 IDEA配置Scala&#xff1a;教程 配置Spark运行环境 …

【微服务】Nacos(配置中心)

文章目录 1.AP和CP1.基本介绍2.说明 2.Nacos配置中心实例1.架构图2.在Nacos Server加入配置1.配置列表&#xff0c;加号2.加入配置3.点击发布&#xff0c;然后返回4.还可以编辑 3. 创建 Nacos 配置客户端模块获取配置中心信息1.创建子模块 e-commerce-nacos-config-client50002…