目录
一、FastDDS 概念介绍
1.1 什么是 FastDDS
FastDDS 是由 eProsima 公司开源的 C++ 实现 DDS(Data Distribution Service)中间件,它完整实现了 OMG(Object Management Group)发布的 DDS-RTPS(Real-Time Publish-Subscribe)线协议。FastDDS 是 ROS 2 的默认通信中间件(rmw_fastrtps),广泛应用于机器人、自动驾驶、工业控制等需要高性能实时通信的领域。
FastDDS = DDS 标准实现 + RTPS 线协议 + 发布/订阅模型 + 丰富 QoS 策略
关键标准
· DDS 1.4 标准(OMG 数据分发服务规范)
· DDS-RTPS 2.5 标准(线协议,保证不同厂商 DDS 互操作)
· DDS-XTypes(动态数据类型)
· DDS-Security 1.1(安全模型)
1.2 核心架构层次
FastDDS 采用分层架构设计,从下到上依次为:
- 传输层(Transport Layer):基于 UDP/TCP/Shared Memory 的数据传输,支持组播与单播
- RTPS 层:实现 DDS-RTPS 线协议,定义 Writer/Reader 实体交互规则
- DDS 层:面向用户的 API,封装 Publisher/Subscriber/DataWriter/DataReader
- 应用层:用户业务代码,通过 DomainParticipantFactory 接入
1.3 DDS 核心实体模型
DDS 标准定义了一组层次化的实体:
- DomainParticipantFactory:顶层工厂,创建 DomainParticipant
- DomainParticipant:域参与者,代表一个节点在特定 DDS Domain 中的身份
- Publisher / Subscriber:发布者/订阅者,管理 DataWriter/DataReader 的工厂
- DataWriter / DataReader:数据写入器/读取器,绑定 Topic 与数据类型
- Topic:主题,描述数据类型与名称,是通信双方的"契约"
- TypeSupport:类型支持,注册 IDL 生成的数据类型
二、RTPS 协议详解
2.1 什么是 RTPS
RTPS(Real-Time Publish-Subscribe Protocol for DDS over Interoperable transports)是 OMG 制定的线协议标准(Wire Protocol),它定义了不同 DDS 实现之间在网络上的数据交互格式与规则。简单来说,DDS 标准定义了"做什么"(API、QoS、数据模型),而 RTPS 协议定义了"怎么传"(报文格式、握手流程、状态机)。
· DDS:面向应用层的高层 API 规范(Publisher/Subscriber/Topic/QoS)
· RTPS:底层线协议规范,将 DDS 的语义映射到网络报文
· 互操作性:只要遵循 RTPS 协议,不同厂商的 DDS(如 FastDDS / Cyclone DDS / RTI Connext)就能互相通信
· 类比:DDS 之于 RTPS,如同 HTTP 之于 TCP;DDS 是应用语义层,RTPS 是传输协议层
2.2 RTPS 核心实体
RTPS 协议在 DDS 实体之下定义了对应的协议层实体:
- Participant(RTPS Participant):对应 DDS DomainParticipant,代表网络中的一个通信端点容器
- Endpoint:端点,分为 Writer 端点和 Reader 端点
- Writer(RTPS Writer):对应 DDS DataWriter,负责发送数据
- StatefulWriter:有状态写入器,跟踪已匹配 Reader 的状态,用于 RELIABLE 通信
- StatelessWriter:无状态写入器,不跟踪 Reader 状态,用于 BEST_EFFORT 通信
- Reader(RTPS Reader):对应 DDS DataReader,负责接收数据
- StatefulReader:有状态读取器,跟踪已匹配 Writer 的状态
- StatelessReader:无状态读取器,不跟踪 Writer 状态
- CacheChange:缓存变更,代表一次数据写入操作的原子单元
- HistoryCache:历史缓存,存储 CacheChange 的队列,对应 DDS 的 History QoS
2.3 RTPS 报文类型(子消息)
| 子消息类型 | 方向 | 作用 |
|---|---|---|
| DATA | Writer → Reader | 携带实际数据负载,包含 CacheChange 的序列号与序列化数据 |
| DATA_FRAG | Writer → Reader | 大数组分片传输,支持流式数据(如图像、点云) |
| HEARTBEAT | Writer → Reader | 心跳,告知 Reader 当前可用的 CacheChange 序列号范围 |
| ACKNACK | Reader → Writer | 确认/否定确认,告知 Writer 哪些数据已收到、哪些需要重传 |
| GAP | Writer → Reader | 告知 Reader 某些序列号的数据不再可用(已过期或被丢弃) |
| INFO_DST | 双向 | 告知对方自己的 GuidPrefix,用于实体寻址 |
| INFO_TS | 双向 | 携带时间戳信息 |
| INFO_SRC | 双向 | 告知报文来源的 GuidPrefix |
2.4 RTPS 通信三阶段
RTPS 协议将通信过程分为三个核心阶段:
· Participant 通过 Simple Discovery Protocol (SDP) 交换 Endpoint 信息
· 分为两个子阶段:
- SPDP(Simple Participant Discovery Protocol):发现网络中的其他 Participant
- SEDP(Simple Endpoint Discovery Protocol):交换 Writer/Reader 的 Topic、类型、QoS 信息
· 匹配条件:Topic 名称相同、数据类型相同、QoS 兼容
· 匹配成功后,Writer 与 Reader 建立"关联"(Association)
· Writer 调用
new_change() 创建 CacheChange 并写入 HistoryCache· StatefulWriter 通过 HEARTBEAT 告知 Reader 可用数据
· Reader 通过 ACKNACK 请求缺失的数据
· Writer 发送 DATA 或 DATA_FRAG 报文传输实际数据
· Reader 接收后写入自己的 HistoryCache,并通知上层 DataReaderListener
· RELIABLE 模式下,Writer 根据 ACKNACK 重传未确认的数据
· BEST_EFFORT 模式下,Writer 直接发送 DATA,不等待确认
· Writer 发送 dispose/unregister 通知数据被删除
· Reader 发送 GAP 清理无效序列号
· Participant 注销时,通过 SPDP 通告离线,其他节点清理关联
2.5 RTPS 可靠传输机制
RTPS 的可靠传输基于 HEARTBEAT-ACKNACK 握手机制,类似 TCP 的 ACK 但更灵活:
- HEARTBEAT:Writer 周期性或按需发送,携带
firstSN(最早可用序列号)和lastSN(最新序列号) - ACKNACK:Reader 回复,携带一个 Bitmap 标识哪些序列号已收到、哪些缺失
- 重传:Writer 根据 ACKNACK 的 Bitmap 重传缺失的 CacheChange
- 心跳周期:可通过
heartbeat_periodQoS 配置,平衡实时性与网络开销 - 延迟ACK:Reader 可累积多个数据后统一确认,减少网络开销
RTPS 匹配时检查 QoS 兼容性。如果 Writer 是 RELIABLE 而 Reader 是 BEST_EFFORT,可以匹配(Reader 忽略重传)。但如果 Writer 是 BEST_EFFORT 而 Reader 要求 RELIABLE,则无法匹配。这是 ROS 2 中常见的"话题无数据"问题根源。
三、核心 UML 架构图
3.1 DDS 实体类图
3.2 发布订阅通信时序图
3.3 RTPS 协议交互图
四、FastDDS 核心能力
4.1 能力矩阵
| 能力 | 说明 | 关键 API / 机制 |
|---|---|---|
| 发布/订阅通信 | 解耦的数据分发模型,N-to-N 通信 | DataWriter::write() / DataReader::take() |
| 动态发现 | 自动发现网络中的其他节点,无需中心配置 | Simple Discovery Protocol (SDP) |
| QoS 策略 | 22 种 QoS 策略,覆盖可靠性、持久性、时限等 | ReliabilityQos / DurabilityQos / DeadlineQos |
| 多传输层 | UDP / TCP / 共享内存 / 跨厂商互操作 | UDPv4TransportDescriptor / SHMTransportDescriptor |
| 内容过滤 | 按数据内容过滤,订阅者只接收感兴趣的样本 | ContentFilteredTopic / SQLFilter |
| 多域隔离 | 通过 Domain ID 隔离通信,不同域互不可见 | DomainParticipantFactory::create_participant(id) |
| 安全机制 | DDS-Security 标准:认证、授权、加密 | Authentication / AccessControl / Cryptographic |
| 零拷贝传输 | 基于共享内存的零拷贝数据传输 | SHMTransportDescriptor + DataSharingQos |
| 流式数据 | 支持大文件、视频流分片传输 | DATA_FRAG 子消息 |
| 持久化 | 历史数据持久化到磁盘,重启后可恢复 | DurabilityQos::TRANSIENT_DURABILITY_QOS |
4.2 核心 QoS 策略
DurabilityQos:VOLATILE(不保留历史) / TRANSIENT_LOCAL(保留晚加入者可见的历史) / TRANSIENT(持久化到磁盘)
HistoryQos:KEEP_LAST(N)(保留最近 N 个样本) / KEEP_ALL(保留全部)
DeadlineQos:期望数据更新周期,超时触发回调
LivelinessQos:检测节点存活状态,自动清理死亡节点
五、适用场景
5.1 典型应用场景
- ROS 2 机器人通信:作为 ROS 2 默认中间件,支撑节点间话题、服务、动作通信
- 自动驾驶系统:多传感器数据融合(LiDAR/相机/雷达),要求高吞吐低延迟
- 工业控制系统:PLC、SCADA 系统的实时数据分发,支持 QoS 保障
- 无人机集群:多机协同通信,动态发现新加入的无人机节点
- 军事/航空航天:符合 DDS-Security 标准,支持高安全性通信
- 金融交易系统:低延迟行情分发,支持多播与可靠传输
- 物联网边缘计算:边缘节点与云端的数据同步,支持跨网段通信
5.2 机器人系统中的实际应用
· 传感器数据流:LiDAR 点云、相机图像使用 BEST_EFFORT + KEEP_LAST(5)
· 控制指令:运动指令使用 RELIABLE + KEEP_LAST(1)
· 状态同步:机器人状态使用 TRANSIENT_LOCAL,晚加入的示教器可获取最后状态
· 告警信息:使用 DEADLINE QoS,超时未收到即触发告警
· 跨网段:通过 TCP 传输跨网段连接远程示教器
六、相比其他中间件的优缺点
6.1 与主流通信中间件对比
| 维度 | FastDDS | ZeroMQ | gRPC | MQTT |
|---|---|---|---|---|
| 通信模型 | 发布/订阅 (动态发现) | 多种模式 (需手动连接) | 请求/响应 + 流 | 发布/订阅 (需 Broker) |
| 动态发现 | ✅ 内置 | ❌ 需手动配置 | ❌ 需服务发现 | ❌ 依赖 Broker |
| QoS 保障 | ✅ 22 种策略 | ❌ 仅基础 | ❌ 仅超时 | ❌ 仅 3 个级别 |
| 实时性 | ✅ 软实时 | ✅ 较好 | ⚠️ 一般 | ❌ 较差 |
| 跨语言 | C++/Python/Java | 多语言 | 多语言 | 多语言 |
| Broker 依赖 | ❌ 去中心化 | ❌ 去中心化 | ✅ 需服务端 | ✅ 需 Broker |
| 互操作标准 | ✅ DDS-RTPS | ❌ 无 | ✅ Protobuf | ✅ MQTT 标准 |
| 复杂度 | 中等 | 低 | 中等 | 低 |
6.2 FastDDS 的优势
② 动态发现:节点自动发现,热插拔无需重启
③ 丰富 QoS:22 种策略精细化控制通信行为
④ 标准 RTPS:不同厂商 DDS 可互操作
⑤ 零拷贝:共享内存传输,极致性能
⑥ ROS 2 集成:作为默认中间件,生态完善
6.3 FastDDS 的劣势
② 配置复杂:QoS 组合多,调优需要经验
③ 资源占用:相比 ZeroMQ,内存与 CPU 占用更高
④ 跨网段需配置:默认多播,跨网段需配置 Initial Peers
⑤ 调试工具少:相比 ROS 1 的 rqt_graph,DDS 层调试工具较少
七、相比其他 DDS 实现的优缺点
7.1 主流 DDS 实现对比
| 维度 | FastDDS | Cyclone DDS | OpenDDS | RTI Connext |
|---|---|---|---|---|
| 开发方 | eProsima | Eclipse Foundation | OCI | RTI |
| 开源协议 | Apache 2.0 | EPL 2.0 | BSD-like | ❌ 商业 |
| ROS 2 支持 | ✅ 默认 | ✅ 可选 | ⚠️ 社区 | ✅ 商业支持 |
| RTPS 互操作 | ✅ 原生 | ✅ 原生 | ⚠️ 需配置 | ✅ 原生 |
| 性能 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 共享内存 | ✅ 内置 | ✅ 支持 | ⚠️ 需扩展 | ✅ 内置 |
| 安全模块 | ✅ 开源 | ✅ 开源 | ✅ 开源 | ✅ 商业 |
| Python 绑定 | ✅ 官方 | ⚠️ 社区 | ⚠️ 社区 | ✅ 商业 |
| 文档完善度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
7.2 FastDDS 相比其他 DDS 的优势
② 性能领先:在多次 ROS 2 中间件性能测评中吞吐量与延迟指标领先
③ Apache 2.0 协议:商业友好,无 GPL 传染风险
④ 共享内存零拷贝:内置 SHM 传输,单机性能极致
⑤ 文档详尽:官方文档与示例丰富,上手快
⑥ Python 绑定:官方维护的 Python 绑定,支持快速原型开发
7.3 FastDDS 相比其他 DDS 的劣势
② 实时性认证:相比 RTI Connext Professional,缺乏硬实时认证
③ 工具链:RTI 提供更完善的监控、记录、回放工具
④ Cyclone DDS 兼容性:部分 ROS 2 场景下 Cyclone DDS 表现更稳定
⑤ 配置复杂度:相比 Cyclone DDS,XML 配置项更多
八、最小 FastDDS 通信全流程示例
8.1 项目结构
fastdds-demo/ ├── CMakeLists.txt ├── idl/ │ └── HelloWorld.idl # 数据类型定义 ├── publisher/ │ └── publisher.cpp # 发布者 ├── subscriber/ │ └── subscriber.cpp # 订阅者 └── scripts/ └── fastddsgen.sh # IDL 代码生成脚本
8.2 定义 IDL 数据类型
// idl/HelloWorld.idl struct HelloWorld { unsigned long index; string message; };
8.3 生成类型支持代码
# 使用 fastddsgen 工具生成 C++ 代码 fastddsgen idl/HelloWorld.idl # 生成文件: # HelloWorld.h / HelloWorld.cpp (数据类型) # HelloWorldPubSubTypes.h / .cpp (序列化/反序列化) # HelloWorldTypeSupport.h (类型支持注册)
7.4 发布者完整实现
// publisher/publisher.cpp #include "HelloWorldPubSubTypes.h" #include <fastdds/dds/domain/DomainParticipantFactory.hpp> #include <fastdds/dds/publisher/Publisher.hpp> #include <fastdds/dds/publisher/DataWriter.hpp> using namespace eprosima::fastdds::dds; int main() { // ① 创建 DomainParticipant DomainParticipantQos participant_qos; participant_qos.name("PublisherParticipant"); DomainParticipant* participant = DomainParticipantFactory::get_instance()->create_participant(0, participant_qos); // ② 注册数据类型 TypeSupport type_support(new HelloWorldPubSubType()); type_support.register_type(participant); // ③ 创建 Topic Topic* topic = participant->create_topic( "HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT); // ④ 创建 Publisher Publisher* publisher = participant->create_publisher(PUBLISHER_QOS_DEFAULT); // ⑤ 创建 DataWriter (配置 QoS) DataWriterQos writer_qos; writer_qos.reliability().kind = RELIABLE_RELIABILITY_QOS; writer_qos.durability().kind = TRANSIENT_LOCAL_DURABILITY_QOS; writer_qos.history().kind = KEEP_LAST_HISTORY_QOS; writer_qos.history().depth = 10; DataWriter* writer = publisher->create_datawriter(topic, writer_qos); // ⑥ 发布数据 HelloWorld data; for (unsigned long i = 0; i < 100; ++i) { data.index(i); data.message("Hello from FastDDS #" + std::to_string(i)); writer->write(&data); std::cout « "Published: " « data.message() « std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(500)); } // ⑦ 清理资源 publisher->delete_datawriter(writer); participant->delete_publisher(publisher); participant->delete_topic(topic); DomainParticipantFactory::get_instance()->delete_participant(participant); return 0; }
7.5 订阅者完整实现
// subscriber/subscriber.cpp #include "HelloWorldPubSubTypes.h" #include <fastdds/dds/subscriber/Subscriber.hpp> #include <fastdds/dds/subscriber/DataReader.hpp> #include <fastdds/dds/subscriber/SampleInfo.hpp> using namespace eprosima::fastdds::dds; // 自定义监听器 class HelloWorldListener : public DataReaderListener { public: void on_data_available(DataReader* reader) override { SampleInfo info; HelloWorld data; while (reader->take_next_sample(&data, &info) == RETCODE_OK) { if (info.valid_data) { std::cout « "Received: index=" « data.index() « ", message=" « data.message() « std::endl; } } } }; int main() { // ① 创建 DomainParticipant DomainParticipant* participant = DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT); // ② 注册数据类型 TypeSupport type_support(new HelloWorldPubSubType()); type_support.register_type(participant); // ③ 创建 Topic Topic* topic = participant->create_topic( "HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT); // ④ 创建 Subscriber Subscriber* subscriber = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT); // ⑤ 创建 DataReader (QoS 必须与 Writer 兼容) DataReaderQos reader_qos; reader_qos.reliability().kind = RELIABLE_RELIABILITY_QOS; reader_qos.durability().kind = TRANSIENT_LOCAL_DURABILITY_QOS; reader_qos.history().kind = KEEP_LAST_HISTORY_QOS; reader_qos.history().depth = 10; HelloWorldListener listener; DataReader* reader = subscriber->create_datareader( topic, reader_qos, &listener); // ⑥ 等待数据 (运行 60 秒后退出) std::this_thread::sleep_for(std::chrono::seconds(60)); // ⑦ 清理资源 subscriber->delete_datareader(reader); participant->delete_subscriber(subscriber); participant->delete_topic(topic); DomainParticipantFactory::get_instance()->delete_participant(participant); return 0; }
7.6 CMakeLists.txt 构建配置
cmake_minimum_required(VERSION 3.16) project(FastDDSDemo) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 查找 FastDDS find_package(fastcdr REQUIRED) find_package(fastrtps REQUIRED) # IDL 生成的源文件 set(IDL_SOURCES idl/HelloWorld.cxx idl/HelloWorldPubSubTypes.cxx ) # 发布者 add_executable(publisher publisher/publisher.cpp ${IDL_SOURCES} ) target_link_libraries(publisher fastrtps fastcdr) # 订阅者 add_executable(subscriber subscriber/subscriber.cpp ${IDL_SOURCES} ) target_link_libraries(subscriber fastrtps fastcdr)
7.7 运行流程
# 终端 1:启动订阅者 ./subscriber # 终端 2:启动发布者 ./publisher # 输出示例: # Published: Hello from FastDDS #0 # Received: index=0, message=Hello from FastDDS #0 # Published: Hello from FastDDS #1 # Received: index=1, message=Hello from FastDDS #1
① 定义 IDL → ② fastddsgen 生成代码 → ③ 创建 Participant → ④ 注册 Type → ⑤ 创建 Topic → ⑥ 创建 Publisher/Subscriber → ⑦ 创建 DataWriter/DataReader(配置 QoS) → ⑧ Writer.write() / Reader Listener 回调 → ⑨ 清理资源
八、总结
8.1 FastDDS 核心优势总结
② 性能卓越:共享内存零拷贝,高吞吐低延迟
③ 动态发现:去中心化,节点自动发现,热插拔
④ 丰富 QoS:22 种策略精细化控制通信行为
⑤ ROS 2 集成:作为默认中间件,生态完善
⑥ 开源友好:Apache 2.0 协议,商业可用
8.2 选型建议
| 场景 | 推荐中间件 | 理由 |
|---|---|---|
| ROS 2 机器人开发 | FastDDS | 默认中间件,生态最完善 |
| 高性能实时通信 | FastDDS 或 RTI Connext | QoS 丰富,零拷贝传输 |
| 简单消息队列 | ZeroMQ / MQTT | 轻量级,部署简单 |
| 跨语言 RPC 调用 | gRPC | 强类型 Protobuf,多语言支持好 |
| 商业级关键系统 | RTI Connext | 企业级支持,硬实时认证 |
8.3 结语
FastDDS 作为 ROS 2 的默认通信中间件,在机器人、自动驾驶、工业控制等领域展现出强大的生命力。其基于 DDS 标准的设计保证了互操作性,丰富的 QoS 策略满足了各种复杂场景的需求,共享内存零拷贝技术提供了极致的性能。对于需要高性能、高可靠、可扩展通信的分布式系统,FastDDS 是一个值得深入学习和使用的优秀中间件。