ROS2 launch design doc

ROS 2 Launch 系统

本文整理并翻译自:ros2 launch system

roslaunch:这里特指ROS 1中的launch 系统。

前言

ROS 2中的 launch 系统负责帮助用户描述其系统的配置,然后按说明执行它。 系统的配置包括运行哪些程序,在何处运行它们,传递它们的参数以及ROS特定约定,这些约定通过为每个组件赋予不同的配置,可以轻松地在整个系统中重用组件。 另外,由于 launch 系统是负责执行用户进程的进程(或进程集),因此它负责监视由它启动的用户进程的状态,并报告并且响应这些进程的状态变化。

ROS 2 的launch系统作为ROS 1中roslaunch的继承者,我们在这里先对ROS 1的launch系统的知识进行一些补充和梳理。通过他们之间的相同点与不同点,深入了解ROS2 launch的作用与特点。

ROS 1中的 roslaunch

wiki中的描述如下:

roslaunch is a tool for easily launching multiple ROS nodes locally and remotely via SSH, as well as setting parameters on the Parameter Server. It includes options to automatically respawn processes that have already died. roslaunch takes in one or more XML configuration files (with the .launch extension) that specify the parameters to set and nodes to launch, as well as the machines that they should be run on.

我们可以总结出roslaunch在 ROS 1 中的主要作用:

  1. 启动节点(集);
  2. 通过SSH协议远程启动节点(集);
  3. 在parameter server上设定parameter;
  4. 自动重启那些死掉的进程;
  5. 节点描述、节点参数以及运行的位置都是基于静态和XML的。

在ROS 2的wiki中,对launch系统有更详细的解释:

roslaunch was designed to fit the ROS architecture of complexity via composition: write a simple system first, then combine it with other simple systems to make more complex systems. In roslaunch, this is expressed through several mechanisms:

roslaunch旨在通过组合来适配复杂的ROS架构:通过一些简单的系统组合成一个复杂的系统,具体来说,在roslaunch,有以下几种机制实现这一目的:

<include>s: you can easily include other .launch files and also assign them a namespace so that their names do not confict with yours.

通过<include>s我们可以使用其他的.launch文件,同时支持namespace,来避免命名的冲突。

''s: you can group together a collection of nodes to give them the same name remappings.

可以将nodes组合到一个集合里,可以给这些节点重映射一个相同的名字(多节点之间组成的构件应该就是这样处理的)。

aliased''s: you can separate machine definitions and node definitions into separate .launch files and use aliases to define which machines get used at runtime. This allows you to reuse the same node definitions for multiple robots. For example, instead of saying that a laser_assembler runs on ‘foo.willowgarage.com’, you can say that it runs on the ‘tilt-laser’ machine. The machine definitions then take care of which host is the ‘tilt-laser’ machine.

我们推荐将机器和节点的.launch文件分开定义,并且可以使用别名来定义在运行时使用哪些机器。这样的好处是可以将相同的节点定义应用到不同的机器人上(注:使用别名可以使launch文件的可读性更好),例如我们可以说一个laser_assembler运行在“tilt-laser”主机上,不用说这个主机的全称,不妨是”foo.willowgarage.com“,而主机名解析的任务就交给machine launch文件去处理。

roslaunch also contains a variety of tools to help you write your .launch files as portably as possible. You can use the tag to specify environment variables that need to be set for a particular machine or node. The $(find pkg) syntax let you specify file paths relative to a ROS package, instead of specifying their location on a particular machine. You can also use the $(env ENVIRONMENT_VARIABLE) syntax within include tags to load in .launch files based on environment variables (e.g. MACHINE_NAME).

roslaunch还包含许多工具,可以帮助用户轻松地编写可移植的launch文件,可以使用<env>tag来指定针对特定计算机或者节点设置的环境变量,使用$(find pkg)语法指定相对于ROS package的相对文件路径,而不必指定他们在特定机器上的绝对路径,还可以将$(env ENVIRONMENT_VARIABLE)语法和<include>tag一起使用,实现根据环境变量加载launch文件。

我们可以看出,ROS 1中 roslaunch的设计目的和特点如下:

  1. 由简单的系统组成复杂的系统,降低管理的复杂度;
  2. 使用<include>重用片段,而不必在新launch中重写每个片段(fragment)
  3. 使用<group>将设定(例如remapping)应用到nodes/processes/(included launch files)
  4. 使用带有namespace的group来形成层次结构
  5. 使用抽象操作系统概念(例如环境变量)来实现高可移植性
  6. 用重定位和可移植的方式在文件系统上定位文件。(例:使用 $(find <package_name>)

至此我们对ROS1 中的roslaunch有了一个整体的认识。下一节中,我们将重点介绍ROS2中launch与ROS1中的不同以及如何在ROS2 launch系统上改进ROS1。

Differences in ROS 2

ROS2中的launch系统的目标之一就是模拟ROS1中launch的功能,但因为ROS2中的体系结构有所更改,因此我们需要修改一些功能、目标以及术语:

Nodes 与 Processes 之间的关系

在ROS1中,每个进程只能对应一个节点,因此ROS1的roslaunch中process和node可以看做是可以互相替换使用的。

即使使用ROS1中的“nodelet”功能来模拟一个进程具有多个节点,从节点或者nodelet向进程的概念上的映射也会被代理进程保留。举例来说,你可以run一个“NodeletManager”进程(注:这个进程应该指的就是后文说的代理进程proxy process),然后为每一个想在这个manager中运行的nodelet建立一个进程。roslaunch可以检测退出的nodelet,并且响应发给代理进程的信号。

由于在ROS2 中每个进程可以对应多个节点,因此不再需要合并节点或者进程,因此在谈到节点和进程时,需要更清楚地了解ROS2中启动系统的设计以及文档。此外,由launch系统传递给节点的配置(参数,remapping等)需要进行调整,虽然该部分与用于静态remapping和参数的设计文档重合。

此外,由于每个进程可以对应多个节点,因此关闭节点并不像ROS1中一样意味着发送一个unix信号给那个节点对应的进程。在多节点进程中,需要其他机制进行更精细的关闭控制。

远程启动 Nodes(Processes)和可移植性

ROS1 中的launch系统仅在linux和其他的类unix系统得到支持,这些机器都有SSH协议,roslaunch通过SSH进行远程启动。It also played a role in defining what you specified and how when configuring roslaunch from ROS 1 to be able to launch processes on remote machines.

在ROS 2中,Windows已添加到目标平台列表中,并且在撰写本文档时,它本身并不支持SSH。因此,除非对launch系统进行更改,否则需要一种不同的,可移植性更好的机制来在任何地方支持此功能。至少,在ROS2中,我们需要为Windows提供一个使用launch系统解决方案。

参数

在ROS 1中,有一个全局参数服务器,其中存储了所有参数,节点将通过该服务器获取并设置所有参数。该服务器已集成到roslaunch中,并且还被ROS 1的其他类型的参数使用,这些参数称为“动态重配置参数”(dynamic reconfigure parameters)。

在ROS 2中,只有一种参数,它们的工作方式不同。一般而言,它们的工作方式更像ROS 1中的“动态重配置参数”,因为它们是特定于节点的(ROS2中没有真正的全局参数),并且由节点管理(节点可以拒绝更改,并且只有在节点运行的时候才可以获取或者修改参数)。

ROS 2中仍然可以(可能还会)有一个“全局参数服务器”,但是它将被简单地实现为一个接受所有更改的节点,并且可以与launch系统一起自动运行,或者可以由用户显式调用(来自ROS 1的 roscore),但基本功能不需要它。

参数工作方式上的根本区别将影响ROS 2中启动系统的体系结构以及用户如何通过启动系统为节点指定参数。

在ROS 1的roslaunch中,只有几种方法可以对系统的变化做出反应,并且它们都与进程“死亡”(干净的exit或不干净的exit)有关:

  1. 如果这个进程挂了,那么重新生成一个进程
  2. 如果必需的进程挂了,那么关闭整个launch系统

ROS 2中的launch系统有望丰富那些ROS 1 roslaunch提供的功能,它不仅可以提供对exit过程的这些常见反应,还可以提供有关进程(和其他事件)退出的更详细的信息,并允许用户指定对这些事件类型的任意响应。

Deterministic Startup

roslaunch的wiki中提到:

roslaunch does not guarantee any particular order to the startup of nodes – although this is a frequently requested feature, it is not one that has any particular meaning in the ROS architecture as there is no way to tell when a node is initialized.

roslaunch并不保证节点启动的顺序,因为在ROS1架构中,无法知道什么时候初始化节点。

这是另一个ROS2 launch可以改进的部分,对于那些有生命周期的节点(也叫被管理节点,Managed Nodes),ROS 1并不能对何时启动节点加以约束。

为了解决这一问题,ROS2的launch系统需要对进程和节点之间的依赖关系以及依赖关系之间的约束进行建模,例如,用户表示一个图像处理节点依赖于相机驱动节点,并且在相机驱动节点active之前,图像处理节点不能被启动(约束),这些约束用户可以在自由定义,也可以由launch 系统直接对常见约束进行建模。

此外,这些约束条件并不止针对ROS中的时间,例如,用户可能表示一个进程运行了10秒钟之后就开始另一个进程。ROS2 的launch系统可以选择让用户定义一个满足该约束的谓词(predicate),或者提供一个launch中实现的通用约束,例如“经过N秒后启动另一个进程”。

ROS2 launch系统支持导出、整理并导出或者响应managed nodes的lifecycles事件。例如,我们可以假设当一个类似总控节点处于一个我们定义的“最终”的状态时,launch系统关闭。我们说这样的节点是“required”的,上述过程类似于ROS 1的roslaunch中的required=true 设定。

Static Description and Programmatic API

ROS1中的roslaunch多被用户用来定义一些节点的静态描述XML以及设定一些运行时的参数。ROS1中有一个roslaunchapi,但是使用的并不广泛,大家更习惯于这两种配置方式:

  1. 使用XML预处理程序(例如xacro或其他通用模板系统)进行预处理
  2. 在roslaunch中使用更复杂的表达式作为XML的tags

那么为什么roslaunch是一个静态的XML而不是一个动态的脚本呢?一个直接的答案是:“可以,但没必要”,以编程的方式(动态脚本)使用API总归难以使用。

脚本式launch和静态声明式的launch文件各有利弊,将在后文详述。ROS 1中更偏好静态的launch配置文件,而在ROS 2中,既支持静态的launch文件,也支持动态的脚本编写。因此编写脚本是一直支持的。

Locating Files

通常,在向启动系统描述系统时需要表达文件的位置,无论它是要运行的可执行文件,要作为参数传递的文件还是要从中加载参数的文件。ROS1和ROS2在这里的不同之处在于怎样找到package,以及一个package可以结合多少个文件夹,因此我们需要有获取相对路径的语法。

Similarities with ROS 1

上一节中主要讨论了ROS2 launch与ROS1 roslaunch不同的地方,本节主要讨论他们之间的相同点:

  1. 将常见的ROS概念(remapping和namespace更改)转换为带有参数的命令,简化用户的使用难度;
  2. 通过launch文件管理系统复杂度;
  3. 允许include其他launch文件;
  4. 使用group将配置应用于节点组或者进程组;
  5. 提供操作系统可移植性。

以及其他。

launch系统模块分解

根据侧重点不同,launch系统可以大致分为以下几点:

  1. 进程与各种节点之间的调用约定;
  2. 事件报告系统;
  3. 系统描述与静态分析;
  4. 系统描述的执行和验证;
  5. 测试

接下来各章节的目的是列举launch系统可以执行的操作以及可以与之交互的事物。

Calling Conventions

为了使 launch 系统执行所描述的系统,它需要了解如何实现该描述。在ROS2 launch的语境下,“Calling Conventions”旨在描述 launch 系统与正在执行和监视的任何事物之间的“interface”或“contract”。contract 涵盖初始执行、运行时的动作、信号处理和launch系统的行为以及关闭。

OS processes

(本节主要涉及OS进程、信号之类的一些基础知识,暂时不做解读)

(Plain) ROS Nodes

只要在其中包含至少一个Node,任何操作系统进程都可以成为ROS特有的进程。注意,ROS并未提供一个标准化方法来抽象Node与操作系统进程之间的通信,但是,它确实在执行过程中添加了一些特定种类的输入,并且还可能影响进程对信号的反应。

以上适用于“普通” ROS节点(plain ROS Nodes),而且launch 系统可以在 Managed ROS Nodes(注:具有生命周期)中使用更多功能,这将在下一部分中进行介绍。

Execution

包含ROS节点的进程可能还需要考虑其他元素,例如:

  1. ros2 run 的参数:<package_name> <executable_name>,而不是<package_name> PATH
  2. ROS中特有的环境变量
  3. ROS特定的命令行参数
  4. 单节点进程与多节点进程的差异
  5. 如何修改节点名称或者namespace
  6. remapping topic、services、actions、parameters...
  7. 初始参数值

ROS特定的构造可以被扩展为命令行参数或者是环境变量。因此,launch系统能够采用ROS特定的声明,例如“remap ‘image’ 为 ‘left/image’ ”。并且将这些声明隐式转换为操作系统可以使用的命令行参数或者环境变量,例如把image:=left/image加入命令行参数。但是ROS的特定声明转换为什么取决于在过程中如何使用节点,之后的章节会详细阐述相关的内容。

Runtime

在运行时,一个plain node不会暴露操作系统进程所没有的任何新内容。plain node确实有topic、service以及parameter这些机制的支持,但是目前尚无标准化的方法可以用于launch。

而且也不会对stdin做出反应,但是包含节点的进程确实倾向于为SIGINT提供一个信号处理程序,这个程序可以更加“正常”的关闭,但是这并不是强制的。如果这些节点使用rclcpp中的一类rclcpp::spin()函数或者轮询rclcpp::ok(),那么发送SIGINT信号通常会导致大多数节点关闭,这也是推荐的做法。

Termination

ROS Node的终止在操作系统层面仅表现为process的终止,即从操作系统层面只能观察到进程的终止,并不能观察到ROS Node的具体信息。

Managed ROS Nodes

对于具有生命周期的ROS节点,每个节点将具有附加的运行状态,launch系统可以访问或者直接使用这些运行状态,将其传递给event系统,或者在将其传递给event系统之前进行汇总。

Managed ROS Nodes建立在之前的实体上,继承了plain ROS 节点以及操作系统进程的执行特征、运行时特征以及终止特征。

Execution

与plain ROS Nodes相比,Managed ROS Nodes 在执行时并未表现出任何其他特有的信息,至少目前为止是这样,将来有可能会在此基础上进行更改。

Runtime

在运行期间,Managed ROS Nodes会在状态修改的任何时间发出事件,载体至少是一个topic,但是也很有可能被其他方式捕获、整合或者通信。这些状态上的更改可能被launch系统直接使用,或者被用户使用,它们都可以对这些更改做出反应。

用户可以通过Managed ROS Nodes表示许多场景,比如当Node A进入active状态时启动Node B和Node C,以及当Node A返回一个状态码退出或者进入“Finalized”状态时,关闭全部节点等等。

Termination

Managed ROS Nodes 在终止时表现出一些可以被观察到的信息(注:这里指Node,对于包含这个Node的进程,对于OS来说并不表现出任何与其它进程不同的信息)。在Managed ROS Node进入Finalized状态前,会经过一个ShuttingDown过渡状态。这种状态之间的转换对于lifecycle event system来说是可见的,信息载体是一个名为lifecycle_state的topic(topic名称可能会发生变化,具体可以参考managed ROS nodes的文档)。

在本章节中,并不会探究Managed ROS Nodes状态转换的具体机制,至少目前我们只需要知道,这些状态转换的工作是由Node自身进行的,或者是通过launch系统发送状态转换请求以及一些特定的信号来处理的。

单节点进程 (Process with a Single Node)

在本节以及Calling Conventions一节中将解释进程与节点之间的不同组合。任何节点都是plain ROS Nodes或者Managed ROS Nodes,我们将详细讨论怎样传递ROS的一些特定设置(options)。

首先是一个进程对应一个节点,这种方式在ROS1中十分常见,而在ROS2中,由于一个进程可以对应多个节点,所以这种方式并不常见,但是在一些快速开发的脚本、驱动程序或者GUI工具这类需要控制主线程的进程中,还是在大量使用单节点进程。

并不需要指明命令行参数具体应用于哪个节点(因为只有一个节点),例如,我们可以使用命令行参数__ns表示更改该节点的namespace。

即使一个进程中只有一个节点,这个节点的启动与停止跟它寄生的进程并不是绑定的(注:寄生一词是笔者的个人理解,原文中并未体现),即进程运行时节点不必启动,节点退出时进程无需终止。如果是一个Managed ROS Node,该节点的lifecycle可以通过lifecycle events跟踪,总之,一个单节点进程可以启动一个节点、支持节点运行、终止一个节点并且重新创建它。

所以单节点进程最大的作用是可以简化命令行参数和环境变量方面的配置(注:个人理解“简化”体现在并不需要指定某个特殊的节点)。

多节点进程 (Process with Multiple Nodes)

多节点进程与单节点进程类似,主要的区别在于在配置(命令行参数以及环境变量)的指定需要具体到某个或某几个节点。remapping文档详细介绍了如何使用命令行参数有选择的配置多个节点。

假设有两个相机驱动节点“camera1”和“camera2”,我们可以使用camera1:__ns:=leftcamera2:__ns:=right等方式配置(注:重命名)它们的namespace。

动态加载Nodes (Dynamically loading Nodes)

动态加载节点表示在该节点要求加载之前,容器进程并不知道该节点的存在。容器进程(Container process)是一个独立的可执行文件,可在其内部加载和执行节点(注:我们之前谈到的进程都是容器进程)。

容器进程API

注:怎样启动一个容器进程并加载节点。

虽然有标准的容器进程,但是自定义容器进程也允许使用自定义执行程序或者客户端库。因此对于launch系统,需要有一个容器进程API,来传递需要加载哪些节点。

launch系统需要告知容器进程将哪些参数提供给动态加载的节点,包括命令行参数以及客户端库的特定选项(例如rclcpp的use_intra_process_comms)。因为launch系统不知道全部的自定义容器进程,这个API必须要包含一种传递未知参数的方式。

该 API 不会对于每个已加载的节点设置环境变量。许多语言都有用于获取环境变量的API,并且无法在进程中隔离它们。

目前考虑了以下的API选项(主要从加载、状态反馈、卸载三个方面讨论):

  • 使用命令行参数的API:容器进程API通过命令行参数传递一个配置文件来加载节点,这种API无需等待进程发现节点,因此启动延迟很低,但是无法获取已加载节点成功或失败的状态,也无法告知容器进程去卸载一个组合结点。

  • 使用stdin的API:启动延迟低,但是也无法获取加载成功或者失败的反馈,无法使用stdout,因为将可组合结点的信息记录到stdout是一个常见但是会引发冲突的选择(注:个人理解如果其他系统进程也使用stdout可能会产生争用或者冲突)。因为stdin一直可用,所以可以通过stdin卸载节点。

  • 使用topic、service的API:在我们的讨论中,这是唯一一种可以获知反馈状态的选择。这也是支持自检的唯一选项,但是从容器进程生成到节点加载成功的潜在延迟也是最高的。

launch系统中提出的容器进程API
  • 命令行参数:容器进程接受命令行参数,包括日志级别、是否remapping以及参数。这些命令行参数不能应用于动态加载的节点。launch系统会给这些容器进程传递参数,和给节点传递参数类似,如果在launch中使用了remapping,那么launch系统会尝试使用remapping的service name。

  • ROS service:一个容器进程提供三种service:~/_container/load_node~/_container/unload_node以及~/_container/list_nodes。这些服务内置在ROS的命名空间内,以防与用户定义的service name冲突,根据名称即可知道作用,需要注意的是,list_nodes不会被launch系统调用,这个service仅仅用于自检。特别指出,在load_node时,通过节点全称加载一个已存在的节点是不可行的,这是为了防止节点名称唯一的功能(例如parameters)发生冲突。容器进程在加载时必须分配一个unique id,已加载的节点id是不可变的。同一个容器进程的两个节点一定不可以有相同的ID,而且在重用id之前需要有相当长的时间延迟。

  • 退出码:如果正常的Termination要求关闭容器进程,那么退出码必须为0,如果是异常退出,退出码必不为0。

节点的并行加载与顺序加载 (Parallel vs Sequential Loading of Nodes)

如果可以并行加载多个节点,则需要确定如何加载节点。容器进程应在要求时立即加载节点。应该由launch系统决定是并行还是顺序加载节点。如果要启动多个相同类型的节点,则launch系统应顺序加载这些节点,以便每个节点都可以在加载下一个节点之前重新映射其名称。如果它们是不同类型的,那么launch系统可以选择尝试并行加载它们,其中它们加载的确切顺序由偶然性或容器进程确定。

可组合节点的注册 (Registration of Composable Nodes)

怎样组合结点我们在这里不讨论,容器进程负责找到要求加载的节点,例如,容器进程可以将pluginlib用于rclcpp节点。

事件系统 (Event Subsystem)

本文的这一部分介绍了launch系统中的事件子系统,该子系统负责生成事件并将其报告给用户和自己,并处理这些事件。

按照来源划分事件 (Categories of Events by Source)

由launch系统事件子系统产生的事件可以大致分为以下两类:只有launch系统才可以观察到的事件,还有就是其他系统也可以观察到的事件。因此,如果我们希望其他应用程序或用户使用只有launch系统可以观察到的事件,则必须通过事件系统公开这些事件。

还有一种分类方式是根据事件的来源:

  1. launch 系统事件:事件系统中最基本的事件来自launch系统的内部。一个比较常见的例子是定时事件,或者是经过一段时间再发生这种事件,或者可以使用“空闲”事件来实现“稍后调用”类型的回调。然而有些事件是launch系统特有的,比如一个launch description included时,或者是launch系统需要关闭时。这些事件可能包含了一些潜在的信息像是一个launch description被include的原因。例如,给定launch系统一个参数,当它被另一个launch file include时或者被一个异步请求要求include时都会发出事件;或者当一个关闭事件发出时,很可能会携带launch system需要关闭的原因,例如进程退出、或者受到了一个SIGINT信号等。

  2. 操作系统事件:有些事件对于任何由launch系统操作的进程来说都是可见的,比如进程的启动和退出。

  3. ROS 特定事件:ROS特定的事件一般发生在launch运行的进程中,而且使用ROS topic 或者是 service 来启动都可以在launch事件系统中观察到这些事件并且生成相等的事件。例如,如果一个正在运行的进程对应了一个managed ROS Node,launch系统将会观察到这个节点的状态转换并且在状态转换发生时会创建相应的事件。用户可以通过这种机制等待一个节点到达active状态后再开始一个新的进程。

汇报并处理事件

本节中不深入实现细节,我们将试着表示事件中应包含哪些信息、如何访问以及事件系统在传递方面的行为。

事件提供的信息

event应该不仅表示这个事件发生了,还应该能传递与事件有关的数据。(注:event表示ROS launch system中的一个事件子系统使用的数据结构。)

“call later”事件就是一个典型的不带有附加信息的简单事件,它不关心谁初始化了它,也不关心他已经发生了多久,如果这些信息是必要的,也可以包括进去,因为这个事件本身就足以支持等待动作发生。

一个“process exit”事件就是一个典型的带有附加信息的事件,要包含足够的信息去识别哪个进程挂掉了以及包含进程的返回码。

同时,事件应该有一个类型(可以作为属性也可以作为一个子类),类型可以被用来过滤特定的事件给相应的事件处理程序。

事件处理程序 (Event Handlers)

一个事件通过在launch 系统中注册事件处理函数的方式进行处理(注:类似于操作系统中的信号处理函数)。一个事件处理函数只需要一个函数,在launch系统中本地注册。

其他种类的事件处理函数可以通过构建本地定义函数的方式使用,可以是类似于用户在launch文件中定义的lambda表达式,或者是一个内建的函数,这个函数发布事件作为ROS的消息。在后一种情况下,事件处理程序可以是对某个topic的订阅(无需在launch系统上提前注册),也可以是一个service的server(需要在launch系统上提前注册)。

因此,如果是一个topic的订阅,那么可以将带有回调的订阅对象视为一个事件处理程序。如果是一个被launch系统调用并由用户定义的service server,这个server和它的response可以被看做事件处理程序。

处理事件 (Handling Events)

默认情况下,事件会传递给所有的事件处理程序,并且事件处理程序无法接受一个事件,以防这个事件被传递给其他的事件。(注:一个事件处理程序无法独占一个事件)

虽然事件处理程序中不存在比较运算符(所以不能排序),但是事件的传递顺序应该是确定的并且应该是与注册顺序相反,即,先注册后传递(first registered last delivered),注意对异步事件处理程序的事件传递也需要有序发送,但是并不需要按序接收。

事件过滤器 (Event Filters)

就像Qt的事件系统,可以通过创建事件过滤器来模拟接受事件并且阻止其向下传递。

与Qt事件系统不同,事件过滤器就像其他任何事件处理程序一样,不会阻止其他事件处理程序接收事件。 相反,每个事件过滤器将具有自己的事件处理程序列表,每个事件处理程序都可以接受或拒绝事件,从而分别允许或拒绝在事件过滤器中对该事件进行进一步处理。

可以将任何事件处理程序添加到事件过滤器,但是“纯事件接收器”无法接受事件,例如像ROS topic之类的东西。这是因为没有反馈机制,即对 topic 的订阅无法指示事件是否已被接受或拒绝,因为它没有返回类型。而作为launch系统本身或ROS服务调用附带的函数或lambda表达式的其他事件处理程序可以具有返回类型,因此可以接受或拒绝事件。

发送事件

除了 launch 系统本身可以发送事件之外,启动系统的用户还应该可以发送事件。

系统描述 (System Descriptions)

系统描述是一组声明性的动作和反应,以 launch 系统可以解释的格式来描述用户想要启动的内容。

系统描述的目的是捕获用户描述要启动的系统的意图,同时尽可能减少副作用。这样做的原因是,launch description可以在无需实际启动所描述的系统,直接对系统进行可视化并进行静态分析。一个让开发人员在所见即所得的编辑器中可视化和修改launch description的工具,对于系统描述来说是一个重要的用例。

首先,本节将以编程语言或文本标记的方式描述系统描述中可以表达的内容,以及它如何映射到前面部分中描述的调用约定和事件处理,以及它如何映射到launch系统的具体行为。之后,它将建议如何将这种系统描述应用于Python和XML,以及如何将其扩展以支持其他语言和文本标记。

启动描述 (Launch Descriptions)

系统各个部分的描述,我们在这里将其称为“Launch Descriptions”。“Launch descriptions”由action和action set的有序列表组成。它也可能包含整个description中的替代(注:某些属性的多种选择),这些替代用于以结构化的方式为description添加一些灵活性和配置。

Actions

(待续)

原文地址:https://www.cnblogs.com/LuoboLiam/p/14578439.html