DDS
DDS是什么
DDS---Data Distribution Service----数据分发服务。
就是可以完成数据发送和接收的一套系统。
DDS需要使用一套统一的通信协议和API标准,DDS的通信协议和API标准由对象管理组(OMG, Object Management Group)发布和维护,OMG仅仅负责制定标准,而标准的实现则由其他服务提供商完成。换句话说,就是OMG只负责指定DDS的实现需要使用哪些协议,API接口要怎么实现,而生产DDS软件系统的事情交给服务提供商去完成。
基于DDS通信协议和标准开发的软件系统成为DDS中间件,比如fastDDS,OpenSplice DDS等。
话题,服务,动作和DDS
话题、服务、动作,他们底层通信的具体实现过程,都是靠DDS来完成的。
DDS在ros系统中的架构
ros中间件(MiddleWare)的作用
1,用于连接ros client lib和DDS中间件;
2,为了兼容不同厂家生产的DDS中间件;
DDS标准
什么是标准?
就比如男生要求当兵,身高标准必须达到168cm.
那么厂家需要开发DDS系统,开发出来的DDS系统必须满足什么要求。
统一DDS标准的目的
确保不同供应商根据DDS标准开发的DDS中间件能够实现互操作,并提供一致的通信行为和服务质量。
DDS中间件
DDS是一套中间件,它提供介于操作系统和应用程序之间的功能,使得组件之间可以互相通信。
ros2默认的DDS中间件
Fast DDS(eProsima Fast DDS) 目前已经被选为 Robot Operating System 2 (ROS 2) 系统的默认中间件并且被包含在最新发布版本Foxy Fitzroy中。
DDS中间件在系统的位置
DDS中间件提供介于操作系统和应用程序之间的功能。
ros如何兼容不同厂家生产的DDS中间件
ros使用了同OMG同样的方法,由ros提供商提供统一的标准(ros调用DDS的函数接口)。
如果DDS厂家需要让自己开发的DDS系统加入ROS社区,就需要按照ros提供的统一标准开发自己的DDS系统。这样,不管使用哪个厂家开发的DDS系统,只要符合ros标准,ros中间件(MiddleWare)都可以调用这个DDS的接口完成同样的功能。
DDS的作用
1,从4种通信模型来说:
2,DDS在分布式系统种的作用:
3,DDS跨语言,跨操作系统,跨处理器进行数据通信:
DDS可以完成不同语言实现的应用层可以在不同操作系统和不同处理器上进行数据通信。
DDS跨语言的原因
1,idl:
IDL(Interface Definition Language,接口定义语言)。
IDL 提供了一种与编程语言无关的标准化描述方式,明确定义了数据结构的名称、字段类型、顺序和嵌套关系。无论目标语言是 C++、Python 还是 Java,IDL 编译器都会根据同一份定义生成对应的类或结构体,确保数据的内存布局和语义一致。
2,序列化和反序列化:
DDS使用序列化和反序列化技术,实现应用层跨语言通信。
比如:
发送端使用C++发送一个结构体sensorData,序列化为字节流之后发送出去;
接收端使用python处理数据,那么python需要处理对应的sensorData的数据,就需要提前定义一个sensor类,接收到这些字节流之后,dds可以将这些字节流反序列化为一个sensorData对象
DDS跨操作系统的原因
DDS作为中间件,位于应用层和操作系统之间,屏蔽了底层操作系统的差异。应用层通过统一的API进行通信,而DDS处理底层操作系统的具体实现,比如线程管理、网络通信等。这样应用层开发者不需要关心不同操作系统的细节,只需调用DDS提供的接口。
数据为中心和消息为中心的中间件的区别
典型中间件
数据为中心 | 消息为中心 |
---|---|
- DDS(Data Distribution Service) - OPC UA(工业领域) - ROS 2(机器人) | - Kafka(分布式流平台) - RabbitMQ(消息队列) - MQTT(轻量级消息协议) |
DDS的原理
DDS 使用 Object Management Group (OMG) 定义的“接口描述语言 (IDL)”进行消息定义和序列化。
DDS通信模型
一般常见的通信模型有4种,点对点(PTP),Broker(中介),广播(Broadcast),数据为中心的模式。
DDS采用的是“数据为中心”通信模式。
数据为中心通信模式的优点
从而可以实现通信的对象只需要处理自己关心的数据,不需要像广播一样出个所有数据,大大节省内存,提高性能。
FastDDS中间件
FastDDS 大致可分为4层:
应用层
为上层开发者提供用户友好的 API。
DDS域层(DDS Domain层/FastDDS 层)
同一个网络分为域进行通信,同一个域中又分为各自的topic进行通信。
发布者-订阅者层
读者-写者层
读者-写者层是相对于发布者-订阅者层更底层的API。
历史记录
在RTPS协议中,Reader和Writer将有关Topic的数据保存在其关联的历史记录中。每个数据段都由一个变更表示,对应的实现是CacheChange_t
。
更改通过历史记录管理。读者和写者的历史是两种类型:
eprosima::fastrtps::rtps::WriterHistory
;eprosima::fastrtps::rtps::ReaderHistory
;
对于Writer来说,发送消息是往历史记录中添加变更;
对于Reader来说,新消息会被放入到历史记录中,只需要从历史记录读取消息即可。
问题:
1,变更CacheChange_t本质是什么?
2,发送消息是向历史记录中添加变更,那么消息被放入历史记录中之后,什么时候向订阅段发送,还是由订阅端自己从发送端的历史就中发现,然后读取到订阅端的历史记录中?
在Fast DDS的源码中,CacheChange_t
是一个C++类,封装了单次数据变更的完整信息,包括:
-
数据内容:实际传输的有效负载(payload)。
-
元数据:序列号、时间戳、写入者标识(GUID)、状态标记等。
-
生命周期管理:引用计数、内存分配/释放逻辑。
write和reader发送和订阅数据流程
对Writer而言
-
数据发布:当应用调用
write()
发送数据时,Writer会创建一个CacheChange_t
对象,将数据序列化后存入serializedPayload
。 -
历史记录管理:将该对象添加到
WriterHistory
中,等待发送给匹配的Reader。 -
可靠性控制:若配置了可靠传输(QoS
RELIABILITY=RELIABLE
),Writer会保留CacheChange_t
直到收到所有Reader的确认(ACK)。
对Reader而言
-
数据接收:从网络收到数据后,Reader解析生成对应的
CacheChange_t
对象,存入ReaderHistory
。 -
数据访问:应用通过
take_next_sample()
或read_next_sample()
从ReaderHistory
中获取CacheChange_t
,并反序列化为用户数据类型。 -
状态跟踪:通过
isRead
标记管理数据是否已被应用处理。
数据何时发送
将该对象添加到WriterHistory中,等待发送给匹配的Reader。Writer什么时候将历史记录中的数据发送给Reader?
Writer何时将历史记录中的数据发送给Reader主要取决于几个因素,包括传输模式(如可靠性和最佳努力传输)、网络状况、以及具体实现的内部逻辑。默认情况下,我们只考虑DDS的传输模式:
DDS默认使用UDP4(不同主机)和SHM(同一个主机)作为数据传输的方式。
-
最佳努力传输(Best-Effort Reliability):
- 在这种模式下,数据一旦被写入
WriterHistory
就会尽快发送出去,通常不等待任何确认。 - 这种模式适用于对实时性要求较高但允许一定程度的数据丢失的应用场景。
- 在这种模式下,数据一旦被写入
-
可靠传输(Reliable Reliability):
- 当配置为可靠传输时,Writer会保留
CacheChange_t
对象直到收到所有匹配的Reader的确认(ACK)消息。 - 如果某个Reader未能成功接收并确认某条消息,Writer会重新发送该消息直至接收到确认或达到重试次数上限。
- 可靠传输保证了更高的数据完整性,但可能会引入额外的延迟。
- 当配置为可靠传输时,Writer会保留
DDS默认的传输方式:
最佳努力传输(Best-Effort Reliability)。
-- 也就是数据的发送是由发布端主动将数据发送给订阅端,看整个发布-订阅系统,像是订阅,但是本质上还是由发布者主动将数据传输到订阅端,订阅端检测到有数据到达自己之后,接收数据转为变更(CacheChange_t)放入历史记录,等待从历史记录中取出进行反序列化。
问题:
为何ros2中默认的的传输方式是Reliable,而DDS默认的确实UDP,也就是Best-Effort?
历史记录持久化
默认情况下,Writer的历史在其生命周期以内可以被Reader访问。这意味着,一旦Writer退出,则其历史就没有了。但如果需要,你可以配置持久化,这使得即便Writer重启了,仍然可以维护早先的历史。
RTPS 层
RTPS(Real-Time Publish-Subscribe)协议是DDS(Data Distribution Service)的核心通信协议,它定义了如何在分布式系统中进行数据的发布和订阅。
由 OMG 联盟定义和维护,以确保不同 DDS 供应商应用程序之间可以互操作;
基于 TCP/UDP/IP 等传输协议,提供订阅/发布通道;
通道层
动态发现(dynamic discovery)
dynamic discovery的实现
- Participant Discovery Phase (PDP):在这个阶段,参与者互相通知彼此的存在。为了达到这个目的,每个参与者需要定时发送公告消息。公告消息通过根据domain_id计算得到的多播地址和端口发送。-----定时通知阶段
- Endpoint Discovery Phase (EDP):在这个阶段,Publisher和Subscriber互相确认。为此,参与者使用在PDP期间建立的通信通道,彼此共享有关其发布者和订阅者的信息。 该信息包含了Topic和数据类型。为了使两个端点匹配,它们的Topic和数据类型必须一致。 一旦发布者和订阅者匹配,他们就发送/接收数据了。---发现并确认阶段
endpoint--端点。
多播地址和端口的计算
为什么不同domain_id的域是隔离的
不同domain ID会计算出不同的多播地址和端口号,PDP阶段以及数据发送阶段都只会向同一个多播域内的主机发送数据。
问题:
1,同一个局域网内的DDS通信,不同domain id的主机之间会接收到各自的数据,然后再由DDS判断是否属于自己的域的数据,再过滤,还是不同域之间的主机根本不会相互发送数据?
2,同一个局域网内的同一个domain id计算出来的多播地址和端口号是一样的吗?
1, 多播组和端口隔离:
DDS默认通过 domain_id
动态计算多播地址和端口,同一个局域网内的同一个domain id计算出来的多播地址和端口号是一样的。
2,PDP隔离:
PDP阶段进行定时多播时,只会根据自己的domain id计算出来的多播地址和端口号的主机和程序发送数据。
-
公告范围:每个参与者定时向本域的同一组多播组发送自身存在的通知。
-
监听范围:参与者仅监听本域的同一组多播地址和端口,因此无法发现其他域的参与者。
双方因多播组和端口不同,完全感知不到对方的存在。
-- 不同域的主机之间既不会相互发送数据,也不会接收到双方的数据。
DDS数据传输的方式
在传输上,Fast-RTPS支持以下五种传输方式:
- UDPv4
- UDPv6
- TCPv4
- TCPv6
- SHM(Shared Memory)
默认的,当Participant
创建时,会自动的配置两个传输通道:
- SHM:用来与同一个机器上的参与者通信。
- UDPv4:同来与跨机器的参与者通信。
当然,开发者可以改变这个默认行为,通过C++接口或者XML配置文件都可以。
使用共享内存的原因
SHM要求所有参与者位于同一个系统上,它是借助了操作系统提供的共享内存机制实现。共享内存的好处是:支持大数据传输,减少了数据拷贝,并且也减少系统负载(大少)。因此通常情况下,使用SHM会获得更好的性能。使用SHM时,可以配置共享内存的大小。
使用udp跨主机通信的原因
DDS通信流程
DDS的运用场景
ros中间件--MiddleWare
为了能够在ROS2中使用一个DDS实现,需要一个ROS中间件(RMW软件包), 这个包需要利用DDS程序提供的API和工具实现ROS中间件的接口。
DDS和socket的区别
1,DDS属于中间件,位于操作系统系欸应用程序之间;socket属于操作系统层。
特性 | Socket API | DDS |
---|---|---|
架构 | TCP:点对点 UDP:点对点,广播,多播 | Publish-subscribe模型 |
平台独立 | 需要为不同硬件,操作系统和编程语言编写不同的代码 | 所有硬件,操作系统和编程语言使用相同的API |
发现 | 需要硬编码IP地址和端口号 | 动态发现,无需关注端点所在位置 |
类型安全 | 没有类型安全,应用需要将字节流转换成正确类型 | 强类型安全,write() 和read() 针对特定数据类型 |
通信行为定制 | 需要通过自定义的代码来实现 | 通过QoS策略来完成 |
互操作性 | 不支持 | 具有公认的互操作性的开放标准 |
DDS资料
作者讲的很详细,在此谢过。