Service trong ROS2 là gì?
Atlasbot · 16/8/2026 · 10 phút đọc
"Reset lại giá trị encoder về 0", "lưu bản đồ hiện tại ra file" — đây là những yêu cầu chỉ cần thực hiện một lần, có kết quả rõ ràng ngay sau đó, không phải luồng dữ liệu liên tục. Service là cơ chế ROS2 dành cho đúng kiểu tương tác này.
Khái niệm chính
Service hoạt động theo mô hình request-response kinh điển, gồm hai phía:
- Service Server — "đăng ký" cung cấp một dịch vụ có tên cụ thể, chờ nhận yêu cầu và trả về kết quả
- Service Client — gửi một yêu cầu (request) tới server, chờ nhận đúng một phản hồi (response) rồi mới tiếp tục
Khác với Topic (publisher không biết ai nhận, không có gì đảm bảo), Service luôn có đúng một cặp yêu cầu – phản hồi cho mỗi lần gọi — giống hệt việc gọi một hàm (function call) thông thường, chỉ khác là hàm đó chạy trên một tiến trình (node) khác.
Tóm lại: Topic phù hợp cho dữ liệu chảy liên tục không cần xác nhận; Service phù hợp cho một hành động rời rạc cần biết chắc chắn kết quả — như bật/tắt một chế độ, hay thực hiện một phép tính theo yêu cầu.
Nguyên lý hoạt động
Service Client Service Server
│ gửi request │
├─────────────────────────────────►│
│ │ xử lý yêu cầu
│ chờ... │
│◄─────────────────────────────────┤
│ nhận response │
Gọi thử một service có sẵn ngay từ dòng lệnh, không cần viết code:
ros2 service list # liệt kê mọi service đang hoạt động
ros2 service call /reset_encoder std_srvs/srv/Trigger
Ví dụ Service Server đơn giản bằng Python, cộng hai số nguyên theo yêu cầu:
from example_interfaces.srv import AddTwoInts
class AddServer(Node):
def __init__(self):
super().__init__('add_server')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_callback)
def add_callback(self, request, response):
response.sum = request.a + request.b # xử lý, gán kết quả vào response
return response # trả về — client đang chờ ở đây
Kiểu dữ liệu request/response (như AddTwoInts ở trên) được định nghĩa trong file .srv riêng của package — chủ đề được trình bày sâu hơn ở bài Service trong mục ROS2 Communication.
Kiến thức kỹ thuật
Thông số
Một số kiểu service dùng chung (built-in) phổ biến, tránh phải tự định nghĩa .srv riêng cho các thao tác đơn giản:
| Kiểu service | Request | Response | Dùng cho |
|---|---|---|---|
std_srvs/srv/Trigger | (rỗng) | success: bool, message: string | Kích hoạt 1 hành động đơn giản, không cần tham số |
std_srvs/srv/SetBool | data: bool | success: bool, message: string | Bật/tắt 1 tính năng/chế độ |
example_interfaces/srv/AddTwoInts | a: int64, b: int64 | sum: int64 | Ví dụ minh hoạ, ít dùng thực tế |
Protocol
Ở tầng dưới, mỗi service ROS2 thực chất được cài đặt bằng 2 DDS topic ẩn — 1 topic cho request, 1 topic cho response, cộng thêm cơ chế khớp đúng cặp request/response qua một mã định danh (sequence number) — client publish request lên topic request, server subscribe topic đó, xử lý xong publish response lên topic response kèm đúng mã định danh để client nhận diện đúng response nào ứng với request nào của chính nó (quan trọng khi có nhiều client cùng gọi 1 server).
Ứng dụng thực tế
Service phù hợp cho các thao tác điều khiển chế độ vận hành robot — VD: một service SetBool tên /switch_autonomous_mode cho phép chuyển robot giữa chế độ điều khiển thủ công (teleop) và tự hành hoàn toàn theo yêu cầu từ giao diện người dùng; hoặc một service Trigger tên /save_map để yêu cầu node SLAM lưu bản đồ hiện tại ra file ngay lập tức. Cả 2 trường hợp đều là hành động rời rạc cần biết chắc chắn đã thực hiện thành công hay chưa — đúng thế mạnh của Service so với Topic.
Ví dụ thực tế
$ ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 3, b: 5}"
Output:
waiting for service to become available...
requester: making request: example_interfaces.srv.AddTwoInts_Request(a=3, b=5)
response:
example_interfaces.srv.AddTwoInts_Response(sum=8)
Kết quả sum=8 xác nhận server đã nhận đúng request, xử lý, và gửi lại đúng response — cách nhanh nhất để kiểm tra 1 service có hoạt động đúng hay không mà không cần viết code client riêng.
Lỗi thường gặp
- Gọi service đồng bộ (blocking) từ bên trong callback của node khác — nếu client gọi service theo kiểu chờ đồng bộ ngay trong 1 callback (VD: subscriber callback), và server đó lại nằm trên cùng node/executor đang bị chiếm bởi chính callback đang chờ, có thể gây deadlock (cả 2 phía cùng chờ nhau, không bên nào tiến triển được) — nên dùng lời gọi bất đồng bộ (async) khi gọi service từ bên trong callback khác.
- Không xử lý timeout khi server không phản hồi — nếu server bị treo hoặc chưa khởi động, client mặc định có thể chờ vô thời hạn — luôn đặt timeout hợp lý và xử lý trường hợp không nhận được phản hồi.
- Gọi service trước khi server sẵn sàng — nếu client gọi ngay khi node vừa khởi động, trong khi server chưa kịp đăng ký (do thứ tự khởi động các node khác nhau), request sẽ thất bại — nên dùng
client.wait_for_service()trước khi gọi lần đầu. - Sai kiểu service giữa client và server — tương tự lỗi sai kiểu message ở Topic, client và server phải khai đúng cùng 1 kiểu
.srv— sai kiểu khiến client không tìm thấy server tương ứng dù tên service đúng.
Kết luận
Service là cơ chế request-response cho các hành động rời rạc cần biết chắc chắn kết quả — bổ sung cho Topic (dữ liệu liên tục, không cần xác nhận). Hiểu đúng khi nào dùng Service thay vì Topic, và cẩn trọng với rủi ro block/deadlock khi gọi đồng bộ, là kỹ năng cần thiết khi thiết kế giao tiếp giữa các node. Bài tiếp theo trong chuyên mục nói về Action — cơ chế dành cho các tác vụ dài, cần cả phản hồi tiến độ liên tục lẫn kết quả cuối cùng, điều mà cả Topic lẫn Service đều không làm tốt riêng lẻ.