QoS 深入:Deadline 與 Liveliness
- ros2
- qos
- system-design
- dds
問題定義
第 2.2 節介紹的 Reliability、Durability、History 三個維度,解決的是「訊息會不會遺失、新加入者能不能拿到歷史資料」這類基本問題。但正式系統上線後,還有另一類問題這三個維度回答不了:Publisher 真的還活著嗎?它有沒有按照約定的頻率持續發布? 這一節介紹的 Deadline 與 Liveliness,是專門處理這類「連線本身是否健康」問題的 QoS 政策,在需要嚴格即時性保證的系統(例如安全相關的控制迴路)裡格外重要。
核心概念說明
Deadline:承諾的發布頻率
Deadline 政策讓你為一個主題設定一個時間期限,承諾「至少每隔這麼長時間,就會有一則新訊息」。如果 Publisher 沒有在期限內發布新訊息,系統會觸發 on_offered_deadline_missed(在 Publisher 端)或 on_requested_deadline_missed(在 Subscriber 端)事件回呼。
這解決了一個一般 QoS 三維度無法處理的問題:假設一個感測器節點因為某種原因卡住了(例如硬體驅動內部發生阻塞),但行程本身還活著、沒有真正當機,主題連線也還存在——單純看 Reliability/Durability 不會發現任何異常,因為技術上「連線」完全正常,只是沒有新資料進來而已。Deadline 提供了主動偵測「該來的資料沒有按時來」的機制。
Liveliness:Publisher 存活狀態的持續宣告
Liveliness 政策讓 Publisher 定期「宣告」自己還活著,Subscriber 端如果在約定的時間內沒有收到存活宣告,會判定這個 Publisher 已經失去存活訊號(lost liveliness),觸發對應的事件回呼。這在概念上類似心跳機制,但由 DDS 底層自動管理,不需要應用邏輯自己實作心跳訊息的收發。
Liveliness 有兩種宣告方式:AUTOMATIC(DDS 底層自動判斷,只要這個行程還在正常運作,所有該行程底下的 Publisher 都會被視為存活)跟 MANUAL_BY_TOPIC(需要應用程式明確呼叫 API 主動宣告,適合用來表達更細緻的「這個特定功能模組還健康」而不只是「行程還沒當機」)。
Lifespan:訊息的有效期限
Lifespan 政策則是為每則訊息設定一個有效期限,超過這個期限還沒被讀取的訊息,會被系統自動從佇列裡移除,即使 History 政策設定了足夠深的佇列容量。這在「過期的資料沒有意義、甚至有害」的場景很有用——例如一則指出「3 秒前偵測到障礙物在某個位置」的訊息,如果因為某種延遲累積導致 3 秒後才被讀取,這時候障礙物可能早已不在原本位置,讓下游邏輯基於這則過期資料做決策可能比完全沒有資料更危險。
實作範例:一個帶有 Deadline 與 Liveliness 監控的心跳主題
假設有一個安全相關的節點,需要確保另一個負責安全監控的節點持續正常運作,一旦對方停止發布心跳訊號,就要觸發緊急停止邏輯。
檔案位置:~/ros2_ws/src/my_package/my_package/safety_monitor.py
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile, ReliabilityPolicy
from rclpy.duration import Duration
from rclpy.qos_event import SubscriptionEventCallbacks
from std_msgs.msg import Empty
class SafetyMonitor(Node):
def __init__(self):
super().__init__("safety_monitor")
qos = QoSProfile(
reliability=ReliabilityPolicy.RELIABLE,
depth=1,
deadline=Duration(seconds=1.0),
liveliness_lease_duration=Duration(seconds=2.0),
)
event_callbacks = SubscriptionEventCallbacks(
deadline=self.on_deadline_missed,
liveliness=self.on_liveliness_changed,
)
self.subscription = self.create_subscription(
Empty, "heartbeat", self.on_heartbeat, qos, event_callbacks=event_callbacks
)
def on_heartbeat(self, msg: Empty):
self.get_logger().info("收到心跳訊號")
def on_deadline_missed(self, event):
self.get_logger().warn(f"心跳逾時!違規次數: {event.total_count}")
self.trigger_emergency_stop()
def on_liveliness_changed(self, event):
if event.alive_count == 0:
self.get_logger().error("Publisher 已失去存活訊號!")
self.trigger_emergency_stop()
def trigger_emergency_stop(self):
self.get_logger().error("觸發緊急停止邏輯")
def main():
rclpy.init()
node = SafetyMonitor()
try:
rclpy.spin(node)
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == "__main__":
main()
對應的 Publisher 端需要用相容的 QoS 設定發布心跳,並在自己這一側也可以選擇性監控 on_offered_deadline_missed:
qos = QoSProfile(
reliability=ReliabilityPolicy.RELIABLE,
depth=1,
deadline=Duration(seconds=1.0),
liveliness_lease_duration=Duration(seconds=2.0),
)
self.publisher_ = self.create_publisher(Empty, "heartbeat", qos)
self.timer = self.create_timer(0.5, self.publish_heartbeat)
預期輸出:正常情況
[INFO] [safety_monitor]: 收到心跳訊號
[INFO] [safety_monitor]: 收到心跳訊號
預期輸出:手動中斷 Publisher(例如按 Ctrl+C 終止心跳發布節點)之後
[WARN] [safety_monitor]: 心跳逾時!違規次數: 1
[ERROR] [safety_monitor]: Publisher 已失去存活訊號!
[ERROR] [safety_monitor]: 觸發緊急停止邏輯
常見錯誤與除錯技巧
錯誤一:設定了 Deadline,但 Subscriber 端從來沒有觸發過 on_deadline_missed,即使 Publisher 明顯已經停止發布
原因:常見是 Publisher 端跟 Subscriber 端各自設定了不相容的 Deadline 值(例如 Subscriber 要求 1 秒內要有新訊息,但 Publisher 端宣告自己每 3 秒才發布一次),這種情況下連線在一開始就無法建立(QoS 不相容,呼應第 2.2 節介紹的相容性判斷邏輯),根本沒有訊息在流動,自然也就不會有「逾時」這個概念可以觸發。
排除方式:確認兩端的 deadline 設定相容(Subscriber 要求的期限,要大於或等於 Publisher 承諾的期限),並用 ros2 topic info --verbose 確認連線確實建立成功,再進一步測試逾時偵測是否如預期運作。
錯誤二:Liveliness 監控太敏感,網路稍有延遲就誤判 Publisher 已經失去存活訊號
現象:Publisher 明明還在正常運作,只是因為系統負載較高導致心跳訊息延遲了零點幾秒送達,就觸發了緊急停止邏輯。
原因:liveliness_lease_duration 設定得太接近實際的心跳發送間隔,沒有留出容忍網路正常抖動(jitter)的餘裕,導致本來正常的些微延遲就被誤判為存活訊號遺失。
排除方式:liveliness_lease_duration 通常應該設定成明顯大於心跳實際發送間隔的數值(本文範例心跳間隔 0.5 秒,租期設定 2 秒,留有 4 倍的餘裕),具體倍數要依實際網路環境的穩定度調整,過於敏感的監控設定反而會造成不必要的誤報,降低系統的實際可用性。
小結
Deadline 監控「該來的資料有沒有按時來」,Liveliness 監控「Publisher 是否還存活」,Lifespan 讓過期資料自動失效,這三個政策共同構成了比第 2.2 節基礎三維度更進一步的連線健康監控機制,適合用在安全關鍵、需要主動偵測異常的場景,但也伴隨額外的效能開銷,不需要每個主題都套用。系統設計除了通訊層面的健康監控,另一個同樣重要的面向是怎麼確保程式碼邏輯本身的正確性——下一節要介紹 ROS2 的測試框架。
延伸閱讀
常見問題
- Deadline 違規之後,訊息傳輸會被中斷嗎?
- 不會,Deadline 是一種監控與通知機制,不是強制阻斷機制——即使發布頻率違反了約定的 Deadline,訊息還是會照常傳送,差別在於系統會觸發對應的事件回呼(QoS event callback)通知你『這次沒有在承諾的時間內發布』,讓應用邏輯自己決定要怎麼反應(記錄、報警、觸發復原行為),而不是由 DDS 底層強制做什麼動作。
- Liveliness 監控的是節點本身還活著,還是資料真的有意義?
- 只監控『Publisher 有沒有按照約定持續表態自己還活著』,跟資料內容本身是否合理無關。Liveliness 遺失代表的是『這個 Publisher 可能已經當機、斷線,或行程已經終止』,這是連線層級的健康監控,不是資料驗證——資料驗證(例如數值是否在合理範圍)是應用邏輯自己要處理的另一個獨立問題。
- 這些進階 QoS 政策,是不是每個主題都應該設定?
- 不是,Deadline、Liveliness 這類監控機制本身也有效能開銷(持續的心跳檢查、逾時判斷),只在真正需要『主動偵測資料流異常』的關鍵主題上啟用是比較務實的做法——例如安全相關的緊急停止訊號、或需要嚴格即時性保證的控制迴路,這種場景值得投入額外的監控成本;一般日常的狀態資訊主題,通常第 2.2 節介紹的基礎三維度已經足夠。