使用 Fast DDS Discovery Server 作为发现协议 [社区贡献]

目标:本教程将展示如何使用 Fast DDS Discovery Server 发现协议启动 ROS 2 节点。

教程级别:高级

时间:20 分钟

背景

从 ROS 2 Eloquent Elusor 开始,Fast DDS Discovery Server 协议是一种提供集中式动态发现机制的功能,而不是 DDS 中默认使用的分布式机制。

本教程介绍如何使用 Fast DDS Discovery Server 功能作为发现通信来运行一些 ROS 2 示例。

为了获取有关可用发现配置的更多信息,请查看`以下文档 <https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/discovery/discovery.html>`_ 或阅读`Fast DDS Discovery Server 特定文档 <https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/discovery/discovery_server.html#discovery-server>`__。

简单发现协议DDS 标准 中定义的标准协议。 但是,在某些情况下,它存在已知缺点。

  • 它无法有效**扩展**,因为随着新节点的添加,交换的数据包数量会显著增加。

  • 它需要**多播**功能,但在某些情况下可能无法可靠地工作,例如 WiFi。

快速 DDS 发现服务器 提供客户端-服务器架构,允许节点使用中间服务器相互连接。 每个节点都充当*发现客户端*,与一个或多个*发现服务器*共享其信息并从中接收发现信息。 这减少了与发现相关的网络流量,并且不需要多播功能。

../../../_images/ds_explanation.svg

这些发现服务器可以是独立的、重复的或相互连接的,以便在网络上创建冗余并避免出现单点故障。

Fast DDS Discovery Server v2

最新的 ROS 2 Foxy Fitzroy 版本(2020 年 12 月)包含一个新版本,即 Fast DDS Discovery Server 的第 2 版。 此版本包含一项新的过滤功能,可进一步减少发送的发现消息数量。 此版本使用不同节点的主题来决定两个节点是否希望通信,或者是否可以保持不匹配(即不发现彼此)。 下图显示了发现消息的减少:

../../../_images/ds1vs2.svg

这种架构大大减少了服务器和客户端之间发送的消息数量。 下图显示了`RMF Clinic 演示 <https://github.com/open-rmf/rmf_demos#Clinic-World>`__ 发现阶段网络流量的减少情况:

../../../_images/discovery_server_v2_performance.svg

为了使用此功能,可以使用“参与者的 XML 配置 <https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/discovery/discovery_server.html#discovery-server>”来配置发现服务器。 还可以使用“fastdds”工具 <https://fast-dds.docs.eprosima.com/en/v2.1.0/fastddscli/cli/cli.html#discovery>”和“环境变量 <https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/env_vars/env_vars.html>”来配置发现服务器,这是本教程中使用的方法。 有关发现服务器配置的更详细说明,请访问`Fast DDS 发现服务器文档 <https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/discovery/discovery_server.html#discovery-server>`__。

先决条件

本教程假设您拥有 ROS 2 Foxy(或更新版本):doc:安装

如果您的安装使用的 ROS 2 版本低于 Foxy,则无法使用``fastdds`` 工具。 因此,为了使用发现服务器,您可以更新存储库以使用不同的 Fast DDS 版本,或者使用 Fast DDS XML QoS 配置 配置发现服务器。

运行此教程

talker-listener ROS 2 演示创建了一个每秒发布“hello world”消息的``talker``节点,以及一个监听这些消息的``listener``节点。

通过 sourcing ROS 2,您将可以访问 CLI 工具``fastdds``。

此工具允许访问``发现工具<https://fast-dds.docs.eprosima.com/en/v2.1.0/fastddscli/cli/cli.html#discovery>`__,可用于启动发现服务器。此服务器将管理与其连接的节点的发现过程。

Important

Do not forget to source ROS 2 in every new terminal opened.

设置发现服务器

首先启动 ID 为 0、端口为 11811(默认端口)的发现服务器并监听所有可用接口。

打开一个新终端并运行:

fastdds discovery --server-id 0

启动监听器节点

执行监听器演示,监听 /chatter 主题。

在新终端中,将环境变量 ROS_DISCOVERY_SERVER 设置为发现服务器的位置。 (不要忘记在每个新终端中获取 ROS 2 的源代码)

export ROS_DISCOVERY_SERVER=127.0.0.1:11811

启动侦听器节点。使用参数“–remap __node:=listener_discovery_server”更改本教程中的节点名称。

ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener_discovery_server

这将创建一个 ROS 2 节点,该节点将自动为发现服务器创建一个客户端并连接到之前创建的服务器以执行发现,而不是使用多播。

启动 talker 节点

打开一个新终端并像以前一样设置“ROS_DISCOVERY_SERVER”环境变量,以便节点启动发现客户端。

export ROS_DISCOVERY_SERVER=127.0.0.1:11811
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker_discovery_server

现在您应该看到 talker 发布“hello world”消息,并且 listener 接收这些消息。

演示 Discovery Server 执行

到目前为止,没有证据表明此示例和标准 talker-listener 示例的运行方式不同。 为了清楚地演示这一点,请运行另一个未连接到 Discovery Server 的节点。 在新的终端中运行新的 listener(默认情况下在 /chatter 主题中监听),并检查它是否未连接到已在运行的 talker。

ros2 run demo_nodes_cpp listener --ros-args --remap __node:=simple_listener

新的侦听器节点不应接收“hello world”消息。

为了最终验证一切是否正常运行,可以使用简单发现协议(默认的 DDS 分布式发现机制)创建一个新的 talker 进行发现。

ros2 run demo_nodes_cpp talker --ros-args --remap __node:=simple_talker

现在您应该看到 simple_listener 节点从 simple_talker 接收“hello world”消息,但没有从 talker_discovery_server 接收其他消息。

可视化工具 rqt_graph

rqt_graph 工具可用于验证此示例的节点和结构。

请记住,为了将 rqt_graph 与发现服务器协议一起使用(即查看 listener_discovery_servertalker_discovery_server 节点),必须在启动之前设置 ROS_DISCOVERY_SERVER 环境变量。

高级用例

以下部分展示了发现服务器的不同功能,这些功能可让您通过网络构建强大的发现服务器。

服务器冗余

通过使用 fastdds 工具,可以创建多个发现服务器。 发现客户端(ROS 节点)可以连接到任意数量的服务器。 这样就可以拥有一个冗余网络,即使某些服务器或节点意外关闭,该网络仍可正常工作。 下图显示了一个提供服务器冗余的简单架构。

../../../_images/ds_redundancy_example.svg

在多个终端中,运行以下代码与冗余服务器建立通信。

fastdds discovery --server-id 0 --ip-address 127.0.0.1 --port 11811
fastdds discovery --server-id 1 --ip-address 127.0.0.1 --port 11888

--server-id N 表示 id 为 N 的服务器。当使用 ROS_DISCOVERY_SERVER 引用服务器时,服务器 0 必须排在第一位,服务器 1 排在第二位。

export ROS_DISCOVERY_SERVER="127.0.0.1:11811;127.0.0.1:11888"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker
export ROS_DISCOVERY_SERVER="127.0.0.1:11811;127.0.0.1:11888"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener

现在,如果其中一个服务器发生故障,仍然有可用的发现功能,节点仍然可以发现彼此。

备份服务器

Fast DDS 发现服务器允许创建具有备份功能的服务器。 这样,服务器就可以在关闭时恢复其保存的最后状态。

../../../_images/ds_backup_example.svg

在不同的终端运行以下代码,与备份服务器建立通信。

fastdds discovery --server-id 0 --ip-address 127.0.0.1 --port 11811 --backup
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener

在发现服务器的工作目录(启动它的目录)中创建了几个备份文件。 两个 SQLite 文件和两个 json 文件包含启动新服务器和在发生故障时恢复故障服务器状态所需的信息,从而避免了再次发生发现过程的需要,并且不会丢失信息。

发现分区

可以拆分与发现服务器的通信以在发现信息中创建虚拟分区。 这意味着两个端点只有在它们之间存在共享发现服务器或发现服务器网络时才会相互了解。 我们将执行一个包含两个独立服务器的示例。 下图显示了架构。

../../../_images/ds_partition_example.svg

使用此模式,“侦听器 1”将连接到“讲话者 1”和“讲话者 2”,因为它们共享“服务器 1”。 “侦听器 2”将与“讲话者 1”连接,因为它们共享“服务器 2”。 但“侦听器 2”不会听到来自“讲话者 2”的消息,因为它们不共享任何发现服务器,包括通过冗余发现服务器之间的连接间接共享。 运行第一个在本地主机上侦听的服务器,默认端口为 11811。

fastdds discovery --server-id 0 --ip-address 127.0.0.1 --port 11811

在另一个终端中运行第二个服务器,使用另一个端口(在本例中为端口 11888)监听本地主机。

fastdds discovery --server-id 1 --ip-address 127.0.0.1 --port 11888

现在,在不同的终端中运行每个节点。使用``ROS_DISCOVERY_SERVER``环境变量来决定它们连接到哪个服务器。请注意,`ids 必须与<https://fast-dds.docs.eprosima.com/en/v2.1.0/fastdds/env_vars/env_vars.html>`__匹配。

export ROS_DISCOVERY_SERVER="127.0.0.1:11811;127.0.0.1:11888"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker_1
export ROS_DISCOVERY_SERVER="127.0.0.1:11811;127.0.0.1:11888"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener_1
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker_2
export ROS_DISCOVERY_SERVER=";127.0.0.1:11888"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener_2

我们应该看到“侦听器 1”如何从两个谈话者节点接收消息,而“侦听器 2”与“谈话者 2”位于不同的分区,因此不会从它接收消息。

Note

一旦两个端点(ROS 节点)发现了彼此,它们就不需要它们之间的发现服务器网络来监听彼此的消息。

ROS 2 自省

ROS 2 命令行界面 支持多种自省工具来分析 ROS 2 网络的行为。 这些工具(即 ros2 bag recordros2 topic list 等)对于理解 ROS 2 工作网络非常有帮助。

大多数这些工具使用 DDS 简单发现与每个现有参与者交换主题信息(使用简单发现,网络中的每个参与者都相互连接)。 但是,新的 Discovery Server v2 实现了一种网络流量减少方案,限制了不共享主题的参与者之间的发现数据。 这意味着,只有当节点具有该主题的写入器或读取器时,它才会接收该主题的发现数据。 由于大多数 ROS 2 CLI 都需要网络中的节点(其中一些依赖于正在运行的 ROS 2 守护程序,而一些则创建自己的节点),因此使用 Discovery Server v2,这些节点将不会拥有所有网络信息,因此它们的功能将受到限制。

Discovery Server v2 功能允许每个参与者作为 超级客户端 运行,这是一种连接到 服务器客户端,它从中接收所有可用的发现信息(而不仅仅是它需要的信息)。 从这个意义上讲,ROS 2 自省工具可以配置为 超级客户端,从而能够发现网络内使用 Discovery Server 协议的每个实体。

Note

在本节中,我们使用术语“参与者”作为 DDS 实体。每个 DDS“参与者”都对应一个 ROS 2“上下文”,即 DDS 上的 ROS 2 抽象。 节点 是依赖于 DDS 通信接口的 ROS 2 实体:“DataWriter”和“DataReader”。 每个“参与者”可以容纳多个 ROS 2 节点。 有关这些概念的更多详细信息,请访问“节点到参与者映射设计文档 <http://design.ros2.org/articles/Node_to_Participant_mapping.html>`__

Daemon 的相关工具

ROS 2 Daemon 用于多个 ROS 2 CLI 自省工具。 它创建自己的参与者,将 ROS 2 节点添加到网络图,以便接收所有发送的数据。 为了使 ROS 2 CLI 在使用发现服务器机制时工作,需要将 ROS 2 Daemon 配置为**超级客户端**。 因此,本节专门介绍如何使用 ROS 2 CLI 和作为**超级客户端**运行的 ROS 2 Daemon。 这将允许 Daemon 发现整个节点图,并接收所有主题和端点信息。 为此,使用 Fast DDS XML 配置文件来配置 ROS 2 Daemon 和 CLI 工具。

下面您可以找到一个 XML 配置文件,在本教程中,它应在工作目录中保存为“super_client_configuration_file.xml”文件。 此文件将使用它来配置每个新参与者,作为**超级客户端**。

<?xml version="1.0" encoding="UTF-8" ?>
 <dds>
     <profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
         <participant profile_name="super_client_profile" is_default_profile="true">
             <rtps>
                 <builtin>
                     <discovery_config>
                         <discoveryProtocol>SUPER_CLIENT</discoveryProtocol>
                         <discoveryServersList>
                             <RemoteServer prefix="44.53.00.5f.45.50.52.4f.53.49.4d.41">
                                 <metatrafficUnicastLocatorList>
                                     <locator>
                                         <udpv4>
                                             <address>127.0.0.1</address>
                                             <port>11811</port>
                                         </udpv4>
                                     </locator>
                                 </metatrafficUnicastLocatorList>
                             </RemoteServer>
                         </discoveryServersList>
                     </discovery_config>
                 </builtin>
             </rtps>
         </participant>
     </profiles>
 </dds>

Note

RemoteServer 标签下,应根据 CLI 上传递的服务器 ID 更新 prefix 属性值(请参阅 Fast DDS CLI)。 显示的 XML 代码片段中指定的值对应于 ID 值 0。

首先,使用 Fast DDS CLI 实例化一个发现服务器,并指定 ID 值 0。

fastdds discovery -i 0 -l 127.0.0.1 -p 11811

运行一个谈话者和一个监听器,它们将通过服务器相互发现(请注意,“ROS_DISCOVERY_SERVER”配置与“super_client_configuration_file.xml”中的配置相同)。

export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker

然后,使用**超级客户端**配置实例化 ROS 2 守护进程(记住在每个新终端中获取 ROS 2 安装源)。

export FASTRTPS_DEFAULT_PROFILES_FILE=super_client_configuration_file.xml
ros2 daemon stop
ros2 daemon start
ros2 topic list
ros2 node info /talker
ros2 topic info /chatter
ros2 topic echo /chatter

我们还可以使用 ROS 2 工具“rqt_graph”查看节点的图表,如下所示(您可能需要按刷新按钮):

export FASTRTPS_DEFAULT_PROFILES_FILE=super_client_configuration_file.xml
rqt_graph

无守护进程工具

某些 ROS 2 CLI 工具不使用 ROS 2 守护进程。 为了使这些工具能够连接到发现服务器并接收所有主题信息,它们需要实例化为连接到**服务器**的**超级客户端**。

按照前面的配置,构建一个带有谈话者和侦听者的简单系统。 首先,运行**服务器**:

fastdds discovery -i 0 -l 127.0.0.1 -p 11811

然后,在不同的终端运行说话者和监听者:

export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener
export ROS_DISCOVERY_SERVER="127.0.0.1:11811"
ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker

继续使用带有“–no-daemon”选项的 ROS 2 CLI 和新配置。 新节点将与现有服务器连接并了解每个主题。 不需要导出“ROS_DISCOVERY_SERVER”,因为 ROS 2 工具将通过“FASTRTPS_DEFAULT_PROFILES_FILE”进行配置。

export FASTRTPS_DEFAULT_PROFILES_FILE=super_client_configuration_file.xml
ros2 topic list --no-daemon
ros2 node info /talker --no-daemon --spin-time 2

将快速 DDS 发现服务器与简单发现协议进行比较

为了使用 简单发现 协议(分布式发现的默认 DDS 机制)或 发现服务器 比较执行节点,提供了两个脚本,分别执行一个谈话者和多个侦听器并分析这段时间内的网络流量。

对于此实验,需要在您的系统上安装“tshark”。

为了避免使用进程内模式,配置文件是必需的。

Note

这些脚本仅支持 Linux,并且需要发现服务器关闭功能,该功能仅在 ROS 2 Foxy 中提供的版本以上的版本中可用。 为了使用此功能,请使用 Fast DDS v2.1.0 或更高版本编译 ROS 2。

这些脚本的功能仅供高级用途参考,其研究则留给用户去研究。

运行 bash 脚本,将“setup.bash”文件的路径作为 ROS 2 的源路径作为参数。 这将生成简单发现的流量跟踪。 使用第二个参数“SERVER”执行相同的脚本。 它将生成使用发现服务器的跟踪。

根据您对“tcpdump”的配置,此脚本可能需要“sudo”权限才能读取网络设备上的流量。

两次执行完成后,运行 Python 脚本以生成类似于下图的图表。

$ export FASTRTPS_DEFAULT_PROFILES_FILE="no_intraprocess_configuration.xml"
$ sudo bash generate_discovery_packages.bash ~/ros2/install/local_setup.bash
$ sudo bash generate_discovery_packages.bash ~/ros2/install/local_setup.bash SERVER
$ python3 discovery_packets.py
../../../_images/discovery_packets.svg

该图是特定实验运行的结果。 读者可以执行脚本并生成自己的结果进行比较。 可以很容易地看出,使用发现服务时网络流量会减少。

流量减少是由于避免每个节点都宣布自己并等待网络上其他每个节点的响应。 这在大型架构中会产生大量流量。 此方法带来的减少量会随着节点数量的增加而增加,使此架构比简单发现协议方法更具可扩展性。

新的 Fast DDS 发现服务器 v2 自 Fast DDS v2.0.2 起可用,取代了旧的发现服务器。 在这个新版本中,那些不共享主题的节点将自动不会发现彼此,从而节省了连接它们及其端点所需的整个发现数据。 上面的实验没有显示这种情况,但即便如此,由于 ROS 2 节点隐藏了基础设施主题,流量的大幅减少也是值得赞赏的。