Skip to content

DDS DCPS 规范:实体模型与 QoS 策略 ​

DCPS(Data-Centric Publish-Subscribe,以数据为中心的发布/订阅)规范是 DDS 规范族最初也是最核心的规范,在某些场合 DDS 规范指的就是 DCPS。本文整理 DCPS 的两块核心内容:实体模型(应用如何通过一组实体接入 DDS 通信,其中 DomainParticipant 是整个体系的入口)与 QoS 策略(DDS 1.4 定义的 22 种标准策略及其分组)。读完可以对 DDS 编程模型的整体结构建立认识,并掌握实体生命周期与 QoS 配置的常见实践。

DCPS 的内容构成 ​

DCPS 规范的内容分为三大块:

  • 模型描述:介绍模型中涉及的概念,即下文的实体模型与数据描述;
  • 接口描述:分模块定义类并定义接口形式及接口功能描述;
  • QoS 定义:定义 22 种 QoS 的功能及其配置方式。

实体模型 ​

DCPS 实体模型定义了三层实体(参与者层、发布/订阅/主题层、读写者层),层级关系如下:

各实体的职责:

  • Domain:代表一个通信平面,由 Domain ID 唯一标识,只有同一个域内的通信实体才可以通信。如果考虑车内通信,可以只划分 1 个 Domain,也可以按照交互规则或其他规则定义多个 Domain。
  • DomainParticipant:代表域内通信的应用程序的本地成员身份,可以理解为"某个进程(或应用实例)加入到一个 DDS 通信域后的代表对象"。它是 DDS 实体体系的根节点和入口,负责把应用纳入 DDS 的发现与通信机制中。
  • Topic:数据的抽象概念,由 TopicName 标识并关联相应的数据类型(DataType)。把域内所有 Topic 集合在一起,就形成一个虚拟的全局数据空间(Global Data Space),进一步弱化了节点的概念。
  • Publisher:发布者,发布主题数据,至少与 1 个 DataWriter 关联,通过调用 DataWriter 的相关函数将数据发出去。
  • Subscriber:订阅者,订阅主题数据,至少与 1 个 DataReader 关联。数据到达时,应用程序可能在忙于执行其他操作,也可能正在等待该消息,因此存在同步访问和异步通知两种方式。
  • DataWriter:数据写入者,类似缓存,把需要发布的主题数据从应用层写入 DataWriter。
  • DataReader:数据读取者,同样可以理解为一种缓存,从订阅者得到主题数据,随之传给应用层。

DomainParticipant:通信的入口 ​

没有 DomainParticipant,应用就无法接入 DDS 网络,无法进行服务发现,也无法收发数据。这一节展开它的定位、使用条件与工程实践。

域与参与者 ​

概念 定义 类比
Domain(域) 一个逻辑隔离的通信空间,由唯一的 domain_id 标识。不同 Domain 之间默认互不发现、互不通信。 房间号,不同的房间互不相通。
DomainParticipant 应用在某个 domain_id 中的具体实例。创建它意味着该进程正式"进入"了该房间。 进入房间的人,只有进了房间才能和其他人交流。

DomainParticipant 是应用接入 DDS 发现机制与通信机制的唯一凭证。

核心职责 ​

  • 身份标识与归属:绑定域 ID,声明"我属于哪个域";携带实现相关的本地信息(如 GUID、QoS 策略),供其他参与者发现和匹配。
  • 发现与资源管理的边界:DDS 的自动发现机制通常以 Participant 为粒度进行——对外广播自己的存在并监听其他 Participant 的加入;对内托管其下属所有 DDS 实体(Topic、Publisher、Subscriber、DataWriter、DataReader),可以视为 DDS 在该进程中的运行时上下文。
  • 工厂角色:Participant 充当工厂,负责创建 Topic、Publisher 和 Subscriber。
  • 生命周期管理:标准 DDS(RTI Connext、Fast DDS 等)要求先调用 delete_contained_entities()(或逐个删除子实体)之后,才能通过 DomainParticipantFactory::delete_participant() 删除参与者;直接删除仍含有子实体的 Participant 会失败(返回 RETCODE_PRECONDITION_NOT_MET)。部分实现的封装接口会代为自动清理,但不是规范行为。

通信建立的必要条件 ​

两个应用想要通过 DDS 成功通信,必须同时满足以下条件,其中第一条直接由 DomainParticipant 决定:

  1. 相同的 domain_id:如果两个 Participant 不在同一个 Domain ID 下,它们甚至无法互相"看见",更无法建立连接。
  2. Topic 匹配:Topic 名称相同,数据类型(Type)兼容。
  3. QoS 策略兼容:可靠性(Reliability)、历史深度(History)、截止时间(Deadline)等策略必须匹配。
  4. 网络与安全:网络可达(UDP/TCP 端口通畅);若启用 DDS Security,需通过身份认证和权限校验。

排查"收不到数据"的问题时,首先应检查双方是否创建了相同 domain_id 的 Participant。

一个进程需要几个 Participant ​

绝大多数场景下,一个应用程序进程只需要创建一个 DomainParticipant:

  • 资源开销小:发现协议流量、内置端点(Built-in Endpoints)、线程池、Socket 连接等资源通常按 Participant 分配。
  • 管理简单:配置集中,逻辑清晰。

仅在确有必要时,才在一个进程中创建多个不同 domain_id 或不同配置的 Participant:

  • 多域网关:进程需要同时作为桥梁,连接两个不同的逻辑域(例如 domain_id=0 和 domain_id=1)。
  • 强隔离需求:需要在同一进程内隔离不同的安全身份、完全不同的 QoS 配置或独立的发现组。

代价是资源消耗成倍增加(内存、线程、网络端口),配置复杂度与调试难度上升。除非有明确的多域互通或强隔离需求,坚持一个进程一个 Participant。

常见误区 ​

误区 正确理解
"Participant 就是发布/订阅者" 错。Participant 是容器;Publisher/Subscriber 才是具体的发布/订阅角色。
"一个进程只能有一个 Participant" 错。技术上允许创建多个,但通常不建议,除非为了多域隔离。
"Participant 对应物理机器" 错。它对应的是 DDS 通信栈的一个实例。一台机器上可以运行多个进程,每个进程都有自己的 Participant。
"它是业务逻辑对象" 错。它是基础设施对象,类似于网络编程中的 Socket 或 Context,而非业务实体。

数据描述:主题、实例与样本 ​

概念 说明 标识
主题 最大类,域内唯一的主题名称,并关联 1 个数据类型。 主题名+类型
实例 主题数据中 key 成员相同的数据的集合,即使用 key 成员进一步区分主题数据,可以理解成"子主题"。 InstanceHandle_t
样本 每次向 DDS 发送一次数据产生一个数据样本。 序列号

22 种标准 QoS 策略 ​

QoS(Quality of Service)即服务质量。DDS 规范中定义了 22 种标准 QoS 策略(DDS 1.4 规范 §2.2.3),用于精细控制数据通信的行为,涵盖可靠性、持久性、资源管理、发现机制、数据呈现等多个维度。这些策略可应用于不同的 DDS 实体(如 DomainParticipant、Publisher/Subscriber、DataWriter/DataReader、Topic),并支持匹配协商机制以确保通信双方兼容。

与数据传输行为相关的策略 ​

  1. Reliability(可靠性):控制是否保证数据送达:BEST_EFFORT(尽力而为)或 RELIABLE(可靠传输)。
  2. Durability(持久性):控制历史数据对晚加入订阅者的可用性:VOLATILE、TRANSIENT_LOCAL、TRANSIENT、PERSISTENT。
  3. History(历史记录):定义每个实例保留多少样本:KEEP_LAST(保留最近 N 个)或 KEEP_ALL(保留全部)。
  4. Deadline(截止时间):要求数据必须在指定周期内更新,否则触发事件。
  5. LatencyBudget(延迟预算):允许应用指定可接受的最大端到端传输延迟。
  6. Ownership(所有权):支持独占或共享数据写入权,避免多个发布者冲突。
  7. OwnershipStrength(所有权强度):配合 Ownership 使用,数值高的 DataWriter 获得写入优先权。
  8. Liveliness(活跃性):检测 DataWriter 是否"存活":AUTOMATIC、MANUAL_BY_PARTICIPANT、MANUAL_BY_TOPIC。
  9. TimeBasedFilter(基于时间的过滤):限制 DataReader 接收数据的最大频率(如每 100ms 最多一次)。

设置为 BEST_EFFORT 就一定不可靠吗?不一定。DDS 里定义的可靠性 QoS 指的是"是否需要 DDS 做额外的可靠保证",比如传输层使用 TCP 或其他底层协议已经保证了可靠传输,这时配置 BEST_EFFORT 同样可以实现可靠的传输。

设置为 RELIABLE 就一定可靠吗?同样不一定。可靠的实现方式是确认重传:在接收方确认之前,数据需要缓存在队列中以备可能的重传,而队列有长度限制(无论存储在内存还是文件中,都受限于存储空间)。当队列到达上限且需要发送新样本时:

  • 替换老的样本数据:被替换的数据丢包后无法重传,造成"丢包",无法实现可靠;
  • 阻塞等待队列有额外空间:可以实现"感知上"的可靠。

与资源管理与限制相关的策略 ​

  1. ResourceLimits(资源限制):限制缓存中最大样本数、实例数、每个实例的样本数等。
  2. Presentation(表示):控制同一 Publisher/Subscriber 下多个 DataWriter/DataReader 的数据呈现顺序和范围(INSTANCE、TOPIC、GROUP 级别)。
  3. Partition(分区):在 Domain 内创建逻辑子通道,只有相同 Partition 的发布/订阅才能通信。
  4. DestinationOrder(目标排序):控制 DataReader 接收样本的顺序:按源时间戳(BY_SOURCE_TIMESTAMP)或接收顺序(BY_RECEPTION_TIMESTAMP)。

与元数据与用户自定义信息相关的策略 ​

  1. UserData(用户数据):允许在 DomainParticipant、Publisher 等实体上附加任意二进制元数据。
  2. TopicData(主题数据):附加到 Topic 的元数据,传播给所有基于该 Topic 的读写器。
  3. GroupData(组数据):附加到 Publisher/Subscriber 的元数据,传播给其下属所有 DataWriter/DataReader。

与生命周期与发现机制相关的策略 ​

  1. EntityFactory(实体工厂):控制由工厂创建的实体在创建时是否自动启用(autoenable,默认启用)。
  2. WriterDataLifecycle(写者数据生命周期):控制 DataWriter 是否在删除时自动注销其实例。
  3. ReaderDataLifecycle(读者数据生命周期):控制 DataReader 是否自动清除未更新的实例。
  4. DurabilityService(持久化服务):配置当 Durability 为 TRANSIENT 或 PERSISTENT 时,外部持久化服务的行为(如清理延迟、历史深度等)。

其余标准策略 ​

  1. TransportPriority(传输优先级):为数据流设置网络传输优先级(依赖底层传输支持)。
  2. Lifespan(生命周期):指定数据样本的有效生存时间,超时后自动丢弃。

以上 22 种即 DDS 1.4 DCPS 规范定义的标准 QoS 全集。各扩展规范与具体实现还提供额外策略,如 XTypes 的 TypeConsistencyEnforcement(类型一致性强化)、部分实现中用于避免自收的 IgnoreLocal 配置等,均不计入这 22 种之内。

要点 ​

  • DCPS 是 DDS 规范族的核心,内容分为模型描述、接口描述、QoS 定义三大块
  • 实体模型分三层:DomainParticipant 是根节点和工厂,Publisher/Subscriber 各自关联 DataWriter/DataReader,Topic 关联数据类型
  • 通信建立的四个条件:相同 domain_id、Topic 与类型匹配、QoS 兼容、网络与安全可达——排查收不到数据先查 domain_id
  • 一个进程默认一个 Participant;多 Participant 仅用于多域网关与强隔离场景
  • 删除 Participant 前必须先 delete_contained_entities(),直接删除会返回 RETCODE_PRECONDITION_NOT_MET
  • 22 种标准 QoS 覆盖传输行为、资源管理、元数据、生命周期四个方向;BEST_EFFORT 与 RELIABLE 的实际可靠性都取决于传输层与队列容量
最近更新

基于 VitePress 构建