【ROS2】概念:中级-执行器 Executors

 目录

  •  概述

  •  基本使用

  •  执行器类型

  •  回调组

  •  调度语义

  • Outlook

  •  更多信息

 概述 

在 ROS 2 中,执行管理由执行器处理。执行器使用底层操作系统的一个或多个线程来调用订阅、定时器、服务服务器、操作服务器等的回调,以处理传入的消息和事件。显式的 Executor 类(在 rclcpp 中的 executor.hpp,在 rclpy 中的 executors.py,或在 rclc 中的 executor.h)比 ROS 1 中的旋转机制提供了更多的执行管理控制,尽管基本 API 非常相似。

在以下内容中,我们重点介绍 C++ 客户端库 rclcpp。

 基本用法 

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

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();

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

072f34a16ae5c50c7e29e43b931789dd.png

单线程执行器也被容器进程用于组件 https://docs.ros.org/en/jazzy/Concepts/Intermediate/About-Composition.html ,即在所有没有显式主函数的情况下创建和执行节点。

执行器类型 

目前,rclcpp 提供三种执行器类型,均派生自一个共享的父类:

4967f92b789692cb48f56ffb617d6b6b.png

多线程执行器创建可配置数量的线程,以允许并行处理多个消息或事件静态单线程执行器优化了扫描节点结构(例如订阅、计时器、服务服务器、操作服务器等)的运行成本。它仅在添加节点时执行一次此扫描,而其他两个执行器会定期扫描此类更改。因此,静态单线程执行器应仅与在初始化期间创建所有订阅、计时器等的节点一起使用。

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

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

回调组 

ROS 2 允许将节点的回调组织成组。在 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 进行查询。

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

  • 互斥:此组的回调函数不能并行执行。

  • 可重入:此组的回调可以并行执行。

不同回调组的回调可能始终并行执行。多线程执行器使用其线程作为池,根据这些条件尽可能并行处理尽可能多的回调。有关如何有效使用回调组的提示,请参阅使用回调组 https://docs.ros.org/en/jazzy/How-To-Guides/Using-callback-groups.html 。

rclcpp 中的 Executor 基类也具有 add_callback_group(..) 功能,它允许将回调组分配给不同的 Executor。通过使用操作系统调度程序配置底层线程,可以优先处理特定的回调。例如,控制循环的订阅和定时器可以优先于节点的所有其他订阅和标准服务。examples_rclcpp_cbg_executor 包 https://github.com/ros2/examples/tree/jazzy/rclcpp/executors/cbg_executor 提供了这种机制的演示。

调度语义 

如果回调的处理时间比消息和事件发生的周期短,执行器基本上会按 FIFO 顺序处理它们。然而,如果某些回调的处理时间较长,消息和事件将会在堆栈的较低层排队。等待集机制仅向执行器报告有关这些队列的非常少的信息。具体来说,它只报告某个主题是否有消息。执行器使用此信息以轮询方式处理消息(包括服务和操作)——但不是按 FIFO 顺序。以下流程图可视化了这种调度语义。

5c0eeee346bd0858782a4a4f526ca582.png

这种语义首次在 Casini 等人在 ECRTS 2019 上的一篇论文 https://drops.dagstuhl.de/opus/volltexte/2019/10743/pdf/LIPIcs-ECRTS-2019-6.pdf 中描述。(注意:该论文还解释了计时器事件优先于所有其他消息。这种优先级在 Eloquent 中被移除 https://github.com/ros2/rclcpp/pull/841 。)

Outlook

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

  1. 复杂且混合的调度语义。理想情况下,您需要定义良好的调度语义以执行正式的时间分析。

  2. 回调可能会遭受优先级倒置。较高优先级的回调可能会被较低优先级的回调阻塞。

  3. 对回调执行顺序没有明确的控制。

  4. 没有内置控制以触发特定主题。

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

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

  • rclcpp WaitSet https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/include/rclcpp/wait_set.hpp : rclcpp 的 WaitSet 类允许直接等待订阅、定时器、服务服务器、操作服务器等,而不是使用执行器。它可用于实现确定性的用户定义处理序列,可能会一起处理来自不同订阅的多个消息。examples_rclcpp_wait_set 包 https://github.com/ros2/examples/tree/jazzy/rclcpp/wait_set 提供了几个使用此用户级等待集机制的示例。

  • rclc 执行器 https://github.com/ros2/rclc/blob/master/rclc/include/rclc/executor.h :这个来自 C 客户端库 rclc 的执行器,为 micro-ROS 开发,赋予用户对回调执行顺序的细粒度控制,并允许自定义触发条件来激活回调。此外,它还实现了逻辑执行时间(LET)语义的理念。

 更多信息 

  • Michael Pöhnl 等人:“ROS 2 执行器:如何使其高效、实时和确定性?” https://www.apex.ai/roscon-21 。ROS World 2021 研讨会。虚拟活动。2021 年 10 月 19 日。

  • 拉尔夫·兰格:“使用 ROS 2 的高级执行管理” https://www.youtube.com/watch?v=Sz-nllmtcc8&t=109s 。ROS 工业会议。虚拟活动。2020 年 12 月 16 日。

  • 丹尼尔·卡西尼,托比亚斯·布拉斯,英戈·吕特克博勒和比约恩·勃兰登堡:“基于预留调度的 ROS 2 处理链响应时间分析” https://drops.dagstuhl.de/opus/volltexte/2019/10743/pdf/LIPIcs-ECRTS-2019-6.pdf ,第 31 届 ECRTS 2019 会议论文集,德国斯图加特,2019 年 7 月。

52543bc55a311e4b4b8b3267b98ad208.png

15327b35e56d70b715765db98cddcd4e.png

e2cecb00d3ed3b05c6f6ae690330faa8.png

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一个关于ROS Noetic软件包依赖关系的问题。其中,下列软件包的依赖关系尚不足够满足要求,无法安装: ros-noetic-desktop-full: 依赖于 ros-noetic-desktop,但它不会被安装。 依赖于 ros-noetic-perception,但它不会被安装。 依赖于 ros-noetic-simulators,但它不会被安装。 依赖于 ros-noetic-urdf-sim-tu,但它不会被安装。 ### 回答2: 这个错误提示是说明在安装 ros-noetic-desktop-full 软件包时,发现它需要依赖一些其他的软件包,但是这些软件包未被安装。其中,ros-noetic-desktop、ros-noetic-perception、ros-noetic-simulators 和 ros-noetic-urdf-sim-tu 是四个未满足依赖关系的软件包。 这个错误提示一般是由于软件源的问题所导致的。在安装软件包时,系统会从软件源中查找该软件包以及它所需的依赖关系。如果软件源中不存在某个软件包的依赖关系,则会提示这个错误信息。 要解决这个问题,可以尝试以下几个方法: 1. 更新软件源:可通过修改软件源配置文件或使用软件源管理工具来更新软件源。更新后再次尝试安装软件包,看是否能够解决依赖关系问题。 2. 手动安装依赖关系:如果更新软件源后仍然无法解决依赖关系问题,可以尝试手动安装依赖关系。按照依赖关系的提示,逐个安装这四个软件包。安装完成后再次尝试安装 ros-noetic-desktop-full 软件包,看是否能够正常安装。 3. 使用 aptitude 命令安装:aptitude 命令可以自动处理依赖关系,可能会更好地解决这个问题。可以通过运行以下命令安装 ros-noetic-desktop-full 软件包: sudo aptitude install ros-noetic-desktop-full 以上是我的回答,希望能对你有所帮助。如果你还有其他问题,请随时回复。 ### 回答3: 这个问题意味着在安装 ros-noetic-desktop-full 软件包时,计算机无法满足所有需要的依赖关系。这些依赖关系包括 ros-noetic-desktop、ros-noetic-perception、ros-noetic-simulators 和 ros-noetic-urdf-sim-tu。 在解决这个问题之前,我们需要了解什么是依赖关系。在软件工程中,依赖关系指的是一个软件包需要另一个软件包才能正常运行的情况。例如,在 ROS 中,ros-noetic-desktop-full 需要依赖其他的软件包才能提供完整的功能。 为了解决这个问题,我们可以使用以下方法: 1. 更新软件包源列表。我们可以更新软件包源列表,这有助于计算机查找所需的软件包。在 Ubuntu 系统中,我们可以使用以下命令更新软件包源列表:sudo apt-get update。 2. 安装依赖关系。我们可以尝试单独安装缺失的依赖关系。在 ROS 中,我们可以使用以下命令安装缺失的软件包:sudo apt-get install ros-noetic-desktop ros-noetic-perception ros-noetic-simulators ros-noetic-urdf-sim-tu。 3. 检查软件包仓库。某些情况下,软件包源可能已经过时或不再受支持。我们可以检查软件包仓库,查看软件包是否可用。在 Ubuntu 系统中,我们可以使用以下命令查看软件包仓库:apt-cache search ros-noetic-desktop-full。 总之,无法满足依赖关系的问题是常见的,在解决这个问题之前,我们需要了解依赖关系的概念,并掌握一些解决方法。在 ROS 中,我们可以使用更新软件包源列表、安装依赖关系和检查软件包仓库等方法解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值