Robot Operating System——深度解析监控Parameters修改的底层实现

大纲

  • AsyncParametersClient
  • ParameterEventHandler
    • 监控全部Parameters
    • 监控Node上Parameters的变动
    • 触发
  • 总结

在《Robot Operating System——AsyncParametersClient监控Parameters的增删改行为》和《Robot Operating System——ParameterEventHandler监控Parameters的增删改行为》中,我们看到了三种方案的使用案例。本文我们将深度解析它们的底层实现。

AsyncParametersClient

在ROS 2中,我们看到很多同步方案的底层实际是通过对异步方案的封装实现的。本例中分析的同步方案SyncParametersClient也是如此。

// /opt/ros/jazzy/include/rclcpp/rclcpp/parameter_client.hpp
class SyncParametersClient
{
public:RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient)……RCLCPP_PUBLICSyncParametersClient(rclcpp::Executor::SharedPtr executor,const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,const std::string & remote_node_name = "",const rclcpp::QoS & qos_profile = rclcpp::ParametersQoS()): executor_(executor), node_base_interface_(node_base_interface){async_parameters_client_ =std::make_shared<AsyncParametersClient>(node_base_interface,node_topics_interface,node_graph_interface,node_services_interface,remote_node_name,qos_profile);}

所以我们直接分析底层的异步方案AsyncParametersClient的实现。

用于设置监控的AsyncParametersClient::on_parameter_event方法底层实现如下

/*** The NodeT type only needs to have a method called get_node_topics_interface()* which returns a shared_ptr to a NodeTopicsInterface, or be a* NodeTopicsInterface pointer itself.*/template<typename CallbackT,typename NodeT,typename AllocatorT = std::allocator<void>>static typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtron_parameter_event(NodeT && node,CallbackT && callback,const rclcpp::QoS & qos = rclcpp::ParameterEventsQoS(),const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>())){return rclcpp::create_subscription<rcl_interfaces::msg::ParameterEvent>(node,"/parameter_events",qos,std::forward<CallbackT>(callback),options);}

我们看到其底层实际创建了一个"/parameter_events"主题的订阅者,然后使用我们传递的回调来处理相关事件。这样在Parameters发生变动时,ROS 2底层会发布一条"/parameter_events"主题的事件消息,于是我们就会监控到其变化。

通过这段代码,我们可以看到AsyncParametersClient的方案还是非常简单的。这就需要我们在自己在回调函数中完成更多的逻辑判断。

而后面解析的ParameterEventHandler则帮我们做了很多“分门别类”的工作。

ParameterEventHandler

ParameterEventHandler的构造函数做了上述订阅"/parameter_events"主题的工作。

  /// Construct a parameter events monitor./*** \param[in] node The node to use to create any required subscribers.* \param[in] qos The QoS settings to use for any subscriptions.*/template<typename NodeT>explicit ParameterEventHandler(NodeT node,const rclcpp::QoS & qos =rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events))): node_base_(rclcpp::node_interfaces::get_node_base_interface(node)){auto node_topics = rclcpp::node_interfaces::get_node_topics_interface(node);callbacks_ = std::make_shared<Callbacks>();event_subscription_ = rclcpp::create_subscription<rcl_interfaces::msg::ParameterEvent>(node_topics, "/parameter_events", qos,[callbacks = callbacks_](const rcl_interfaces::msg::ParameterEvent & event) {callbacks->event_callback(event);});}

只是它的回调函数包含了两个回调函数集合:

  • Map结构的parameter_callbacks_。
  • List结构的event_callbacks_。
// https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/include/rclcpp/parameter_event_handler.hppusing CallbacksContainerType = std::list<ParameterCallbackHandle::WeakPtr>;struct Callbacks{std::recursive_mutex mutex_;// Map container for registered parametersstd::unordered_map<std::pair<std::string, std::string>,CallbacksContainerType,StringPairHash> parameter_callbacks_;std::list<ParameterEventCallbackHandle::WeakPtr> event_callbacks_;/// Callback for parameter events subscriptions.RCLCPP_PUBLICvoidevent_callback(const rcl_interfaces::msg::ParameterEvent & event);};struct ParameterEventCallbackHandle
{RCLCPP_SMART_PTR_DEFINITIONS(ParameterEventCallbackHandle)using ParameterEventCallbackType =std::function<void (const rcl_interfaces::msg::ParameterEvent &)>;ParameterEventCallbackType callback;
};struct ParameterCallbackHandle
{RCLCPP_SMART_PTR_DEFINITIONS(ParameterCallbackHandle)using ParameterCallbackType = std::function<void (const rclcpp::Parameter &)>;std::string parameter_name;std::string node_name;ParameterCallbackType callback;
};

监控全部Parameters

add_parameter_event_callback会监控所有Node上Parameters的变动。

我们看到它在底层实际是将我们传入的回调插入到上述回调函数List的头部(emplace_front)。这说明add_parameter_event_callback可以被反复调用,也就说我们可以设置多个不同的回调来监控所有Parameters变动。

ParameterEventCallbackHandle::SharedPtr
ParameterEventHandler::add_parameter_event_callback(ParameterEventCallbackType callback)
{std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);auto handle = std::make_shared<ParameterEventCallbackHandle>();handle->callback = callback;callbacks_->event_callbacks_.emplace_front(handle);return handle;
}

监控Node上Parameters的变动

add_parameter_callback会将传入的Parameter的名称以及所在Node上的名称,保存到回调结构parameter_callbacks_的Key中,Value就是我们传入的回调函数List的头部。这样在数据结构上,我们让Pair<ParameterName,NodeName>和Callback产生了映射。

通过这段底层实现,可以发现,我们是可以针对同一Pair<ParameterName,NodeName>设置多个回调监控。

ParameterCallbackHandle::SharedPtr
ParameterEventHandler::add_parameter_callback(const std::string & parameter_name,ParameterCallbackType callback,const std::string & node_name)
{std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);auto full_node_name = resolve_path(node_name);auto handle = std::make_shared<ParameterCallbackHandle>();handle->callback = callback;handle->parameter_name = parameter_name;handle->node_name = full_node_name;// the last callback registered is executed first.callbacks_->parameter_callbacks_[{parameter_name, full_node_name}].emplace_front(handle);return handle;
}

触发

当 "/parameter_events"收到发布的事件后,Callbacks::event_callback会被调用。
它会先挨个遍历针对具体Parameter的回调结构parameter_callbacks_

void
ParameterEventHandler::Callbacks::event_callback(const rcl_interfaces::msg::ParameterEvent & event)
{std::lock_guard<std::recursive_mutex> lock(mutex_);for (auto it = parameter_callbacks_.begin(); it != parameter_callbacks_.end(); ++it) {

确定其是否和事件中的ParameterName以及NodeName一致。如果一直则提取出Parameter结构;如果不一致就继续遍历寻找。

    rclcpp::Parameter p;if (get_parameter_from_event(event, p, it->first.first, it->first.second)) {

找到一致的Callback就直接调用

        auto shared_handle = cb->lock();if (nullptr != shared_handle) {shared_handle->callback(p);} else {cb = it->second.erase(cb);}}}}

处理完针对具体Node和Parameter的回调后,会去处理针对所有Node、所有Parameters的回调。

  for (auto event_cb = event_callbacks_.begin(); event_cb != event_callbacks_.end(); ++event_cb) {auto shared_event_handle = event_cb->lock();if (nullptr != shared_event_handle) {shared_event_handle->callback(event);} else {event_cb = event_callbacks_.erase(event_cb);}}
}

总结

ParameterEventHandler包含了AsyncParametersClient的实现,且比其功能更丰富。AsyncParametersClient只能设置一个全部Parameter监控回调函数,而ParameterEventHandler可以设置多个。而且ParameterEventHandler还可以针对不同Node、不同Parameter设置多个不同的回调,但是AsyncParametersClient没有这个能力。

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

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

相关文章

《Java初阶数据结构》----10.<Map和Set---TreeSet和TreeMapHashSet和HashMap >

前言&#xff1a; 大家好&#xff0c;我目前在学习java。我准备利用这个暑假&#xff0c;来复习之前学过的内容&#xff0c;并整理好之前写过的博客进行发布。如果博客中有错误或者没有读懂的地方。热烈欢迎大家在评论区进行讨论&#xff01;&#xff01;&#xff01; 喜欢我文…

Linux守护进程daemon与服务service及systemctl命令的作用

在 Linux 系统的服务管理中会经常提到 daemon&#xff08;守护进程&#xff09; 与 service&#xff08;服务&#xff09;&#xff0c;守护进程 daemon 是指后台运行的进程&#xff0c;随系统启动而为应用程序提供支持&#xff0c; 而服务 service 则用于提供某种功能。 简单的…

C/C++大雪纷飞代码

目录 写在前面 C语言简介 EasyX简介 大雪纷飞 运行结果 写在后面 写在前面 本期博主给大家带来了C/C实现的大雪纷飞代码&#xff0c;一起来看看吧&#xff01; 系列推荐 序号目录直达链接1爱心代码https://want595.blog.csdn.net/article/details/1363606842李峋同款跳…

C#实现数据采集系统-Mqtt实现采集数据转发

在数据采集系统中,通过ModbusTcp采集到数据之后,再通过MQTT转发到其他应用 MQTT操作 安装MQTT mqtt介绍和环境安装 使用MQTT 在C#/Net中使用Mqtt MQTT类封装 MQTT配置类 public class MqttConfig{public string Ip {get; set;

每日任务:网络协议对比:HTTPS/HTTP与TCP/UDP

1.HTTPS和HTTP有哪些区别 HTTP 是超文本传输协议&#xff0c;信息是明文传输&#xff0c;存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷&#xff0c;在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议&#xff0c;使得报文能够加密传输。HTTP 连接建立相对简单&#x…

【LeetCode】141.环形链表、142. 环形链表 II(算法 + 图解)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构 &#x1f4da;本系列文章为个人学…

网站如何从0-1搭建部署蓝图介绍

第一步&#xff1a;网站规划 确定网站目的&#xff1a;明确网站的目标和预期的受众。内容规划&#xff1a;决定网站将包含哪些内容和功能。技术需求分析&#xff1a;确定所需的技术栈&#xff0c;例如前端和后端技术。 第二步&#xff1a;设计 草图和布局&#xff1a;绘制网…

AI学习记录 - 本地知识库实现的相关知识

在公司内部实现了个知识库&#xff0c;但这里只介绍在实现知识库的过程中用到的知识。 1、分词器 先分词&#xff0c;中文可以使用jieba分词 2、构造数据集 将词汇向量化是自然语言处理中的重要任务&#xff0c;它可以将文本数据转化为计算机能够理解和处理的向量形式。以…

在react中如何计算本地存储体积

1.定义useLocalStorageSize钩子函数 // 计算localStorage大小 function useLocalStorageSize() {const [size, setSize] useState(0);useEffect(() > {const calculateSize () > {let totalSize 0;for (let key in localStorage) {//过滤掉继承自原型链的属性if (loc…

抄作业-跟着《React通关秘籍》捣鼓React-playground-上集

文章目录 前言1. 搭建react 开发环境2、react hooks 知识3. 目标&#xff1a;跟着小册实现 react-playground3.1 整体布局初始化项目使用Alloment 来实现左右分屏的拖拉功能 3.2 代码编辑器Monaco Editor 3.3 实现了多文件的切换用 useContext 来共享数据。优化 tab的样式&…

扫雷游戏小程序

目录 一.文件 1.头文件 2.源文件 二.游戏界面和执行(test.c) 三.函数实现(void game部分,源文件game.c) 1.定义雷二维数组和展示二维数组 2.初始化地雷数组 3.初始化显示的数组 4.显示当前的情况 5.随机放置地雷 6.排雷 ps:深度优先遍历数组 四.结束 一.文件 1.头…

接口测试工具:yapi和postman、Apifox 对比选型

在接口测试工具领域&#xff0c;YApi、Postman和Apifox都是备受推崇的工具&#xff0c;它们各有特点&#xff0c;适用于不同的测试场景和需求。以下是对这三款工具的详细对比选型分析&#xff1a; 一、YApi 1. 概述 YApi是一个可本地部署的、打通前后端及QA的、可视化的接口…

《Single-Stage Extensive Semantic Fusion for multi-modal sarcasm detection》

系列论文研读目录 文章目录 系列论文研读目录文章题目含义ABSTRACTKeywords1. Introduction2. Related work3. Method3.1. Multi-modal projection 多模态投影3.2. Extensive Semantic Fusion Multiway Transformer 可拓语义融合多路Transformer3.3. Multi-objective optimizat…

GO-学习-04-基本数据类型-浮点型

浮点型&#xff1a;float32和float64 %f -3.4e38——3.4e38 package main //import "fmt" //import "math" import ("fmt""unsafe""math" )func main(){//1.定义float类型var a float32 3.12fmt.Printf("值&#xff…

LeetCode 热题 HOT 100 (011/100)【宇宙最简单版】

【图论】No. 0200 岛屿数量 【中等】&#x1f449;力扣对应题目指路 希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&#xf…

DjangoRF-10-过滤-django-filter

1、安装pip install django-filter https://pypi.org/ 搜索django-filter基础用法 2、进行配置 3、进行内容调试。 4、如果碰到没有关联的字段。interfaces和projects没有直接关联字段&#xff0c;但是interface和module有关联&#xff0c;而且module和projects关联&#x…

linux下使用yum安装mysql

本文使用常规方式手动安装mysql 第一步 下载mysql的repo源 wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm第二步 安装mysql-community-release-el7-5.noarch.rpm包 rpm -ivh mysql-community-release-el7-5.noarch.rpm第三步 安装mysql-server yum -y…

Esp_server 安卓嵌入壳子,原创! 2024/7/28 20:58

用到的软件: uni-app 思路: 让用户感觉是,一个完整的程序.实际上只是一个类浏览器壳子.轻便小巧. 由于是第一次用uni-app开发类软件,所以前前后后耗费7小时! 隔行如隔山,不是白讲的. 软件界面: 软件功能: 简化输入: 输入ip地址,例:http:// www.baidu.com 完整网址 前面…

Javaer 5分钟入门 Golang

文章目录 前言一个 go 文件长啥样&#xff1f;Go 语言的变量变量类型变量声明 Go 语言的函数&方法Go 语言没有的概念访问修饰符面向对象模型异常处理机制 Go 语言高效简洁的并发编程总结 前言 这两年主流的编程语言 Golang&#xff0c;想必大家或多或少都接触过。云原生领…

使用 Docker Compose 部署 RabbitMQ 的一些经验与踩坑记录

前言 RabbitMQ 是一个功能强大的开源消息队列系统&#xff0c;它实现了高效的消息通信和异步处理。 本文主要介绍其基于 Docker-Compose 的部署安装和一些使用的经验。 特点 成熟&#xff0c;稳定消息持久化灵活的消息路由高性能&#xff0c;高可用性&#xff0c;可扩展性高支…