掘金 人工智能 前天 17:55
ROS2 Jazzy:执行器
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

ROS2中的执行管理依赖于执行器(Executor),它利用操作系统线程来调用订阅者、定时器等的回调函数。C++客户端库rclcpp提供了单线程、多线程和静态单线程三种执行器,并引入了回调组(Callback Groups)机制,允许用户将回调按互斥或可重入的类型进行组织,以实现更精细的回调处理和并行化。执行器通过等待集感知消息和事件,并按轮询方式处理。尽管当前执行器已相当强大,但未来仍有改进空间,例如通过rclcpp WaitSet直接控制处理序列,或借鉴rclc执行器的细粒度控制和LET语义,以满足更严格的实时应用需求。

💡 **执行器是ROS2的核心组件,负责管理节点的回调函数执行。** 执行器利用底层操作系统的线程,在接收到消息和事件时,能够精确地调用订阅者、定时器、服务服务器、动作服务器等的回调函数。rclcpp库提供了比ROS1的spin机制更精细的执行管理控制,其基本用法可以通过`rclcpp::spin(node)`简化实现,这等同于使用单线程执行器。

🚀 **rclcpp提供了三种执行器以满足不同需求:单线程执行器、多线程执行器和静态单线程执行器。** 单线程执行器适用于基本场景;多线程执行器能创建多个线程以并行处理消息和事件;静态单线程执行器则对节点结构的扫描进行了优化,适用于节点初始化时就创建好所有资源的情况。所有执行器都支持添加多个节点,实现资源共享。

🔀 **回调组(Callback Groups)是组织和控制回调执行的关键。** 用户可以创建互斥组(Mutually Exclusive)和可重入组(Reentrant)来管理回调的并行性。互斥组的回调不能并行执行,而可重入组的回调则允许并行。不同回调组的回调始终可以并行执行。多线程执行器会根据这些规则,利用线程池实现高效的并行处理。

⏳ **ROS2执行器的调度语义为轮询(Round-Robin),而非严格的先进先出(FIFO)。** 当回调处理时间短于事件产生周期时,执行器大致按FIFO顺序处理;但若回调处理时间较长,消息会在底层栈中排队。执行器通过等待集(Wait Set)获知消息可用性,并以此进行轮询调度,而不是严格按照消息到达的顺序。这种语义在某些实时场景下可能需要进一步优化。

🛠️ **未来展望聚焦于解决实时性、确定性和控制粒度问题。** 当前执行器在混合调度语义、优先级反转、回调执行顺序控制、特定主题触发控制以及CPU/内存开销等方面仍有改进空间。rclcpp WaitSet允许用户绕过执行器直接控制处理序列,而rclc执行器则提供了更细粒度的控制,支持自定义触发条件和LET语义,为实时应用提供了新的解决方案。

ROS2 中的执行管理由 执行器(Executor) 处理。执行器利用底层操作系统的一个或多个线程,在接收到消息和事件时调用订阅者、定时器、服务服务器、动作服务器的回调函数。显式的Executor类(rclcpp 中的executor.hpp、rclpy 中的executors.py或 rclc 中的executor.h)提供了与ROS1 API 非常相似的,但是比 ROS1 中spin机制更精细的执行管理控制。

以下我们主要关注 C++ 客户端库 rclcpp。

基本用法

最简单的情况是通过调用rclcpp::spin(..),使用主线程处理节点的传入消息和事件,示例如下:

int main(int argc, char* argv[]){   // 初始化   rclcpp::init(argc, argv);   ...   // 创建节点   rclcpp::Node::SharedPtr node = ...   // 运行执行器   rclcpp::spin(node);   // 关闭并退出   ...   return 0;}

调用spin(node)本质上等同于实例化并调用单线程执行器(Single-Threaded Executor),这是最简单的执行器:

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

调用执行器实例的spin()后,当前线程会开始查询 rcl 和中间件层的传入消息和其他事件,并调用相应的回调函数,一直到节点关闭。为了不影响中间件的 QoS 设置,传入消息不会存储在客户端库层的队列中,而是保留在中间件中,直到被回调函数处理(这里是与 ROS1 的关键区别)。执行器通过等待集(wait set) 感知中间件层的可用消息,每个队列对应一个二进制标志位,等待集也用于检测定时器是否到期。

在所有无需显式主函数创建和执行节点的场景中,单线程执行器也用于组件的容器进程。

执行器类型

当前,rclcpp 提供了三种执行器类型,均派生自同一个父类:

其中多线程执行器(Multi-Threaded Executor) 会创建可配置数量的线程,允许并行处理多条消息或事件。而静态单线程执行器(Static Single-Threaded Executor) 针对节点的订阅、定时器、服务服务器、动作服务器等结构的扫描进行了运行时优化。它仅在添加节点时执行一次扫描,而另外两种执行器则会定期扫描这些变化。因此,静态单线程执行器应该仅仅在节点初始化时创建所有订阅、定时器等资源的场景中使用。

所有三种执行器都可以通过调用 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();

在上面这个例子中,静态单线程执行器的单个线程用于共同服务三个节点。对于多线程执行器,实际并行性取决于回调组(Callback Groups)。

回调组

ROS2 允许将节点的回调组织成组。在 rclcpp 中,可通过 Node 类的 create_callback_group 函数创建回调组;在 rclpy 中,则通过调用特定回调组类型的构造函数实现。回调组必须在节点的整个执行周期中保持存活(例如作为类成员),否则执行器无法触发回调。创建订阅、定时器等资源时,可通过选项指定回调组,例如:

C++

my_callback_group = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);rclcpp::SubscriptionOptions options;options.callback_group = my_callback_group;my_subscription = create_subscription<Int32>("/topic", rclcpp::SensorDataQoS(),                                             callback, options);

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 获取。

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

不同回调组的回调始终可以并行执行。多线程执行器会根据这些规则,利用线程池尽可能并行处理回调。有关高效使用回调组的建议,可以参考《使用回调组》。

rclcpp 的执行器基类还提供 add_callback_group(..) 函数,允许将回调组分配给不同执行器。通过操作系统调度器配置底层线程,可对特定回调设置优先级(例如,控制回路的订阅和定时器可优先于节点的其他订阅和标准服务)examples_rclcpp_cbg_executor(github.com/ros2/exampl…) 软件包提供了该机制的演示。

调度语义

如果回调的处理时间短于消息和事件的产生周期,执行器基本按 先进先出(FIFO) 顺序处理它们。而如果某些回调处理时间较长,消息和事件会在底层栈中排队。等待集(Wait Set)机制仅向执行器报告这些队列的少量信息(例如是否有某个主题的消息)。执行器基于此信息以 轮询(Round-Robin) 而非 FIFO 顺序处理消息、服务和动作。以下流程图展示了该调度语义:

这种语义最早由 Casini 等人在 ECRTS 2019 会议的论文(drops.dagstuhl.de/opus/vollte…) 中描述(注:该论文还提到定时器事件优先级高于其他消息,但这一优先级在 Eloquent 版本中已移除(github.com/ros2/rclcpp…

未来展望

尽管 rclcpp 的三种执行器适用于大多数应用,但仍存在一些不适合实时应用的问题(实时应用需要明确的执行时间、确定性和对执行顺序的自定义控制),主要问题包括:

下面这两个功能部分解决了这些问题:


欢迎关注【智践行】,发 【机器人】 获得机器人经典学习资料

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

ROS2 执行器 回调组 rclcpp 实时性
相关文章