ROS 2 QoS到底是什么?Reliability、Durability、History、Deadline一次讲清楚
在上一篇文章中,我们详细讨论了 ROS 2 中 Topic、Service 和 Action 三种通信机制。
很多人在第一次接触 ROS 2 时,会发现一个非常特别的概念:QoS(Quality of Service,服务质量)。
在传统的软件开发中,我们发送一条消息,通常只需要关心“消息有没有发出去”和“对方有没有收到”。
但机器人系统并不是一个普通的软件通信系统。
一台移动机器人上的摄像头可能每秒产生几十帧图像,激光雷达可能持续输出扫描数据,IMU 可能以数百 Hz 甚至更高频率输出数据,而关节控制系统则可能要求严格按照固定周期运行。
这些数据对于系统而言,并不具有完全相同的重要性。
例如:
-
一帧已经过时的摄像头图像,还要不要?
-
丢失一帧激光雷达数据是否可以接受?
-
关节状态数据应该保留多少条?
-
一个控制命令如果延迟 100 ms 才到达,还有没有意义?
-
一个新启动的节点,是否需要拿到之前已经发布的数据?
-
消息发送失败后,是应该重传,还是直接丢弃?
-
一个周期性数据如果长时间没有到达,系统应该怎么办?
这些问题实际上都属于通信服务质量问题。
这也是 ROS 2 与 ROS 1 在设计理念上的一个重要区别。
ROS 2 底层通信建立在 DDS(Data Distribution Service)等中间件机制之上,通过 QoS 对数据传输行为进行更加精细的控制。
因此,如果真正想理解 ROS 2 的通信机制,不能只停留在:
Publisher 发布消息 → Subscriber 接收消息。
还需要继续往下理解:
ROS 2 → DDS → QoS → 网络传输 → Executor → Callback → Linux调度。
更重要的是,QoS解决的是“数据怎么传”的问题,而实时操作系统解决的是“任务什么时候运行”的问题。
这两个问题看起来相似,实际上属于不同层次。
本文就从 QoS 开始,把 ROS 2 中最重要的 Reliability、Durability、History、Depth、Deadline 等机制一次讲清楚,并进一步分析:
为什么 QoS 配置得再好,也不等于机器人系统就具备真正的实时性?
一、ROS 2为什么需要QoS?机器人通信并不是“消息到了就行”
理解 QoS 最简单的方法,是先假设我们正在设计一台移动机器人。
机器人上有:
-
摄像头;
-
激光雷达;
-
IMU;
-
GPS;
-
轮速计;
-
电机控制器;
-
关节状态;
-
路径规划模块;
-
定位模块;
-
控制模块。
这些模块之间都需要通信。
例如:
摄像头
│
▼
Camera Node
│
│ Image
▼
视觉算法 Node
│
▼
目标检测
│
▼
路径规划
│
▼
运动控制
再比如:
IMU ───────────────┐
│
LiDAR ─────────────┼──> 定位 Node
│
Wheel Odometry ────┘
│
▼
Robot Pose
│
▼
Path Planner
│
▼
Controller
表面看起来,这只是几个 Publisher 和 Subscriber。
但是,如果进一步思考,就会发现不同数据对于通信的要求完全不同。
例如摄像头:
Frame 100
Frame 101
Frame 102
Frame 103
Frame 104
如果 Frame 101 丢失了,系统通常可以继续处理 Frame 102。
但对于某些控制命令:
停止
前进
停止
如果“停止”命令发生异常延迟,问题可能就完全不同。
因此,机器人通信不能简单地使用一种统一的传输策略。
这就是 QoS 存在的意义。
QoS 本质上是在定义:某一种数据,在通信过程中应该遵守什么规则。
例如:
数据必须可靠到达吗?
数据允许丢失吗?
新加入的节点需要历史数据吗?
数据最多保存多少条?
多久没有收到数据就应该认为异常?
这些问题都可以通过 QoS 进行描述。
从架构上看,可以简单理解为:
ROS 2应用层
│
Publisher / Subscriber
│
▼
ROS 2 Middleware
│
▼
DDS / RMW
│
┌─────┴─────┐
│ QoS │
└─────┬─────┘
│
▼
网络 / 进程间通信
│
▼
接收端 Middleware
│
▼
Executor
│
▼
Callback
│
▼
Linux任务调度
│
▼
CPU
这个结构非常重要。
因为它告诉我们:
QoS 位于 ROS 2 通信链路中,但它并没有直接控制 CPU 上的任务什么时候获得执行机会。
这也是后面理解 ROS 2 实时性的关键。
二、Reliability、Durability、History:QoS最核心的三个概念
ROS 2 QoS 有很多策略,但如果刚开始学习,最值得优先理解的是:
Reliability、Durability、History。
它们分别回答三个不同的问题:
Reliability:消息要不要尽可能可靠地送达?
Durability:新加入的节点要不要看到过去的数据?
History:过去的数据到底保存多少?
1. Reliability:数据到底要不要“可靠送达”?
Reliability 主要有两种常见策略:
BEST_EFFORT
RELIABLE
BEST_EFFORT
Best Effort 可以简单理解为:
能发就发,丢了就丢了。
它不强调一定要把每一条数据送到。
例如:
Publisher
Frame 1 ─────────────> Subscriber
Frame 2 ─────────────> Subscriber
Frame 3 ─────X
Frame 4 ─────────────> Subscriber
Frame 5 ─────────────> Subscriber
Frame 3 丢失。
Subscriber 不会为了 Frame 3 一直等待,也不会要求发送方重新发送。
这种机制非常适合某些高频传感器数据。
比如摄像头:
30 FPS
Frame 1
Frame 2
Frame 3
Frame 4
Frame 5
...
如果偶尔丢一帧,后面的新帧很快就会到达。
相比于:
为了保证 Frame 3,一直等待重传。
很多情况下,系统更希望:
Frame 3 不要了,直接处理 Frame 4。
这就是 Best Effort 的典型使用场景。
RELIABLE
Reliable 则更加关注:
消息应该可靠地交付。
如果发生丢包,底层通信机制可能采取确认、重传等机制来保证可靠传输。
可以简单理解为:
Frame 1 ─────────────> 收到
Frame 2 ─────────────> 收到
Frame 3 ─────X
│
└── 重传
│
▼
收到 Frame 3
这样做的好处很明显:
消息可靠性更高。
但代价同样明显:
-
可能产生额外通信开销;
-
可能产生重传;
-
可能增加队列压力;
-
网络异常时可能产生等待;
-
在高负载场景下可能增加系统复杂度。
因此:
Reliable 并不意味着一定更适合机器人实时控制。
这是学习 ROS 2 QoS 时非常容易出现的误区。
很多人会自然地认为:
Reliable 比 Best Effort 更可靠,所以实时系统应该全部使用 Reliable。
实际上并不是这样。
假设一个机器人控制器每 1 ms 更新一次:
T0 T1 T2 T3 T4
│ │ │ │ │
CMD1 CMD2 CMD3 CMD4 CMD5
如果 CMD2 因网络问题迟迟没有到达。
此时系统到底应该:
等待 CMD2
还是:
CMD2已经过时
直接处理最新命令 CMD3
需要结合具体业务判断。
这说明:
可靠性和实时性不是同一个指标。
Reliability 解决的是:
“消息有没有可靠地传递。”
实时性更关注:
“任务有没有在规定时间内完成。”
二者不能简单画等号。
2. Durability:新加入的节点要不要看到过去的数据?
第二个非常重要的 QoS 策略是 Durability。
它主要解决一个问题:
Subscriber 在 Publisher 已经发布过一些数据之后才启动,它要不要看到之前的数据?
最容易理解的例子是:
地图 Map
假设系统已经生成了一张地图:
Map Node
│
└──────> /map
地图发布以后,导航节点才启动。
如果地图只在过去发布过一次,那么新启动的导航节点还能不能拿到地图?
这时候 Durability 就很重要。
常见策略包括:
VOLATILE
TRANSIENT_LOCAL
VOLATILE
Volatile 可以理解为:
我只关心现在的数据,过去的数据不负责保存给后来者。
例如:
Publisher
│
├── Data 1
├── Data 2
├── Data 3
│
▼
Subscriber后来才启动
Subscriber 通常不会因为自己晚启动,就自动获得之前的数据。
这种模式非常适合持续变化的数据。
例如:
IMU
Camera
LiDAR
因为这些数据本身就是连续产生的。
TRANSIENT_LOCAL
Transient Local 则可以理解为:
Publisher 在本地保留一定的数据状态,让后来加入的 Subscriber 有机会获得。
例如:
Map Publisher
发布 Map
│
▼
保存状态
│
▼
Navigation Node启动
│
▼
获得之前的Map
对于地图、配置、状态类数据,这种机制可能更加有意义。
这里需要注意:
Durability 并不等于数据库。
它解决的是 DDS/ROS 2 通信层面的数据持久性策略,而不是把数据永久存储到磁盘。
三、History、Depth:为什么ROS 2消息队列不能无限增长?
再来看 History。
机器人系统里面有一个非常典型的问题:
如果消息产生得比处理得快,会发生什么?
例如:
Publisher:1000 Hz
Subscriber处理速度:500 Hz
那么:
产生:
1000条/s
处理:
500条/s
每秒就会多出约 500 条待处理数据。
如果无限保存:
Queue
│
├── Message 1
├── Message 2
├── Message 3
├── ...
├── Message 10000
├── Message 10001
└── Message 10002
最终系统的内存和处理压力都会不断增加。
因此 QoS 中需要定义:
历史数据到底保存多少?
这就是 History 和 Depth 的意义。
1. KEEP_LAST
KEEP_LAST 是机器人系统里非常常见的一种策略。
例如:
Depth = 10
可以简单理解为:
最多保留最近的 10 条消息。
如果:
Message 1
Message 2
...
Message 10
队列已经满了。
此时来了:
Message 11
那么旧的数据可能被淘汰:
Message 2
Message 3
...
Message 11
这样系统始终保持一个有限长度的消息队列。
对于实时机器人系统而言,这种设计非常重要。
因为很多机器人控制数据具有明显的“新鲜度”。
例如:
cmd_vel
0.0 m/s
0.1 m/s
0.2 m/s
0.3 m/s
0.4 m/s
假设控制器已经处理到了:
0.4 m/s
这时候再去处理几百毫秒之前的:
0.1 m/s
很多情况下并没有意义。
因此,对于部分实时控制数据:
最新数据可能比完整历史数据更重要。
2. KEEP_ALL
KEEP_ALL 则意味着:
尽可能保留所有历史消息。
听起来很安全,但代价也很明显。
如果生产速度长期高于消费速度:
Producer
1000 Hz
Consumer
500 Hz
↓
Queue不断增长
最终会造成:
-
内存压力;
-
调度压力;
-
延迟增加;
-
系统资源竞争。
因此,QoS 的设计本质上也是一种:
资源管理策略。
四、Deadline、Lifespan、Liveliness:QoS开始进入“实时系统”领域
前面讲的 Reliability、Durability、History 更多是在解决:
消息应该如何传递和保存。
而 Deadline、Lifespan、Liveliness 则开始涉及:
数据是不是按预期产生?
这时候,QoS 就开始与机器人实时系统产生更强的联系。
1. Deadline:数据多久没有更新,就算异常?
假设 IMU 应该:
100 Hz
那么理论上:
每10ms产生一次数据
正常情况:
T0
↓ 10ms
T1
↓ 10ms
T2
↓ 10ms
T3
如果突然变成:
T0
↓ 10ms
T1
↓ 10ms
T2
↓ 100ms
T3
那么系统就应该意识到:
IMU 数据是不是出现异常?
Deadline 就可以用于描述这种通信周期要求。
例如:
Deadline = 10ms
意味着:
数据发布之间不应该超过这个预期时间。
如果超过,就可能触发对应的 QoS 事件机制,让系统知道:
数据更新超时
这对于机器人系统很有价值。
比如:
IMU
│
▼
状态估计
│
▼
控制器
如果 IMU 长时间没有数据:
IMU ─────X──────> 状态估计
控制系统就不能继续假设:
“数据一定正常。”
它需要进入异常处理逻辑。
所以 Deadline 可以帮助系统回答:
数据有没有按照预期的节奏出现?
2. Lifespan:消息过期之后还有没有意义?
Lifespan 解决的是另一个问题:
一条消息最多允许“活”多久?
例如:
cmd_vel
假设一条速度控制指令:
前进 0.5 m/s
发布之后经过:
1ms
可能仍然有效。
但如果因为系统阻塞:
500ms
之后才真正处理这条消息。
那么这个控制命令可能已经过时。
因此对于一些具有明显时间敏感性的数据:
Message
│
├── 10ms:有效
├── 20ms:可能有效
├── 50ms:开始过期
└── 500ms:基本失去实时意义
Lifespan 可以帮助定义:
超过生命周期之后,这条消息不再应该被认为是有效数据。
这与实时控制系统的设计非常相关。
因为实时系统不是简单追求:
“每条数据都不能丢。”
而是需要考虑:
数据到了的时候,它是不是还具有使用价值?
3. Liveliness:对方还活着吗?
机器人系统还有一个经常被忽视的问题:
一个节点到底还活着吗?
例如:
Controller Node
│
▼
Motor Driver
如果 Controller Node 崩溃了:
Controller
X
下游系统是否能够及时发现?
Liveliness 可以用于描述通信参与者的“存活状态”。
这类机制对于:
-
故障检测;
-
节点健康状态;
-
分布式机器人系统;
-
安全机制;
都有一定意义。
但同样要强调:
Liveliness 是通信层面的存活状态,不等于整个机器人控制链路的安全认证。
真正的机器人安全系统还需要结合:
-
看门狗;
-
故障检测;
-
安全状态机;
-
硬件保护;
-
控制器保护;
-
独立安全机制。
五、QoS配置好了,ROS 2就实时了吗?真正的关键还在Executor和操作系统
到这里,我们已经可以把 ROS 2 QoS 的核心逻辑串起来。
可以把一个机器人数据链路抽象成:
ROS 2
│
┌───────────┴───────────┐
│ │
Publisher Subscriber
│ │
▼ ▲
QoS QoS
│ │
└────────── DDS ────────┘
│
▼
数据传输
│
▼
Executor
│
▼
Callback
│
▼
Linux Scheduler
│
▼
CPU
这里最容易产生一个错误认识:
QoS配置得越严格,系统就越实时。
实际上,这是不成立的。
例如:
Reliable
+
Deadline
+
Keep Last
并不能自动保证:
Callback一定在1ms内执行
为什么?
因为 QoS 主要解决的是:
数据通信层的问题。
而 Callback 到底什么时候获得 CPU,需要经过:
Executor + Thread + Linux Scheduler + CPU。
假设:
T0:消息到达
T1:DDS收到消息
T2:Executor发现可执行Callback
T3:Callback进入等待执行状态
T4:Linux调度器选择线程
T5:线程真正获得CPU
T6:Callback开始执行
那么:
实时响应延迟
≈
通信延迟
+
Executor调度延迟
+
线程调度延迟
+
CPU资源竞争
+
Callback执行时间
即使:
通信延迟 = 0.1ms
如果后面发生:
Executor排队
+
高优先级任务抢占
+
CPU竞争
+
中断干扰
+
锁等待
最终:
Callback实际执行时间
仍然可能远远超过预期。
因此:
QoS ≠ 实时性。
更准确地说:
QoS 是 ROS 2 实时系统设计的重要组成部分,但它无法单独决定整个系统的实时确定性。
这也是为什么当 ROS 2 开始进入:
-
机械臂控制;
-
人形机器人运动控制;
-
移动机器人底盘控制;
-
无人系统;
-
工业机器人;
-
高速运动控制;
这些场景之后,开发者最终都会逐渐从:
“ROS 2通信是否正常?”
继续深入到:
“Callback到底什么时候执行?”
再继续深入到:
“Linux线程什么时候获得CPU?”
最后进入:
“操作系统能不能提供确定性的调度和资源隔离?”
这就从 ROS 2 的通信问题,进入了实时操作系统问题。
一个典型的机器人控制链路
假设一台机械臂控制周期为:
1ms
那么整个链路可能是:
关节编码器
│
▼
Joint State
│
▼
ROS 2
│
▼
Controller
│
▼
read-update-write
│
▼
Motor Driver
理想状态下:
1ms
│
├── 数据采集
├── 数据传输
├── Callback触发
├── 控制算法
├── 输出控制命令
└── 周期结束
如果其中任何一个环节出现不可控延迟:
通信延迟
Executor延迟
线程调度延迟
锁竞争
中断
CPU竞争
就可能变成:
周期1:0.8ms
周期2:0.9ms
周期3:1.1ms
周期4:0.8ms
周期5:2.4ms
平均值看起来可能并不严重。
例如:
平均延迟:0.9ms
但是对于实时控制来说,更值得关注的是:
Worst Case Latency
也就是:
最坏情况下到底会延迟多少?
这也是实时系统和普通软件系统思维方式上的重要区别。
普通应用可能更加关注:
平均性能
吞吐量
平均响应时间
而实时控制系统通常更加关注:
最大延迟
抖动
周期确定性
任务优先级
资源隔离
因此,当 ROS 2 应用进入高实时性场景之后,仅仅优化 QoS 往往是不够的。
还需要继续分析:
ROS 2通信
↓
DDS
↓
Executor
↓
Callback Group
↓
线程
↓
Linux Scheduler
↓
CPU
↓
中断 / IRQ
↓
硬件
这条链路上的任何一个环节,都可能成为实时性的瓶颈。
QoS应该怎么选?不要追求“最强配置”,而应该根据数据类型设计
实际项目中,并不存在一个:
“所有 Topic 都应该使用的最佳 QoS。”
不同的数据应该使用不同策略。
例如可以进行这样的思考:
| 数据类型 | Reliability | History | 典型关注点 |
|---|---|---|---|
| 摄像头图像 | Best Effort常见 | Keep Last | 最新数据 |
| LiDAR | Best Effort常见 | Keep Last | 数据新鲜度 |
| IMU | 视场景选择 | Keep Last | 周期性、延迟 |
| Joint State | 视场景选择 | Keep Last | 最新状态 |
| 控制命令 | 根据系统设计 | Keep Last | 实时性、过期数据 |
| 地图 | Reliable常见 | 视场景选择 | 数据完整性 |
| 配置数据 | Reliable常见 | 视场景选择 | 新节点获取状态 |
| 状态/诊断 | 根据需求 | Keep Last | 异常检测 |
这里不能简单理解成:
某种数据永远必须采用某种 QoS。
真正合理的方法应该是:
从业务语义出发设计 QoS。
例如:
高频传感器数据
重点可能是:
新鲜
快速
允许一定丢包
不要积压大量历史数据
那么:
Best Effort
+
Keep Last
+
合理Depth
可能更加符合需求。
配置/状态数据
重点可能是:
可靠
新加入节点可以获取状态
那么:
Reliable
+
Transient Local
可能更加合适。
控制数据
重点则可能变成:
低延迟
数据新鲜
避免历史命令堆积
异常时及时发现
这时就需要进一步结合:
QoS
+
Executor
+
线程优先级
+
CPU隔离
+
实时调度
+
控制周期
一起设计。
这才是真正的机器人实时系统架构。
结语:QoS解决的是“数据怎么传”,实时操作系统解决的是“任务什么时候跑”
如果只记住本文几个最重要的结论,可以把它浓缩成下面这张图:
ROS 2
│
▼
┌──────────────┐
│ QoS │
└──────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
Reliability Durability History
│ │ │
└───────────┼───────────┘
│
▼
DDS
│
▼
消息到达
│
▼
Executor
│
▼
Callback
│
▼
Linux任务调度
│
▼
CPU
QoS解决的问题是:
数据应该以什么方式传递、保存和管理。
而实时操作系统解决的问题则更加底层:
任务什么时候执行、执行多久、能不能及时抢占、资源会不会互相干扰。
所以:
Reliable ≠ 实时
Deadline ≠ 实时
QoS优化 ≠ 操作系统实时化
真正的 ROS 2 实时系统,需要把通信、执行器、线程、调度器和 CPU 资源放在一起考虑。
对于普通机器人应用来说,ROS 2 + Linux 已经能够满足大量开发需求。
但当系统进入:
-
高速机械臂控制;
-
工业机器人;
-
人形机器人运动控制;
-
高实时底盘控制;
-
多轴同步控制;
-
高可靠嵌入式设备;
这类对低延迟、低抖动、确定性和资源隔离要求更高的场景时,开发者需要进一步关注底层实时操作系统。
对于这类系统,望获rtLinux可以作为底层实时操作系统的一种技术选择,将 ROS 2 上层的软件通信与底层实时调度、资源隔离能力结合起来。
但真正值得继续研究的问题已经不是:
“QoS参数怎么配置?”
而是:
一条 ROS 2 消息到达之后,到底是谁决定它什么时候执行?
这就要进入 ROS 2 的另一个核心机制:
Executor。
下一篇我们继续深入:
《ROS 2 Executor到底是什么?从回调函数到机器人任务调度机制》
从一个简单的 Callback 开始,一路分析到:
Node
↓
Callback
↓
Callback Group
↓
Executor
↓
Thread
↓
Linux Scheduler
↓
CPU
也正是在这里,ROS 2 的“软件框架”和“实时操作系统”真正开始发生深度交汇。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)