当前位置: 首页 > 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不支持复制构造和复制…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

C++11 constexpr和字面类型:从入门到精通

文章目录 引言一、constexpr的基本概念与使用1.1 constexpr的定义与作用1.2 constexpr变量1.3 constexpr函数1.4 constexpr在类构造函数中的应用1.5 constexpr的优势 二、字面类型的基本概念与使用2.1 字面类型的定义与作用2.2 字面类型的应用场景2.2.1 常量定义2.2.2 模板参数…...

使用python进行图像处理—图像滤波(5)

图像滤波是图像处理中最基本和最重要的操作之一。它的目的是在空间域上修改图像的像素值&#xff0c;以达到平滑&#xff08;去噪&#xff09;、锐化、边缘检测等效果。滤波通常通过卷积操作实现。 5.1卷积(Convolution)原理 卷积是滤波的核心。它是一种数学运算&#xff0c;…...

第2课 SiC MOSFET与 Si IGBT 静态特性对比

2.1 输出特性对比 2.2 转移特性对比 2.1 输出特性对比 器件的输出特性描述了当温度和栅源电压(栅射电压)为某一具体数值时,漏极电流(集电极电流...

【笔记】结合 Conda任意创建和配置不同 Python 版本的双轨隔离的 Poetry 虚拟环境

如何结合 Conda 任意创建和配置不同 Python 版本的双轨隔离的Poetry 虚拟环境&#xff1f; 在 Python 开发中&#xff0c;为不同项目配置独立且适配的虚拟环境至关重要。结合 Conda 和 Poetry 工具&#xff0c;能高效创建不同 Python 版本的 Poetry 虚拟环境&#xff0c;接下来…...