比较杂乱,调试会遇到问题,并且ROS2的问题和ROS1有非常大的差异性。
一些概念:
词汇表:
- DDS - 数据分发服务
- RTPS - 实时发布订阅
- QoS - 服务质量
- 客户端 - 也称为客户端,是指连接到 ROS 服务以发送请求和接收响应的应用程序。
- 服务器 - 也称为服务器,是指运行 ROS 服务的应用程序,它接收请求并发送响应。
eProsima 快速 RTPS
eprosima Fast RTPS 是 RTPS(实时发布订阅)协议的 C 实现,它通过对象管理组 (OMG) 联盟定义和维护的不可靠传输(如 UDP)提供发布者-订阅者通信。 RTPS 还是 OMG 为数据分发服务 (DDS) 标准定义的有线互操作性协议。 eProsima Fast RTPS 具有独立和最新的优势,因为大多数供应商解决方案要么将 RTPS 作为实现 DDS 的工具,要么使用过去版本的规范。
该库的一些主要功能是:
- 为实时应用程序配置的尽力而为和可靠的发布-订阅通信策略。
- 即插即用连接,以便网络的任何其他成员自动发现任何新应用程序。
- 模块化和可扩展性允许网络中复杂和简单的设备持续增长。
- 可配置的网络行为和可互换的传输层:为每个部署选择最佳协议和系统输入/输出通道组合。
- 两个 API 层:一个专注于可用性的高级发布者-订阅者和一个提供对 RTPS 协议内部工作的更精细访问的较低级别的写入者-阅读者。
关于服务质量设置
概述
ROS 2 提供了丰富多样的服务质量 (QoS) 策略,允许您调整节点之间的通信。使用正确的服务质量策略集,ROS 2 可以像 TCP 一样可靠,也可以像 UDP 一样尽最大努力,在这两者之间有很多很多可能的状态。与主要仅支持 TCP 的 ROS 1 不同,ROS 2 受益于底层 DDS 传输的灵活性,在有损无线网络的环境中,“尽力而为”策略更合适,或者在具有正确质量的实时计算系统中需要服务配置文件才能满足最后期限。
一组 QoS “策略”组合起来形成一个 QoS “配置文件”。鉴于为给定场景选择正确 QoS 策略的复杂性,ROS 2 为常见用例(例如传感器数据)提供了一组预定义的 QoS 配置文件。同时,开发人员可以灵活地控制 QoS 配置文件的特定策略。
可以为发布者、订阅者、服务服务器和客户端指定 QoS 配置文件。QoS 配置文件可以独立应用于上述实体的每个实例,但如果使用不同的配置文件,它们可能会不兼容,从而阻止消息的传递。
服务质量策略
基本 QoS 配置文件当前包括以下策略的设置:
- 历史
- 保持最后:仅存储最多 N 个样本,可通过队列深度选项进行配置。
- Keep all:存储所有样本,受底层中间件配置的资源限制。
- 深度
- 队列大小:仅当“历史”策略设置为“保持最后”时才使用。
- 可靠性
- 尽力而为:尝试提供样本,但如果网络不健壮,可能会丢失它们。
- 可靠:保证样品送达,可多次重试。
- 耐用性
- Transient local:发布者负责为“后期加入”订阅保存样本。
- Volatile:不尝试保留样本。
- 最后期限
- 持续时间:后续消息发布到主题之间的预期最长时间
- 寿命
- Duration:消息发布和接收之间的最长时间,而消息不被视为陈旧或过期(过期消息被静默丢弃,实际上永远不会收到)。
- 活泼
- 自动:当任何一个发布者发布消息时,系统将认为节点的所有发布者在另一个“租用期限”内都处于活动状态。
- 按主题手动:如果系统手动断言它仍然活着(通过调用发布者 API),则系统将认为发布者在另一个“租约期限”内还活着。
- 租期
- Duration:发布者在系统认为它失去活力之前必须表明它处于活动状态的最长时间(失去活力可能表示失败)。
对于每一个不是持续时间的策略,还有“系统默认”选项,它使用底层中间件的默认值。对于每个作为持续时间的策略,还存在一个“默认”选项,表示持续时间未指定,底层中间件通常会将其解释为无限长的持续时间。
与 ROS 1 的比较
ROS 2 中的“历史”和“深度”策略结合起来提供类似于 ROS 1 中队列大小的功能。
ROS 2 中的“可靠性”策略类似于使用 UDPROS(仅在 中roscpp
)表示“尽力而为”,或使用 TCPROS(ROS 1 默认)表示“可靠”。但是请注意,即使 ROS 2 中的可靠策略也是使用 UDP 实现的,它允许在适当的情况下进行多播。
“持久性”策略“本地瞬态”,结合任何深度,提供类似于“锁定”发布者的功能。ROS 2 中的其余策略与 ROS 1 中可用的任何策略都不相似,这意味着 ROS 2 在这方面比 ROS 1 更具特色。未来可能会在 ROS 2 中提供更多的 QoS 策略。
QoS 配置文件
配置文件允许开发人员专注于他们的应用程序,而不必担心每一个可能的 QoS 设置。QoS 配置文件定义了一组策略,这些策略预计可以很好地用于特定用例。
当前定义的 QoS 配置文件是:
- 发布者和订阅的默认 QoS 设置 为了使从 ROS 1 到 ROS 2 的转换更容易,执行类似的网络行为是可取的。默认情况下,ROS 2 中的发布者和订阅者具有“保持最后”的历史队列大小,队列大小为 10,可靠性为“可靠”,持久性为“易失”,活力为“系统默认”。最后期限、寿命和租约期限也都设置为“默认”。
- 服务 与发布者和订阅者一样,服务也是可靠的。服务使用 volatile 持久性尤其重要,否则重新启动的服务服务器可能会收到过时的请求。虽然客户端不会收到多个响应,但服务器不会收到过时请求的副作用。
- 传感器数据 对于传感器数据,在大多数情况下,及时接收读数比确保所有读数都到达更重要。也就是说,开发人员希望在捕获最新样本后立即获得最新样本,代价是可能会丢失一些样本。因此,传感器数据配置文件使用尽力而为的可靠性和较小的队列大小。
- 参数 ROS 2 中的参数基于服务,因此具有类似的配置文件。不同之处在于参数使用了更大的队列深度,以便在例如参数客户端无法到达参数服务服务器时,请求不会丢失。
- 系统默认 这对所有策略使用 RMW 实现的默认值。不同的 RMW 实现可能有不同的默认值。
- 虽然 DDS 提供了许多设置来实现对实体的服务质量 (QoS) 的细粒度控制,但 ROS 只为其中的少数提供本机支持。ROS 用户在创建发布者、订阅者等时,可以通过 QoS 配置结构指定历史、深度、可靠性和持久性。
- 这留下了很多 QoS 设置,只有在 DDS 供应商可以通过配置文件加载其他默认设置时才能设置。如果用户想要将他们的代码挂接到这些额外的 QoS 设置中,那么他们需要获取对 rmw 实现的引用,并针对供应商特定的 API 进行编程。如果没有 ROS 提供的抽象层,它们的代码就变得不那么可移植了。
- 主题将支持以下级别的活跃度:
- LIVELINESS_SYSTEM_DEFAULT - 使用 ROS 指定的默认默认值(即 LIVELINESS_AUTOMATIC)。
- LIVELINESS_AUTOMATIC - 建立 Topic 的信号来自 ROS rmw 层。
- LIVELINESS_MANUAL_BY_NODE - 建立 Topic 的信号处于活动状态是在节点级别。在节点上的任何传出通道上发布消息或来自应用程序的显式信号以断言节点上的活跃性将标记节点上的所有传出通道为活跃。
- LIVELINESS_MANUAL_BY_TOPIC - 建立主题的信号处于活动状态,处于主题级别。只有在主题上发布消息或来自应用程序的显式信号以断言主题上的活跃性,才会将主题标记为活跃。
- 为了让订阅者收听发布者的主题,他们请求的活跃度跟踪级别必须等于或低于发布者提供的跟踪级别,并且订阅者设置的直到被认为不活跃的时间必须大于时间由发布者设置。
- 生命周期策略为消息保持有效的时间建立了合同。对于订阅,它确定消息被视为有效的时间长度,在此时间之后将不会被接收。对于发布者,它确定消息被视为有效的时间长度,在此时间之后,它将从主题历史记录中删除并且不再发送给订阅者。生命周期时间为 0 将禁用生命周期跟踪。默认寿命时间为 0。
- 这些是 ROS 中需要进行的各种更改,以便原生支持 Deadline 和 Liveliness。
- 资源状态事件处理程序
- Deadline 和 Liveliness 策略都从需要通知应用程序的 rmw 层生成事件。对于截止日期,如果订阅者在截止日期内未收到任何内容,则订阅者将收到事件通知,如果在截止日期内未发布任何内容,则发布者将收到事件通知。对于 Liveliness,当不再有任何 Publisher 活动时,订阅者会收到事件来断言主题是活动的。当客户端和服务器违反定义的策略时,服务会生成类似的事件。这两个都属于“资源状态事件”的类别。
- 为了处理这些通知,用户可以提供新的回调函数,在特定主题的任何事件发生时都会调用这些回调函数。它将接收一个结构值作为参数,其中包含有关事件的信息,例如事件发生的时间和与事件相关的其他元数据。当用户的应用程序为发布者和订阅者调用创建函数时,这些回调函数将可选地提供。构造函数和创建函数将被重载以使这个新的处理程序成为可选的。
- 不会为每个状态事件调用一次状态事件处理程序。相反,只有在为回调提供服务的 Executor 检查时存在尚未处理的状态更改事件时,才会调用事件处理程序。
- 服务质量结构
- 在当前版本的 ROS 中,有一个 QoS 结构,用于在创建发布者和订阅者时指定 QoS 策略。通过这些新的 QoS 设置,支持的主题和服务的 QoS 策略集会有所不同。尽管如此,我们将坚持为 Topics 和 Services 使用单个结构,而不是切换到两种不同的结构类型,以便将更改保持在最低限度并在客户端库接口中保持尽可能多的向后兼容性。
- 现有的 QoS 策略结构将添加新字段,以指定 Deadline、Liveliness 和 Lifespan 所需的 QoS 设置。这些新字段实例将是枚举和时间值的组合。
- 常问问题
- 在确定是否错过最后期限时,Deadline 策略如何考虑 ROS 的额外开销(例如反序列化)?
- 作为简化,它不会尝试考虑任何 ROS 开销。如果 rmw 层在截止日期之前没有收到消息,并且如果 ROS 之上的用户应用程序在截止日期之前没有收到消息,则认为错过了截止日期。考虑到这一点,可以稍后添加新的截止日期政策。
- 为什么不会为每个状态更改事件调用回调,而不是潜在地组合相同类型的事件?
- 添加此功能将需要一个额外的缓冲区,用于在服务之间存储多个事件。此外,DDS API 更适合仅获知最新更改,并且需要对状态更改事件进行实时响应,以免错过单个事件。这不是单向门,我们可以稍后更改它以允许缓冲事件而不会破坏向后兼容性。
- 这些 QoS 策略如何影响操作和服务?
- 初始实现不支持操作和服务,因为这些概念如何在本地支持这些 QoS 功能有更复杂的微妙之处。在下面的未来工作部分中,我们将探讨服务可以实施这些政策的一些方式。
- DDS 主题实例如何影响这些 QoS 策略?
- 虽然所有这些策略都可以并且最终将支持键控实例,但本文档并未关注如何高度依赖 ROS 2 的设计来支持一般键控消息的细节。
- sudo sysctl net.ipv4.ipfrag_time=3
- sudo sysctl net.ipv4.ipfrag_high_thresh=134217728 # (128 MB)
- 设置发现服务器
- 首先启动一个 id 为 0、端口 11811(默认端口)的发现服务器并监听所有可用的接口。
- 打开一个新终端并运行:
- fastdds discovery --server-id 0
- export ROS_DISCOVERY_SERVER=127.0.0.1:11811
- ros2 run demo_nodes_cpp listener --ros-args --remap __node:=listener_discovery_server
- 启动侦听器节点
- 执行监听器演示,监听
/chatter
主题。 - 在新终端中,将环境变量设置
ROS_DISCOVERY_SERVER
为发现服务器的位置。(不要忘记在每个新终端中获取 ROS 2) - 启动侦听器节点。使用该参数更改本教程的节点名称。
--remap __node:=listener_discovery_server
- 这将创建一个 ROS 2 节点,它将自动为发现服务器创建一个客户端并连接到之前创建的服务器以执行发现,而不是使用多播。
- export ROS_DISCOVERY_SERVER=127.0.0.1:11811 ros2 run demo_nodes_cpp talker --ros-args --remap __node:=talker_discovery_server
- 启动谈话者节点
- 打开一个新终端并
ROS_DISCOVERY_SERVER
像以前一样设置环境变量,以便节点启动发现客户端。 - 您现在应该看到说话者发布“hello world”消息,而听众接收这些消息。
- ros2 run demo_nodes_cpp listener --ros-args --remap __node:=simple_listener
- ros2 run demo_nodes_cpp talker --ros-args --remap __node:=simple_talker
- 演示 Discovery Server 执行
- 到目前为止,没有证据表明这个例子和标准的说话者 - 听众例子的运行方式不同。为了清楚地证明这一点,请运行另一个未连接到发现服务器的节点。在新终端中运行一个新的监听器(默认监听
/chatter
主题)并检查它是否没有连接到已经运行的谈话者。 - 新的侦听器节点不应接收“hello world”消息。
- 为了最终验证一切是否正常运行,可以使用简单的发现协议(默认的 DDS 分布式发现机制)创建一个新的talker 进行发现。
- 现在您应该看到
simple_listener
节点接收来自 的“hello world”消息,simple_talker
但没有接收来自 的其他消息talker_discovery_server
。
该rqt_graph
工具可用于验证此示例的节点和结构。请记住,为了rqt_graph
与发现服务器协议一起使用(即查看listener_discovery_server
和talker_discovery_server
节点),ROS_DISCOVERY_SERVER
必须在启动它之前设置环境变量。
- 细节:
- DDS 调优信息
- 问题:当某些 IP 片段被丢弃时,通过有损(通常是 WiFi)连接发送数据会出现问题,可能导致接收端的内核缓冲区变满。
- 当一个 UDP 数据包缺少至少一个 IP 片段时,其余收到的片段会填满内核缓冲区。默认情况下,Linux 内核将在尝试重组数据包片段 30 秒后超时。由于此时内核缓冲区已满(默认大小为 256KB),因此无法进入新的片段,因此连接似乎会“挂起”很长一段时间。
- 这个问题在所有 DDS 供应商中都很普遍,因此解决方案涉及调整内核参数。
- 解决方案:使用尽力而为的 QoS 设置而不是可靠的。
- 尽力而为设置减少了网络流量,因为 DDS 实施不必产生可靠通信的开销,其中发布者需要确认发送给订阅者的消息,并且必须重新发送尚未正确接收的样本。
- 但是,如果 IP 片段的内核缓冲区已满,则症状仍然相同(阻塞 30 秒)。此解决方案应该可以在一定程度上改善问题,而无需调整参数。
- 解决方法:减小
ipfrag_time
参数的值。 net.ipv4.ipfrag_time / /proc/sys/net/ipv4/ipfrag_time
(默认 30 秒):将 IP 片段保存在内存中的时间(以秒为单位)。- 例如,通过运行将值减小到 3 秒:
- 减小此参数的值也会减小没有接收到片段的时间窗口。该参数对于所有传入的片段都是全局的,因此需要针对每个环境考虑降低其值的可行性。
- 解决方法:增大
ipfrag_high_thresh
参数值。 net.ipv4.ipfrag_high_thresh / /proc/sys/net/ipv4/ipfrag_high_thresh
(默认值:262144 字节):用于重组 IP 片段的最大内存。- 例如,通过运行将值增加到 128MB:
- 显着增加此参数的值是为了确保缓冲区永远不会完全变满。
ipfrag_time
但是,假设每个 UDP 数据包都缺少一个片段,则该值可能必须非常高才能保存在 的时间窗口内接收到的所有数据。