节点到参与者的映射¶
本文分析了在 ROS 节点与 DDS 参与者之间强制实现一对一映射的性能影响,并提出了替代的实现方法。
作者: Ivan Paunovic
翻译:Akana-kunama
写日期:2020-06
上次修改时间:2020 年 7 月
背景¶
节点¶
在 ROS 中,Node 是用于对其他实体进行分组的实体。 例如:发布者(Publishers)、订阅(Subscriptions)、服务器(Servers)、客户端(Clients)。 节点简化了组织和代码重用,因为它们可以以不同的方式组合。
域参与者¶
参与者是一种 DDS 实体。 参与者还对其他实体进行分组,如发布者、订阅者、数据写入者、数据读取者等。 创建更多 Participants 会增加应用程序的开销:
每个参与者都参与发现。 创建多个 Participant 通常会增加 CPU 使用率和网络 IO 负载。
每个参与者都会跟踪其他 DDS 实体。 在单个进程中使用多个可能会导致数据重复。
每个 Participant 都可以创建多个线程来处理事件、发现等。 每个参与者创建的线程数取决于 DDS 供应商(例如:RTI Connext)。
因此,参与者是一个较为“重量级”的实体。
注意:这实际上可能取决于 DDS 供应商,其中一些供应商在参与者之间共享这些资源。 但是,许多 DDS 供应商不执行此类优化,并且实际上建议每个进程只创建一个参与者。OpenSplice``RTI Connext``Fast-RTPS
上下文¶
在 ROS 中,Context 表示初始化-关闭周期的非全局状态。它还封装了节点和其他实体之间共享的状态。大多数应用程序中,一个进程通常只有一个 ROS Context。
Foxy 之前的行为¶
节点和 DDS 参与者之间存在一对一的映射。 这简化了最初的实现,因为 DDS 参与者提供了许多与 ROS 节点相当的功能。 这种方法的缺点是创建许多 Participant 会带来开销。 此外,DDS 域参与者的最大数量非常有限。 例如,在 RTI Connext 中,每个域限制为 120 个参与者。
建议的方案¶
此提案的目标是通过避免每个节点创建一个 Participant 来提高整体性能。 如果可能,将避免 API 更改。
DDS 参与者到 ROS 实体的映射¶
除了 Foxy 之前使用的一对一节点到参与者映射外,还有两种选择:
每个进程使用一个参与者。
每个 Context 使用一个参与者。
第二种方法更灵活,允许在单个应用程序中使用多个参与者,例如用于域桥应用程序。因此,选择了一对一的参与者与 Context 映射。
当多个节点在同一进程中运行时,可以通过多种方式对它们进行分组,例如为每个节点创建一个独立的 Context,将几个节点分组到同一个 Context 中,或对所有节点使用单一 Context。对于大多数应用程序,通常只创建一个 Context。
发现信息¶
如果未使用一对一的节点到参与者映射,则需要额外的发现信息才能将其他实体与节点匹配,例如发布者、订阅等。 可以使用多种方法来共享此信息。 建议的方法使用了一个主题。 每个 Participant 都会发布一条消息,其中包含将实体与 Node 匹配所需的所有信息。 消息结构如下:
参与者信息
GID: 全局唯一标识符
节点信息
Node 命名空间
节点名称
Reader gid
写入 GID
当一个实体更新时(例如:创建或销毁 Publisher),将发送一条新消息。
客户端和服务器根据其主题名称的 ROS 约定进行标识(请参阅主题和服务名称到 DDS 的映射)。
本主题被视为实现详细信息,并非所有实现都必须使用它。 因此,所有必要的 logic 都必须在 rmw implementation 本身或 upstream package 中。 实现此 logic 会使其成为 API 的一部分,而不是实现细节。rmw``rcl
为避免代码重复,rmw_dds_common 包提供了此逻辑的常见实现。
ROS 发现主题的详细信息¶
主题名称:
ros_discovery_infoWriter QoS:
耐久性:瞬态局部
历史记录:保留最后一条
历史深度: 1
可靠性: 可靠
Reader QoS:
耐久性:瞬态局部
历史记录:保留所有
历史深度: 1
可靠性: 可靠
其他影响¶
安全¶
以前,每个 Node 可以具有不同的安全构件。 这是可能的,因为每个 Node 都映射到一个 Participant。 新方法允许为每个进程指定不同的安全工件。 有关更多详细信息,请参阅 ROS 2 安全区域。
Ignore local publications 选项¶
在创建 Subscription 时,可以设置一个选项。 该选项可避免从同一 Node 内的发布者接收消息。 这并未在所有 rmw 实现中实现(例如:FastRTPS)。ignore_local_publications
在此更改之后,实现此功能将不那么直接。 需要添加一些额外的逻辑,以便识别从哪个节点创建 Publisher。
替代实现¶
使用键控主题¶
可以使用键控主题,将参与者的 gid 作为键值。这将允许主题的读取端使用“保留最后一条,深度为 1”的历史记录策略。
使用参与者/写入者/读取者的 userData QoS¶
与其使用自定义主题共享 ROS 特定的发现信息,还可以使用 Participant、Reader 和 Writer 的 userData QoS 组合:
Participant
userData: 由参与者创建的节点列表(当节点被创建或销毁时更新)。Reader
userData: 节点名称/命名空间。Writer
userData: 节点名称/命名空间。
这些信息足以满足 ROS 的图形 API 要求。这种机制还可以避免所有节点必须读写同一主题,从安全角度看这是更好的选择。
未采用该替代方案的原因是目前某些支持的 DDS 供应商尚未完全支持这一机制。
后续工作¶
该优化已应用于 rmw_cyclonedds、rmw_fastrtps_cpp 和 rmw_fastrtps_dynamic_cpp。其他基于 DDS 的实现(例如 rmw_connext_cpp)也可以采用相同的方法。