当前位置: 首页 > news >正文

ROS 2基础概念#5:执行器(Executor)| ROS 2学习笔记

在ROS 2中,Executor是一个核心概念,负责管理节点(Node)中的回调函数,如订阅消息的回调、服务请求的回调、定时器回调等。Executor决定了何时以及如何执行这些回调,从而在ROS 2系统中实现异步编程。

ROS 2 Executor的工作原理

在ROS 2中,节点是构成系统的基本单元,每个节点可以发布和订阅主题(Topic),提供和使用服务(Service),以及使用定时器(Timer)。Executor是控制节点中回调函数执行的机制。当消息到达或服务请求发生时,这些回调函数需要被调用。Executor负责从节点中收集待处理的回调,并决定执行它们的顺序和时间。

ROS 2 Executor的类型

ROS 2提供了几种不同类型的Executor,每种都有其特定的用途和调度策略:

  1. SingleThreadedExecutor:这是最简单的Executor,它在单个线程中顺序执行所有回调。这意味着在任意时刻,只有一个回调被执行,直到完成。这种Executor适用于不需要并行处理的简单应用。
  2. MultiThreadedExecutor:这种Executor允许在多个线程中并行执行回调。它使用一个线程池,回调任务可以在不同的线程中同时运行,适用于需要处理大量并行任务的复杂应用。
  3. StaticSingleThreadedExecutor:这是对SingleThreadedExecutor的优化,适用于性能要求较高的场景。它在内部缓存了一些信息,以减少每次执行回调时的开销。

通过为每个节点调用 add_node(..) ,所有三个执行器都可以与多个节点一起使用。

rclcpp::Node::SharedPtr node1 = ...
rclcpp::Node::SharedPtr node2 = ...
rclcpp::Node::SharedPtr node3 = ...rclcpp::executors::StaticSingleThreadedExecutor executor;
executor.add_node(node1);
executor.add_node(node2);
executor.add_node(node3);
executor.spin();

尽管基本 API 非常相似,但ROS 2 Executor 类(在 rclcpp 中的 executor.hpp 中、rclpy 中的 executors.py 中或 rclc 中的 executor.h 中)比 ROS 1 中的spin机制提供了更多对执行管理的控制。接下来的内容我们重点关注 C++ 客户端库 rclcpp。

基本使用方法

在最简单的情况下,主线程用于通过调用 rclcpp::spin(..) 来处理 Node 的传入消息和事件,如下所示:

int main(int argc, char* argv[])
{// Some initialization.rclcpp::init(argc, argv);...// Instantiate a node.rclcpp::Node::SharedPtr node = ...// Run the executor.rclcpp::spin(node);// Shutdown and exit....return 0;
}

对 spin(node) 的调用基本上扩展为单线程执行器的实例化和调用,这是最简单的执行器:

rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
executor.spin();

通过调用 Executor 实例的 spin(),当前线程开始查询 rcl 和中间件层以获取传入消息和其他事件,并调用相应的回调函数,直到节点关闭。 为了不影响中间件的 QoS 设置,传入消息不会存储在客户端库层的队列中,而是保留在中间件中,直到由回调函数进行处理(这是与 ROS 1 的一个关键区别)。等待集(waitset)用于通知执行器中间件层上的可用消息,每个队列有一个二进制标志。 等待集还用于检测计时器何时到期。

单线程执行器也被组件的容器进程使用,即在没有显式主函数的情况下创建和执行节点的所有情况。

回调(callback)分组

ROS 2 允许按组组织节点的回调。 在 rclcpp 中,可以通过 Node 类的 create_callback_group 函数创建这样的回调组。 在rclpy中,通过调用特定回调组类型的构造函数来完成相同的操作。 回调组必须在节点的整个执行过程中存储(例如作为类成员),否则执行器将无法触发回调。 然后,可以在创建订阅、计时器等时指定该回调组 – 例如通过订阅选项:C++或Python。

my_callback_group = MutuallyExclusiveCallbackGroup()
my_subscription = self.create_subscription(Int32, "/topic", self.callback, qos_profile=1, callback_group=my_callback_group)

所有在没有指定回调组的情况下创建的订阅、计时器等都将分配给默认回调组。 默认回调组可以通过 rclcpp 中的 NodeBaseInterface::get_default_callback_group() 和 rclpy 中的 Node.default_callback_group 查询。

有两种类型的回调组,必须在实例化时指定类型:

  • Mutually exclusive: 该组的回调不得并行执行。
  • Reentrant: 该组的回调可以并行执行。

不同回调组的回调可能总是并行执行。 多线程执行器使用其线程作为资源池,根据这些条件并行处理尽可能多的回调。 有关如何有效使用回调组的提示,请参阅使用回调组。

自 Galatic 版本以来,rclcpp 中 Executor 基类的接口已通过新函数 add_callback_group(..) 进行了细化。 这允许将回调组分配给不同的执行器。 通过使用操作系统调度程序配置底层线程,特定回调可以优先于其他回调。 例如,控制循环的订阅和定时器可以优先于节点的所有其他订阅和标准服务。 example_rclcpp_cbg_executor 包提供了此机制的演示。

调度语义

如果回调的处理时间短于消息和事件发生的周期,Executor 基本上按照 FIFO 的顺序处理它们。 但是,如果某些回调的处理时间较长,消息和事件将在堆栈的较低层排队。 等待集机制只向执行器报告关于这些队列的很少的信息。 具体来说,它仅报告是否有特定主题的消息。 执行器使用此信息以循环方式处理消息(包括服务和操作),但不是按照 FIFO 顺序。 下面的流程图直观地展示了这种调度语义。

Casini 等人在 ECRTS 2019 上的论文首次描述了这种语义(注意:本文还解释了计时器事件的优先级高于所有其他消息。这种优先级在 Eloquent 版本中已被删除。)

Executor的使用

在ROS 2应用程序中,开发者需要创建一个或多个节点,并将它们添加到Executor。然后,开发者调用Executor的spin方法来开始循环,等待并执行回调。例如,使用SingleThreadedExecutor的基本用法如下:

import rclpy
from rclpy.executors import SingleThreadedExecutor
from my_package import MyNode
def main(args=None):
rclpy.init(args=args)
node = MyNode()
executor = SingleThreadedExecutor()
executor.add_node(node)
try:executor.spin()  # 开始执行回调
finally:executor.remove_node(node)rclpy.shutdown()
if name == 'main':
main()

展望

虽然 rclcpp 的三个执行器适用于大多数应用程序,但它也存在一些问题,使它们不适合实时应用程序,因为实时应用程序需要明确定义的执行时间、确定性以及对执行顺序的自定义控制。 以下是其中一些问题的摘要:

  1. 复杂且混合的调度语义。 理想情况下,您需要明确定义的调度语义来执行正式的时序分析。
  2. 回调可能会受到优先级反转的影响。 较高优先级的回调可能会被较低优先级的回调阻止。
  3. 没有对回调执行顺序的显式控制。
  4. 没有对特定主题的触发的内置控制。

此外,执行器在 CPU 和内存使用方面的开销也相当大。 静态单线程执行器大大减少了这种开销,但对于某些应用程序来说可能还不够。

这些问题已通过以下开发得到部分解决:

  • rclcpp WaitSet:rclcpp 的 WaitSet 类允许直接等待订阅、计时器、服务服务器、操作服务器等,而不是使用 Executor。 它可用于实现确定性的、用户定义的处理序列,可能会一起处理来自不同订阅的多个消息。 example_rclcpp_wait_set 包提供了几个使用此用户级等待设置机制的示例。
  • rclc 执行器:来自 C 客户端库 rclc 的执行器,为 micro-ROS 开发,为用户提供了对回调执行顺序的细粒度控制,并允许自定义触发条件来激活回调。 此外,它还实现了逻辑执行时间(LET)语义的思想。

总结

ROS 2的Executor提供了一个灵活且强大的机制来管理和调度节点中的回调函数,使得开发者可以根据应用的需求选择或自定义合适的执行策略。通过合理地使用Executor,可以有效地提高ROS 2应用程序的性能和响应能力。

作者个人Blog ROS 2学习笔记系列文章: ROS 2学习笔记 归档 - HY's Blog

相关文章:

ROS 2基础概念#5:执行器(Executor)| ROS 2学习笔记

在ROS 2中,Executor是一个核心概念,负责管理节点(Node)中的回调函数,如订阅消息的回调、服务请求的回调、定时器回调等。Executor决定了何时以及如何执行这些回调,从而在ROS 2系统中实现异步编程。 ROS 2 …...

Unity 动画(旧版-新版)

旧版 旧版-动画组件:Animation 窗口-动画 动画文件后缀: .anim 将制作后的动画拖动到Animation组件上 旧版的操作 using System.Collections; using System.Collections.Generic; using UnityEngine;public class c1 : MonoBehaviour {// Start is called before…...

Linux和Windows操作系统线程调度策略

本文介绍Linux和Windows操作系统线程调度策略。 不同的操作系统具有不同的线程调度策略,本文针对常见的操作系统(Linux和Windows操作系统)对其线程调度策略作简要说明,并不对其内在运行机制作详细介绍。 1.Linux操作系统线程调度…...

[OpenWrt 22.03] ttylogin添加登录密码与禁止登录的配置

ttylogin 的使用 Openwrt 串口默认是没有密码的。Openwrt启动后,一个默认的密码将被启用去保护ssh登录和页面(http)登录,而串口登录密码却是空缺的。 对于 Openwrt,当内核初始化后,就会启动第一个进程 init,init进程会进行一系列的系统初始化工作,然后会读取 /etc/in…...

RK3568平台 USB数据包的收发格式

一.USB硬件拓扑结构 compound device :多个设备组合起来,通过HUB跟Host相连composite device :一个物理设备有多个逻辑设备(multiple interfaces) 在软件开发过程中,我们可以忽略Hub的存在,硬件拓扑图简化如下&#x…...

Day 8.TCP通信

TCP通信 TCP发端: socket connect send recv close TCP收端: socket bind listen accept send recv close 1.connect int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 功能:发…...

小游戏加固方案已全面适配微信、QQ、抖音、快手、美团、华为、支付宝渠道

2023年,国内移动游戏收入与游戏用户规模双双创下历史新高。其中小游戏异军突起,市场规模达到200亿元,同比增长300%,成了万众瞩目的行业新风口。 小游戏的高速发展带来了更多的活力,产出了多款月流水过亿的热门游戏。行…...

粉色ui微信小程序源码/背景图/头像/壁纸小程序源码带流量主

云开发版粉色UI微信小程序源码,背景图、头像、壁纸小程序源码,带流量主功能。 云开发小程序源码无需服务器和域名即可搭建小程序另外还带有流量主功能噢!微信平台注册小程序就可以了。 这套粉色UI非常的好看,里面保护有背景图、…...

chrome选项页面options page配置

options 页面用以定制Chrome浏览器扩展程序的运行参数。 通过Chrome 浏览器的“工具 ->更多工具->扩展程序”,打开chrome://extensions页面,可以看到有的Google Chrome扩展程序有“选项Options”链接,如下图所示。单击“选项Options”…...

迭代器失效问题(C++)

迭代器失效就是迭代器指向的位置已经不是原来的含义了,或者是指向的位置是非法的。以下是失效的几种情况: 删除元素: 此处发生了迭代器的失效,因为erase返回的是下一个元素的位置的迭代器,所以在删除1这个元素的时候&…...

2-web端管理界面使用rabbitmq

Web管理界面可以直接操作RabbitMQ,下面进行操作并记录步骤 1、添加交换器: Add a new exchange 中,Name是交换器名称,Type是交换器类型,有direce、fanout、heders、topic 4种。 这里先只填Name和选个类型,…...

【华为OD机试】最多购买宝石数目【C卷|100分】

【华为OD机试】-真题 !!点这里!! 【华为OD机试】真题考点分类 !!点这里 !! 题目描述: 橱窗里有一排宝石,不同的宝石对应不同的价格, 宝石的价格标记为gems[i],0<=i<n, n = gems.length 宝石可同时出售0个或多个,如果同时出售多个,则要求出售的宝石编号连续; 例如…...

RK3588 Android 12 源码编译与开发板烧录

前言 开发板型号&#xff1a;RK_EVB7_RK3588_LP4…_V11 获取RK3588源码 解压RK提供的Android 12的tgz&#xff0c;开通权限 your_verify.sh # 身份验证脚本&#xff08;由RK提供&#xff09; .repo/repo/repo sync -l # 检出代码 .repo/repo/repo sync -c # 同…...

学习JAVA的第十四天(基础)

目录 Collection集合 迭代器遍历 增强for遍历 Lambda表达式遍历 List集合 遍历 数据结构 栈 队列 数组 链表 前言&#xff1a; 学习JAVA的第十三天 Collection集合 Collection的遍历方式&#xff1a; 迭代器&#xff08;不依赖索引&#xff09;遍…...

安捷伦N5182A信号源 AgilentN5182A

描述&#xff1a; 1&#xff09;信号特性&#xff1a; 250 kHz to 3 or 6 GHz频率范围 (可选低至 100 kHz) 13 dBm 1GHz输出功率 5dBm输出功率时W-CDMA动态范围:单载波 ≤-73 dBc &#xff1b;4载波≤-66 dBc ≤1.2 ms切换速度在SCPI模式 2&#xff09;调制与扫描&#x…...

就业班 2401--3.7 Linux Day13--日志轮转+jumpserver堡垒机

一、日志轮转 日志重要性 Linux系统日志对管理员来说&#xff0c;是了解系统运行的主要途径&#xff0c;因此需要对 Linux 日志系统有个详细的了解。 Linux 系统内核和许多程序会产生各种错误信息、告警信息和其他的提示信息&#xff0c;这些各种信息都应该记录到日志文件中&a…...

信息安全概论 习题

用密钥information构造一个Playfair矩阵 Playfair密码是一种替换加密技术&#xff0c;它不像传统的单字母替换密码那样工作&#xff0c;而是将信息分成一对字母&#xff08;双字母&#xff09;进行加密。构造Playfair矩阵时&#xff0c;首先需要一个密钥词&#xff0c;然后根据…...

仓储管理系统(WMS) 的研发历程-PRD撰写

题外话&#xff1a;PRD的展现形式有多种&#xff0c;有的人喜欢在axure上直接做产品描述&#xff0c;觉得word较为过时&#xff0c;有的人认为axure不专业&#xff0c;任何展现形式都无可厚非&#xff0c;重要的达到PRD的目的&#xff0c;PRD的目标是让团队知道需求实现细节&am…...

springboot实现多线程开发(使用@Async注解,简单易上手)

根据springboot的核心思想便捷开发&#xff0c;使用多线程也变得简单起来&#xff0c;通过一下几个步骤即可实现。 核心注解 EnableAsync将此注解加在启动类上&#xff0c;使项目支持多线程。 Async 使用我们的Async注解在所需要进行多线程的类上即可实现。 配置线程池 …...

并发支持库(1)-线程

线程允许多个程序任务在统一时间执行&#xff0c;不同的线程可以共享内存空间&#xff0c;每个线程也有自己的栈空间。 线程类 thread 类thread表示单个执行线程。线程在thread构造对象时开始执行。每个thread对象表示唯一的一个线程&#xff0c;thread不支持复制构造和复制…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...