DDS 中的 QoS(Quality of Service,服务质量) 是其最核心的特性之一,它允许开发者精确控制数据通信的行为,满足不同场景对可靠性、实时性、资源使用的差异化需求。
一、QoS 核心概念
什么是 QoS?
QoS = 一组可配置的策略参数,用于控制:
├── 数据可靠性(Reliability)
├── 传输实时性(Latency)
├── 资源使用(Resource limits)
├── 数据持久性(Durability)
└── 网络行为(Liveliness, Deadline 等)
QoS 设计哲学
传统通信:One size fits all(一刀切)
DDS QoS:为每个 Topic 量身定制通信契约
比喻:
- TCP = 可靠但慢(如快递,必达但慢)
- UDP = 快但不可靠(如短信,可能丢)
- DDS QoS = 灵活选择(急件用专机,普通件用快递)
二、QoS 策略分类(23+ 种)
按功能分类
| 类别 | 策略 | 作用 |
|---|---|---|
| 可靠性 | RELIABILITY | 可靠传输 vs 尽力传输 |
| 持久性 | DURABILITY | 数据是否保存给后加入者 |
| 实时性 | DEADLINE, LATENCY_BUDGET | 时效性保证 |
| 生命周期 | LIFESPAN, LIVELINESS | 数据/节点存活检测 |
| 资源控制 | HISTORY, RESOURCE_LIMITS | 缓存大小、队列深度 |
| 传输配置 | TRANSPORT_PRIORITY, OWNERSHIP | 优先级、所有权 |
三、核心 QoS 策略详解
1. RELIABILITY(可靠性)
控制数据是否保证送达。
cpp
// DDS 配置示例(伪代码)
ReliabilityQosPolicy reliability;
// 选项 1:可靠传输(RELIABLE)
reliability.kind = RELIABLE;
// 行为:类似 TCP,丢包会重传,保证送达
// 适用:关键指令、控制信号
// 选项 2:尽力传输(BEST_EFFORT)
reliability.kind = BEST_EFFORT;
// 行为:类似 UDP,不保证送达,低延迟
// 适用:传感器数据、视频流、高频数据
ROS 2 示例:
python
# Python 中设置 QoS
from rclpy.qos import QoSProfile, ReliabilityPolicy
qos = QoSProfile(
reliability=ReliabilityPolicy.RELIABLE, # 或 BEST_EFFORT
depth=10
)
publisher = node.create_publisher(String, '/cmd_vel', qos)
2. DURABILITY(持久性)
控制数据是否保存给"后加入"的订阅者。
场景:订阅者启动比发布者晚,能否收到之前的数据?
┌─────────────────────────────────────────┐
│ Durability: VOLATILE(易失) │
│ └── 不保存历史数据 │
│ └── 后加入者只能收到新数据 │
│ └── 默认设置,低资源占用 │
│ │
│ Durability: TRANSIENT_LOCAL(本地持久) │
│ └── 发布者保存历史数据 │
│ └── 后加入者能收到缓存的历史数据 │
│ └── 适用:配置参数、状态信息 │
│ │
│ Durability: TRANSIENT/PERSISTENT │
│ └── 数据持久化到存储(需外部服务) │
└─────────────────────────────────────────┘
3. HISTORY(历史记录)
控制缓存多少历史样本。
cpp
HistoryQosPolicy history;
// KEEP_LAST: 只保留最近 N 个样本(环形缓冲区)
history.kind = KEEP_LAST;
history.depth = 10; // 保留最近 10 条消息
// KEEP_ALL: 保留所有样本(直到资源限制)
history.kind = KEEP_ALL;
// 配合 RESOURCE_LIMITS 使用
实际效果:
订阅者处理速度慢于发布者时:
KEEP_LAST(depth=10):
发布:[1][2][3][4][5][6][7][8][9][10][11][12]
订阅:处理 [1]... 处理完时,收到 [3][4]...[12]
丢失 [2](缓冲区满了,最老的被覆盖)
KEEP_ALL:
发布:[1][2][3]...
订阅:处理 [1] 时,[2][3]... 排队等待
直到内存耗尽或 RESOURCE_LIMITS 触发
4. DEADLINE(截止期限)
检测数据是否按时到达。
cpp
DeadlineQosPolicy deadline;
deadline.period.sec = 1; // 期望每 1 秒收到一次数据
// 如果超过 1 秒没收到新数据,触发回调通知
应用场景:
- 心跳检测:机器人 1 秒必须发一次状态,否则认为失联
- 传感器监控:激光雷达 100ms 必须输出一帧
5. LIFESPAN(数据有效期)
控制数据的有效时间,过期自动丢弃。
cpp
LifespanQosPolicy lifespan;
lifespan.duration.sec = 5; // 数据 5 秒后过期
// 发布者发送数据后,如果订阅者 5 秒内没收到,数据失效
// 适用:实时性要求高的数据,旧数据无意义
6. LIVELINESS(活跃度)
检测节点是否存活。
┌─────────────────────────────────────────┐
│ AUTOMATIC(自动) │
│ └── DDS 自动管理,只要进程在就视为存活 │
│ │
│ MANUAL_BY_TOPIC(手动按话题) │
│ └── 应用层必须定期声明"我还活着" │
│ └── 适用:需要应用级心跳检测 │
│ │
│ MANUAL_BY_PARTICIPANT(手动按节点) │
│ └── 应用层为整个节点声明存活 │
└─────────────────────────────────────────┘
lease_duration: 声明间隔,超时则认为节点死亡
7. OWNERSHIP(所有权)
多发布者竞争同一 Topic 时的行为。
┌─────────────────────────────────────────┐
│ SHARED(共享) │
│ └── 多个发布者同时有效 │
│ └── 订阅者收到所有发布者的数据 │
│ └── 默认设置 │
│ │
│ EXCLUSIVE(独占) │
│ └── 只有"所有权强度"最高的发布者有效 │
│ └── 其他发布者被静默 │
│ └── 适用:主备切换、冗余设计 │
└─────────────────────────────────────────┘
四、ROS 2 常用 QoS 预设配置
ROS 2 提供了常用场景的 QoS 预设:
| 预设名称 | 特点 | 适用场景 |
|---|---|---|
qos_profile_sensor_data |
尽力传输、小队列、低延迟 | 传感器数据(激光、图像) |
qos_profile_parameters |
可靠、持久、大队列 | 参数配置 |
qos_profile_default |
可靠、易失、中等队列 | 一般通信 |
qos_profile_services_default |
可靠、易失 | 服务调用 |
qos_profile_parameter_events |
可靠、持久 | 参数变更事件 |
qos_profile_system_default |
使用 DDS 默认设置 | 底层系统 |
python
# 使用预设
from rclpy.qos import qos_profile_sensor_data
publisher = node.create_publisher(
LaserScan,
'/scan',
qos_profile_sensor_data # 传感器优化配置
)
五、QoS 兼容性规则
关键原则:发布者和订阅者的 QoS 必须兼容才能建立连接。
兼容性矩阵
| 发布者 QoS | 订阅者 QoS | 是否兼容 | 实际行为 |
|---|---|---|---|
| RELIABLE | RELIABLE | ✅ | 可靠传输 |
| RELIABLE | BEST_EFFORT | ✅ | 降级为尽力传输 |
| BEST_EFFORT | RELIABLE | ❌ | 不兼容,无法连接 |
| VOLATILE | VOLATILE | ✅ | 无历史数据 |
| VOLATILE | TRANSIENT_LOCAL | ❌ | 不兼容 |
| TRANSIENT_LOCAL | VOLATILE | ✅ | 订阅者不要历史,不给 |
| TRANSIENT_LOCAL | TRANSIENT_LOCAL | ✅ | 提供历史数据 |
兼容性口诀
"订阅者不能要求比发布者更高的可靠性"
"订阅者不能要求发布者没有的能力"
六、实际应用示例
场景 1:机器人遥控指令
python
# 要求:绝对不能丢,但实时性次要
qos_cmd = QoSProfile(
reliability=ReliabilityPolicy.RELIABLE,
durability=DurabilityPolicy.VOLATILE,
depth=1, # 只保留最新指令
deadline=Deadline(deadline_period=Duration(seconds=0.5)) # 500ms 超时检测
)
场景 2:激光雷达数据
python
# 要求:高频、低延迟,丢几帧无所谓
qos_lidar = QoSProfile(
reliability=ReliabilityPolicy.BEST_EFFORT,
durability=DurabilityPolicy.VOLATILE,
history=HistoryPolicy.KEEP_LAST,
depth=5 # 保留最近 5 帧
)
场景 3:地图数据(大文件,后加入者需要)
python
# 要求:可靠、持久,新节点启动能收到地图
qos_map = QoSProfile(
reliability=ReliabilityPolicy.RELIABLE,
durability=DurabilityPolicy.TRANSIENT_LOCAL,
history=HistoryPolicy.KEEP_ALL,
lifespan=Lifespan(lifespan_duration=Duration(seconds=0)) # 永不过期
)
七、总结
| 核心概念 | 要点 |
|---|---|
| QoS 是什么 | 可配置的通信策略集合,精确控制数据行为 |
| 为什么重要 | 同一系统内不同数据有不同需求(控制指令 vs 传感器 vs 配置) |
| 关键策略 | RELIABILITY、DURABILITY、HISTORY、DEADLINE、LIVELINESS |
| 兼容性 | 发布者和订阅者 QoS 必须匹配,否则无法通信 |
| ROS 2 简化 | 提供预设配置,覆盖 90% 场景 |
QoS 是 DDS 区别于其他中间件的核心竞争力,它让 ROS 2 能同时满足实时控制 和大数据传输的矛盾需求,在同一系统中灵活配置。