RTPS(Real-Time Publish-Subscribe)---DDS的传输协议

RTPS(Real-Time Publish-Subscribe Protocol)是 DDS 标准体系的基石,也是 ROS2 能够实现高性能、高可靠、即插即用通信的核心技术。

一、RTPS 概述

1.1 定义

RTPS 全称为 DDS Interoperability Wire Protocol (DDSI-RTPS),是 OMG 组织于 2002 年制定的标准有线协议,专门解决不同厂商 DDS 实现之间的互操作性问题。

它定义了 DDS 数据在网络上传输的精确二进制格式、通信行为规则和状态机,是 DDS 世界中唯一的"通用网络语言"。我们可以用三层模型清晰理解它的定位:

  • CDR:定义数据"长什么样"(二进制序列化格式)
  • RTPS:定义数据"怎么传"(网络传输规则与协议)
  • DDS:定义应用"怎么用"(上层 API 接口与语义)

1.2 设计目标与优先级

OMG 官方明确规定了 RTPS 的设计目标,并按优先级从高到低排序:

  1. 性能:最小化延迟和开销,支持微秒级延迟和百万级消息吞吐量
  2. 可配置性:细粒度 QoS 控制,可在可靠性、实时性和资源消耗之间灵活平衡
  3. 容错性:无单点故障,支持节点动态加入/离开,网络中断后自动恢复
  4. 可扩展性:支持从几个节点到数千个节点的大规模分布式系统
  5. 即插即用:完全自动发现,无需手动配置任何 IP 地址和端口
  6. 类型安全:编译时和运行时双重类型检查,确保数据类型一致性
  7. 可移植性:与底层传输无关,支持 UDP、TCP、TSN、5G 等多种网络技术

1.3 DDS 与 RTPS

维度 DDS (Data Distribution Service) RTPS (Real-Time Publish-Subscribe)
层级 应用层标准 传输层标准
定义 数据中心发布-订阅(DCPS)模型、API 接口、QoS 语义 网络包格式、通信行为、状态机
解决问题 应用程序如何与中间件交互 不同 DDS 实现如何在网络上交换数据
提供能力 应用可移植性 实现互操作性
类比 HTTP 协议(定义请求/响应语义) TCP/IP 协议(定义数据包传输规则)

DDS 是接口规范,RTPS 是 wire 协议规范。所有符合 DDS 标准的实现(如 Fast DDS、Cyclone DDS、RTI Connext)都必须支持 RTPS 协议,才能实现跨厂商互操作。

二、RTPS 架构:PIM 与 PSM 分离

RTPS 采用 OMG 模型驱动架构(MDA),严格分离平台无关模型(PIM)平台特定模型(PSM),这是它能够支持多种传输技术并保持高度可扩展性的关键。

2.1 平台无关模型(PIM,Platform Independent Model)

PIM 定义了 RTPS 的核心逻辑和行为,与任何具体传输技术无关。它只描述业务逻辑、功能规则和数据结构,完全不绑定任何编程语言、操作系统或网络协议。

PIM 包括以下核心内容:

  • 实体模型(Participant、Writer、Reader、Topic)
  • 消息类型和二进制格式
  • 通信状态机定义
  • 自动发现协议(SPDP、SEDP)
  • 可靠传输机制(心跳-确认-重传)
  • QoS 策略执行逻辑
  • 数据序列化规则

2.2 平台特定模型(PSM,Platform Specific Model)

PSM 将抽象的 PIM 映射到具体的传输技术,在 PIM(平台无关抽象模型) 的基础上,绑定具体技术平台、框架、语言、中间件、系统,带上所有落地细节的可直接落地实现模型。OMG 官方定义了两种标准 PSM:

  1. UDP/IP PSM:最原始、最广泛使用的 PSM,利用 UDP 的无连接、低延迟和多播特性,是实时系统的首选
  2. TCP/IP PSM:RTPS v2.2 新增,用于穿越防火墙和利用现有 TCP 基础设施,适合跨网段通信

这种分离架构使得 RTPS 可以轻松扩展到其他传输技术。目前已有多个厂商实现了:

  • RTPS over TSN(时间敏感网络):用于工业自动化领域的确定性通信
  • RTPS over 5G:用于车联网和移动机器人
  • RTPS over 串口:用于嵌入式设备和低带宽场景

三、RTPS 实体模型:构建通信的基本单元

RTPS 定义了四个核心实体,构成了整个协议的基础。所有通信都是通过这些实体之间的交互完成的。

3.1 Domain:通信隔离的边界

  • 最高级别的隔离单元,不同域之间完全独立,不能通信
  • 由一个唯一的 Domain ID 标识(范围 0-232)
  • 所有属于同一域的 RTPS 实体可以相互发现和通信
  • 在 ROS2 中,可以通过 ROS_DOMAIN_ID 环境变量设置域 ID

设计目的:允许在同一物理网络上运行多个独立的 DDS 系统,互不干扰。例如,在一个工厂里,可以用域 0 运行机器人控制系统,用域 1 运行环境监测系统。

3.2 RTPSParticipant:应用的网络代表

  • 应用程序在 RTPS 域中的唯一代表
  • 每个应用程序在一个域中只能有一个 Participant
  • 负责创建和管理所有 Writer 和 Reader 端点
  • 包含一个全局唯一的 GUID,用于标识自身
  • 维护本地参与者列表和端点列表
  • 处理自动发现和通信连接建立

Participant :一个应用程序在 DDS 网络中的"身份证"和"通信门户"

3.3 RTPSWriter 与 RTPSReader:数据的发送与接收

  • RTPSWriter:负责发送数据的端点,每个 Writer 关联一个主题(Topic)和一个数据类型
  • RTPSReader:负责接收数据的端点,每个 Reader 关联一个主题(Topic)和一个数据类型
  • 两者都维护一个历史缓存(HistoryCache),用于存储最近的消息
  • 当 Writer 和 Reader 的主题、数据类型匹配且 QoS 兼容时,自动建立通信连接

3.4 GUID 与 Locator:实体的唯一标识与地址

GUID 结构

每个 RTPS 实体(Participant、Writer、Reader)都有一个 16 字节的全局唯一标识符(GUID),结构如下:

  • GuidPrefix(12 字节):标识 Participant,同一 Participant 下的所有实体共享相同的 GuidPrefix
  • EntityId(4 字节):标识 Participant 内的具体实体

EntityId 详细结构

  • 前 2 字节:实体类型(如 Participant、Writer、Reader、内置端点)
  • 后 2 字节:实体实例 ID

例如:

  • Participant 的 EntityId:0x00000001
  • BuiltinPublicationWriter 的 EntityId:0x00000002
  • BuiltinSubscriptionReader 的 EntityId:0x00000003
Locator

Locator 完整标识了一个特定传输协议上的一个特定端口 ,Locator以 24 字节 标准结构封装了传输协议、IP 地址和端口号 ,支持 UDPv4/v6、TCPv4/v6、共享内存等多种类型。
RTPS 采用两级 Locator 体系

  • Participant 级 Locator 用于 SPDP/SEDP 自动发现阶段交换元数据
  • 端点级 Locator 用于匹配成功后的端到端用户数据传输,实现流量隔离与性能优化。

Locator天然支持多网卡自动切换、IP 地址动态更新和多协议同时通信,端口号按标准公式计算。

Locator 是 RTPS 实体的网络地址,格式为:

复制代码
<协议>://<地址>:<端口>
<protocol>://<ip_address>:<port>

例如:udp://192.168.1.100:7410tcp://10.0.0.5:7420

关键特性

  • 每个 Participant 可以有多个 Locator,支持同时在多个网络接口上通信
  • 每个 Writer 和 Reader 也有自己的 Locator,用于直接通信
  • Locator 可以动态更新,支持网络地址变化

四、RTPS 四大核心机制

RTPS 的强大功能来自于它的四大核心机制:自动发现可靠传输QoS 服务质量数据序列化

4.1 自动发现:即插即用的核心

RTPS 的即插即用特性完全依赖于自动发现机制。OMG 标准定义了两个层次的发现协议,按顺序执行:

SPDP(Simple Participant Discovery Protocol)

参与者级别的发现,负责找到同域内有哪些节点

  • 每个 Participant 启动后,立即向多播地址 239.255.0.1:7400+DomainID 发送参与者宣告消息
  • 宣告消息包含:Participant 的 GUID、Locator 列表、支持的协议版本和厂商 ID
  • 其他同域 Participant 收到后,将对方加入本地参与者列表
  • Participant 会周期性(默认 30 秒)重新发送宣告消息,更新存活状态
  • 如果在 3 个周期内没有收到某个 Participant 的宣告,就认为它已经下线,从本地列表中删除
SEDP(Simple Endpoint Discovery Protocol)

端点级别的发现,负责找到每个节点里有哪些发布者和订阅者

  • 在 SPDP 发现参与者之后,通过单播交换端点信息
  • 每个 Participant 有两个内置端点:
    • BuiltinPublicationWriter:发布本地所有 Writer 和 Reader 的信息
    • BuiltinSubscriptionReader:接收远程所有 Writer 和 Reader 的信息
  • 端点信息包含:主题名称、数据类型、QoS 配置和 Locator 列表
  • 当 Writer 和 Reader 的主题、数据类型匹配且 QoS 兼容时,自动建立通信连接

发现流程

  1. 节点 A 启动,发送 SPDP 多播宣告
  2. 节点 B 收到宣告,将 A 加入本地参与者列表
  3. 节点 B 向 A 的单播地址发送自己的 SPDP 宣告
  4. 节点 A 收到宣告,将 B 加入本地参与者列表
  5. 节点 A 通过 BuiltinPublicationWriter 发布自己的端点信息
  6. 节点 B 通过 BuiltinSubscriptionReader 收到 A 的端点信息
  7. 节点 B 发布自己的端点信息
  8. 双方匹配主题和 QoS,建立通信连接

4.2 可靠传输:在 UDP 上实现可靠通信

RTPS 在不可靠的 UDP 传输之上实现了比 TCP 更适合实时系统的可靠传输机制 ,其核心是基于序列号的心跳-确认-重传机制

基本工作流程
  1. 消息序列号:每个 Writer 为每个消息分配一个唯一的、单调递增的序列号
  2. 历史缓存:Writer 和 Reader 都维护历史缓存,存储最近的消息
  3. 心跳消息(Heartbeat):Writer 周期性地向所有关联的 Reader 发送心跳,告知当前最高序列号
  4. 确认-否定消息(ACKNACK) :Reader 收到心跳后,回复 ACKNACK 消息,指出哪些序列号已经收到,哪些丢失
  5. 重传:Writer 收到 ACKNACK 后,立即重传丢失的消息
优化
  • NACK 合并:Reader 将多个丢失的序列号合并到一个 ACKNACK 消息中,减少网络开销
  • 心跳抑制:当 Writer 没有新数据时,自动降低心跳发送频率
  • 负向确认:Reader 可以主动请求重传丢失的消息,而不必等待心跳
  • 选择性重传:只重传真正丢失的消息,而不是重传整个窗口

与 TCP 的区别

  • RTPS 支持一对多可靠多播,而 TCP 只能一对一
  • RTPS 可以独立控制每个 Reader 的可靠性级别
  • RTPS 没有 TCP 的队头阻塞问题,更适合实时系统
  • RTPS 的重传延迟更低,因为它不需要等待超时

4.3 QoS 服务质量:平衡可靠与实时

RTPS 支持 20 多种 QoS 策略,允许用户精确控制通信的各个方面。OMG 标准将它们分为以下几类:

QoS 类别 关键策略 作用
可靠性 RELIABILITY 选择 BEST_EFFORT(尽力而为)或 RELIABLE(可靠)模式
时间性 DEADLINE, LATENCY_BUDGET 限制数据传输的最大延迟和截止时间
资源 HISTORY, RESOURCE_LIMITS 控制历史缓存大小和资源使用
生命周期 LIFESPAN, DURABILITY 控制数据的有效期和持久性
所有权 OWNERSHIP 解决多个 Writer 发布同一主题的冲突
存活性 LIVELINESS 检测节点是否存活
表示 DATA_REPRESENTATION 指定数据序列化格式

QoS 匹配规则 :Writer 和 Reader 的 QoS 必须兼容才能建立连接。一般来说,Writer 提供的 QoS 级别必须高于或等于 Reader 要求的 QoS 级别

例如:

  • 如果 Writer 配置为 RELIABLE,Reader 可以配置为 RELIABLE 或 BEST_EFFORT
  • 如果 Writer 配置为 BEST_EFFORT,Reader 只能配置为 BEST_EFFORT
  • 如果 Writer 配置为 TRANSIENT_LOCAL 持久性,Reader 可以配置为 TRANSIENT_LOCAL 或 VOLATILE

4.4 数据序列化:CDR 与 XCDR2

RTPS 使用 CDR(Common Data Representation) 作为标准数据序列化格式,确保跨平台和跨语言的一致性。

CDR 的主要特点:

  • 支持所有基本数据类型和复杂数据类型(结构体、数组、序列、联合)
  • 支持大端和小端字节序
  • 编码效率高,开销小

RTPS v2.5 新增了对 XCDR2(Extensible CDR version 2) 的支持,解决了传统 CDR 不支持数据类型演进的问题。XCDR2 支持:

  • 可选字段
  • 默认值
  • 类型扩展(添加新字段不会破坏兼容性)
  • 更高效的编码

五、RTPS 消息格式:网线上的二进制语言

RTPS 消息由 RTPS 头子消息列表组成,所有字段都采用网络字节序(大端)传输。

5.1 RTPS 头(20 字节)

字节偏移 比特范围 长度 字段名称 说明
0 ~ 3 0 ~ 31 bit 4 Byte RTPS 固定 RTPS 4个字符
4 ~ 7 32 ~ 63 bit 4 Byte ProtocolVersion 高16bit主版本,低16bit次版本
8 ~ 11 64 ~ 95 bit 4 Byte VendorId 厂商标识
12 ~ 19 96 ~ 159 bit 8 Byte GuidPrefix RTPS 全局唯一标识前缀
  • 前 4 字节:固定为"RTPS",用于快速识别 RTPS 消息
  • ProtocolVersion:协议版本,当前为 2.5
  • VendorId:厂商 ID,用于标识 DDS 实现(如 eProsima 为 0x0101,RTI 为 0x0102)
  • GuidPrefix:发送方 Participant 的 GUID 前缀

5.2 子消息格式

RTPS 头后面跟着一个或多个子消息,每个子消息有一个 4 字节的子消息头:

字节偏移 比特范围 字段长度 字段名称 字段说明
0 0~7 bit 1 Byte SubmessageId 子消息类型ID
1 8~15 bit 1 Byte Flags 标志位
2~3 16~31 bit 2 Byte SubmessageLength 子消息长度
4~n 32bit 往后 可变长度 Submessage Contents 子消息实际载荷内容
  • SubmessageId:子消息类型 ID
  • Flags:子消息标志位,用于控制子消息的行为
  • SubmessageLength:子消息内容的长度(不包括子消息头)

5.3 常见子消息类型

OMG 标准定义了 16 种不同的子消息类型,最常用的有:

  • DATA:携带用户数据,包含序列号、时间戳和数据载荷
  • HEARTBEAT:Writer 告知 Reader 当前最高序列号
  • ACKNACK:Reader 向 Writer 确认收到的消息和请求重传
  • INFO_TS:携带时间戳信息
  • INFO_DST:指定消息的目标 Participant
  • GAP:告知 Reader 某些序列号的消息已经被删除,不需要重传

六、RTPS 在 ROS2 中的应用

ROS2 的所有通信功能,包括发布/订阅、服务、动作和参数,都是基于 RTPS 实现的。

6.1 ROS2 实体到 RTPS 实体的映射

ROS2 的上层实体直接映射到 RTPS 的底层实体:

ROS2 实体 RTPS 实体
Node RTPSParticipant
Publisher RTPSWriter
Subscription RTPSReader
Service Server RTPSReader(请求) + RTPSWriter(响应)
Service Client RTPSWriter(请求) + RTPSReader(响应)
Action Server 多个 RTPSWriter 和 RTPSReader
Action Client 多个 RTPSWriter 和 RTPSReader

6.2 ROS2 QoS 与 RTPS QoS 的对应关系

ROS2 的 QoS 配置直接映射到 RTPS 的 QoS 策略:

cpp 复制代码
// ROS2 QoS 配置示例
rclcpp::QoS qos(10);
qos.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
qos.durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);
qos.history(RMW_QOS_POLICY_HISTORY_KEEP_LAST);
qos.deadline(std::chrono::milliseconds(100));
qos.liveliness(RMW_QOS_POLICY_LIVELINESS_AUTOMATIC);

6.3 ROS2 中 RTPS 的调试工具

ROS2 提供了多个工具来调试 RTPS 通信:

  • ros2 doctor:检查 ROS2 环境和网络配置,诊断常见问题
  • ros2 topic info:查看主题的详细信息,包括 QoS 配置和端点信息
  • ros2 node info:查看节点的详细信息,包括发布和订阅的主题
  • ros2 service info:查看服务的详细信息
  • Wireshark:抓包分析 RTPS 消息,Wireshark 已经内置了 RTPS 解析器
  • fastddscyclonedds 命令行工具:查看 DDS 层的详细信息

七、RTPS 性能优化

7.1 传输层优化

  • 优先使用 UDP/IP PSM,它比 TCP/IP PSM 延迟更低、开销更小
  • 对于大数据传输(如点云、图像),启用分片传输
  • 对于跨网段通信,可以使用 TCP/IP PSM 或配置多播路由
  • 启用 UDP 校验和,确保数据完整性

7.2 多播的合理使用

  • 对于一对多通信,使用多播可以显著减少网络带宽
  • 避免在不支持多播的网络中使用多播(如某些云环境)
  • 合理配置多播 TTL,防止消息扩散到不必要的网段
  • 对于大规模系统,可以使用多播分区(Multicast Partition)减少网络流量

7.3 历史缓存与资源限制配置

  • 根据数据的重要性和频率配置合适的历史缓存大小
  • 使用 RESOURCE_LIMITS QoS 限制内存使用,防止内存溢出
  • 对于高频数据,使用 KEEP_LAST 历史策略,只保留最新的 N 条消息
  • 对于不需要历史数据的场景,使用 KEEP_ALL 历史策略

7.4 分片传输与大数据处理

  • 当消息大小超过 MTU(通常为 1500 字节)时,RTPS 会自动进行分片传输
  • 对于非常大的消息(如 10MB 以上的点云数据),可以考虑使用压缩或分块传输
  • 合理配置分片大小,平衡传输效率和重传开销
  • 启用异步发送和接收,提高吞吐量

八、常见问题

Q1:RTPS 为什么用 UDP 而不是 TCP?

A:UDP 具有无连接、低延迟和多播支持的特性,这些对于实时系统至关重要。RTPS 在 UDP 之上实现了可靠传输,同时保留了 UDP 的优点。TCP 的滑动窗口机制和队头阻塞问题不适合实时系统的一对多通信场景。

Q2:ROS2 节点无法发现对方怎么办?

A:这是最常见的问题,通常由以下原因引起:

  1. Domain ID 不匹配 :确保所有节点使用相同的 ROS_DOMAIN_ID
  2. 多播不通:检查防火墙是否阻止了多播流量,网络交换机是否支持多播
  3. 网络接口问题:确保节点绑定到正确的网络接口
  4. 防火墙规则:允许 UDP 端口 7400-7450 的入站和出站流量

Q3:如何解决 ROS2 跨网段通信问题?

A:有两种主要方法:

  1. 配置多播路由:在路由器上配置多播路由,使多播消息能够跨网段传输
  2. 使用 TCP/IP PSM:将 DDS 实现配置为使用 TCP/IP PSM,它支持单播跨网段通信
  3. 使用 Discovery Server:使用 Fast DDS 的 Discovery Server 功能,通过单播进行发现

Q4:RTPS 支持加密吗?

A:是的,RTPS v2.3 及以上版本支持 DDS 安全规范,可以对消息进行加密、认证和访问控制。ROS2 Humble 及以上版本已经集成了 DDS 安全功能。

Q5:不同 DDS 实现之间可以互操作吗?

A:是的,只要它们都符合 RTPS 标准,就可以实现互操作。例如,使用 Fast DDS 的节点可以与使用 Cyclone DDS 或 RTI Connext 的节点通信。

相关推荐
TIEM_691 小时前
C++ vector容器全面解析:从入门到精通
开发语言·c++
Irissgwe1 小时前
c++多态
开发语言·c++·多态
lingran__1 小时前
C++_类和对象(上)
开发语言·c++
lzh200409191 小时前
手搓一个简易 Linux 进程池:巩固进程知识
linux·c++
basketball6161 小时前
C++ 的 const 相关知识点总结
开发语言·c++
阿文的代码库2 小时前
对于C++中push_back的原理介绍与分析
开发语言·c++
枕星而眠2 小时前
C++ 核心语法精讲:auto / 模板 / 命名空间 / 动态内存 从用法到面试
开发语言·c++·面试
yoyo_zzm2 小时前
六大编程语言核心差异全解析
c语言·c++·spring boot·php
liu****2 小时前
第16届国赛蓝桥杯大赛C/C++大学C组
c语言·数据结构·c++·算法·蓝桥杯