QoS (Quality of Service) trong ROS2 là gì?
Atlasbot · 16/8/2026 · 11 phút đọc
Có một lớp lỗi ROS2 khiến người mới bối rối nhất: ros2 topic list thấy topic tồn tại, ros2 node info thấy cả publisher lẫn subscriber đều đã đăng ký đúng topic — nhưng subscriber không bao giờ nhận được dữ liệu. Nguyên nhân rất có thể nằm ở QoS không tương thích.
Khái niệm chính
QoS (Quality of Service) là tập hợp các "chính sách" cấu hình cách dữ liệu được truyền trên một topic — ROS2 kế thừa khả năng này từ DDS (xem bài riêng), khác hẳn ROS1 vốn chỉ có một cách truyền cố định qua TCP. Bốn chính sách quan trọng nhất:
| Chính sách | Lựa chọn | Ý nghĩa |
|---|---|---|
| Reliability | Reliable | Đảm bảo gửi lại nếu mất gói — chậm hơn nhưng không mất dữ liệu |
| Best Effort | Gửi 1 lần, mất thì thôi — nhanh hơn, phù hợp dữ liệu tần suất cao | |
| Durability | Volatile | Subscriber vào sau bỏ lỡ các message đã publish trước đó |
| Transient Local | Subscriber vào sau vẫn nhận được message cuối cùng đã publish | |
| History | Keep Last (N) | Chỉ giữ N message gần nhất trong hàng đợi |
| Keep All | Giữ toàn bộ (giới hạn bởi bộ nhớ) |
Vì sao publisher và subscriber phải "khớp" QoS
DDS có quy tắc tương thích (compatibility): một số tổ hợp QoS giữa publisher và subscriber sẽ không bao giờ kết nối được, dù cả hai cùng đúng tên topic và kiểu message. Ví dụ kinh điển: publisher cấu hình Best Effort, subscriber yêu cầu Reliable — DDS coi đây là yêu cầu subscriber không thể đáp ứng được với những gì publisher cung cấp, và từ chối ghép nối hoàn toàn, âm thầm, không có lỗi hiển thị rõ ràng.
Tóm lại: Dữ liệu cảm biến tần suất cao (LiDAR, IMU) thường dùng Best Effort — mất một khung dữ liệu không sao, khung tiếp theo tới ngay sau đó. Dữ liệu quan trọng không được phép mất (lệnh dừng khẩn cấp, cấu hình) nên dùng Reliable. Cấu hình sai loại cho đúng dữ liệu là nguồn lỗi "không nhận được gì" phổ biến nhất khi mới học ROS2.
Nguyên lý hoạt động
Cấu hình QoS tường minh khi tạo publisher/subscriber trong Python:
from rclpy.qos import QoSProfile, ReliabilityPolicy, DurabilityPolicy
qos = QoSProfile(
reliability=ReliabilityPolicy.BEST_EFFORT, # dữ liệu LiDAR tần suất cao
durability=DurabilityPolicy.VOLATILE,
depth=5
)
self.create_subscription(LaserScan, '/scan', self.callback, qos)
Nhiều package (như bản đồ từ SLAM) publish với QoS Transient Local — vì bản đồ chỉ cập nhật không thường xuyên, node khởi động sau (như RViz2 mở lên sau khi SLAM đã chạy được một lúc) vẫn cần nhận được bản đồ mới nhất ngay lập tức thay vì phải chờ tới lần publish tiếp theo.
Kiến thức kỹ thuật
Thông số
Bảng tương thích Reliability — quan hệ không đối xứng, cần nhớ đúng chiều để tránh cấu hình sai:
| Publisher | Subscriber Reliable | Subscriber Best Effort |
|---|---|---|
| Reliable | ✅ Kết nối được | ✅ Kết nối được |
| Best Effort | ❌ Không kết nối được | ✅ Kết nối được |
Quy tắc chung: subscriber chỉ có thể yêu cầu mức đảm bảo thấp hơn hoặc bằng những gì publisher cung cấp — subscriber "đòi hỏi nhiều hơn" publisher có thể đưa ra sẽ luôn thất bại.
Protocol
ROS2 cung cấp sẵn một số preset QoS chuẩn hoá cho các tình huống phổ biến, tránh phải tự khai từng field một: rclcpp::SensorDataQoS() (Best Effort + Volatile + depth nhỏ, dùng cho dữ liệu cảm biến tần suất cao), rclcpp::SystemDefaultsQoS() (Reliable + Volatile, mặc định cho hầu hết trường hợp chung). Dùng đúng preset này thay vì tự cấu hình tay giúp code nhất quán với quy ước chung của cộng đồng ROS2, giảm nguy cơ tự tạo ra 1 tổ hợp QoS lạ không tương thích với driver/package chuẩn khác.
Ứng dụng thực tế
Hai kiểu dữ liệu tiêu biểu trên AMR minh hoạ rõ khi nào dùng tổ hợp QoS nào: dữ liệu LiDAR/IMU tần suất cao dùng Best Effort + Volatile (mất 1 khung không sao, khung mới sẽ tới ngay, không cần giữ lịch sử cũ); trong khi bản đồ tĩnh (/map) hoặc mô tả hình học robot (/robot_description, thường publish 1 lần lúc khởi động) dùng Reliable + Transient Local — đảm bảo bất kỳ node nào khởi động sau (VD: mở RViz2 để debug giữa chừng phiên vận hành) vẫn nhận được đầy đủ dữ liệu quan trọng này ngay lập tức, không phải chờ hoặc bỏ lỡ vĩnh viễn.
Ví dụ thực tế
$ ros2 topic info /scan --verbose
Output:
Type: sensor_msgs/msg/LaserScan
Publisher count: 1
Node name: lidar_driver
QoS profile:
Reliability: BEST_EFFORT
Durability: VOLATILE
History (Depth): KEEP_LAST (5)
Subscription count: 0
Subscription count: 0 dù publisher đang chạy tốt — nếu subscriber code đã chạy nhưng vẫn hiện số 0 ở đây, gần như chắc chắn là do QoS không tương thích (VD: subscriber yêu cầu Reliable trong khi publisher chỉ cung cấp Best Effort) — kiểm tra QoS 2 bên trước khi nghi ngờ lỗi logic code.
Lỗi thường gặp
- Subscriber yêu cầu Reliable trong khi publisher chỉ cung cấp Best Effort — như đã nói ở mục Thông số, đây là tổ hợp không bao giờ kết nối được — kiểm tra bằng
ros2 topic info --verbosenhư ví dụ trên. - Dùng Reliable cho dữ liệu tần suất cực cao — buộc DDS phải đảm bảo gửi lại mọi gói bị mất có thể tạo ra độ trễ tích luỹ (backpressure) khi mạng không ổn định, tệ hơn việc chấp nhận mất một vài khung dữ liệu với Best Effort — Reliable không phải luôn luôn "an toàn hơn" trong mọi tình huống.
- Không để ý QoS mặc định khác nhau giữa publisher tự viết và driver có sẵn — publisher mặc định trong code tự viết thường là
Reliable, nhưng nhiều driver cảm biến chuẩn lại mặc địnhBest Effort— kết nối 2 node tự nghĩ "cứ để mặc định" đôi khi vẫn gặp lỗi tương thích chỉ vì khác nguồn code. - Quên đặt Transient Local cho dữ liệu cấu hình chỉ publish 1 lần — nếu node subscribe khởi động sau khi dữ liệu đó đã publish xong (với Durability mặc định Volatile), nó sẽ không bao giờ nhận được, phải chờ tới lần publish tiếp theo (nếu có) hoặc mất vĩnh viễn nếu publisher chỉ gửi đúng 1 lần.
Kết luận
QoS là công cụ mạnh mẽ để tinh chỉnh cách dữ liệu được truyền giữa các node, nhưng cũng là nguồn lỗi "kết nối thầm lặng thất bại" phổ biến nhất với người mới ROS2 — luôn dùng ros2 topic info --verbose để kiểm tra khi nghi ngờ. Bài tiếp theo trong chuyên mục nói về DDS — lớp middleware thực sự đứng sau toàn bộ cơ chế QoS, Topic, Service vừa học.