Сервисы: запрос и ответ
Не всё в роботе — потоки данных; иногда нужен разовый вопрос с ответом. Для этого есть сервисы.
Сервис (service) — это связь «запрос → ответ» (request/response): узел-клиент посылает запрос, узел-сервер обрабатывает его и возвращает результат.
Когда топиков недостаточно
Топики идеальны для непрерывных потоков: лидар без устали шлёт сканы, и никто не «отвечает». Но бывают задачи другого рода — разовые команды с подтверждением. «Включи свет на роботе и скажи, получилось ли». «Сбрось одометрию в ноль». «Сделай снимок и верни путь к файлу». Здесь нужен ответ. Публиковать такое в топик неудобно: непонятно, выполнилось ли, и нет обратной связи. Это работа для сервиса.
| Топик | Сервис |
| поток, многие-ко-многим | разовый вызов, один-к-одному |
| без ответа | обязателен ответ |
| данные датчиков | команды, запросы состояния |
Файлы .srv
Структуру сервиса описывают в файле .srv. Он состоит из двух частей, разделённых ---: сверху — поля запроса, снизу — поля ответа.
# example_interfaces/srv/SetBool
bool data # запрос: включить (true) / выключить (false)
---
bool success # ответ: получилось ли
string message # ответ: пояснениеВызов сервиса из терминала
# список сервисов
ros2 service list
# вызвать сервис вручную
ros2 service call /enable_light example_interfaces/srv/SetBool "{data: true}"Вывод:
response: example_interfaces.srv.SetBool_Response(success=True, message='Свет включён')
Сервер на rclpy
Код узла нельзя запустить в браузере (language-text):
import rclpy
from rclpy.node import Node
from example_interfaces.srv import SetBool
class LightServer(Node):
def __init__(self):
super().__init__('light_server')
self.srv = self.create_service(
SetBool, '/enable_light', self.handle)
def handle(self, request, response):
state = 'включён' if request.data else 'выключен'
response.success = True
response.message = f'Свет {state}'
return responseКак работает под капотом
Вызов сервиса синхронен по смыслу: клиент ждёт ответа. В rclpy, чтобы не заблокировать executor, обычно используют асинхронный вызов call_async, который возвращает future — обещание ответа. Когда сервер ответит, future заполнится результатом. Под капотом DDS гарантирует, что запрос дойдёт ровно до одного сервера, а ответ вернётся именно тому клиенту, кто спрашивал. Если сервера нет, клиент будет ждать или получит таймаут — поэтому перед вызовом проверяют доступность через wait_for_service.
Частые ошибки
- Использовать сервис для непрерывного потока. Для данных датчика это топик; сервис — для разовых команд.
- Долгая работа в обработчике сервиса. Если задача длительная (поездка к точке) — это уже не сервис, а действие (action).
- Синхронный call в spin. Может привести к взаимоблокировке; используйте call_async с future.
Итоги
- Сервис — разовая связь запрос/ответ, один-к-одному.
- Структура — файл .srv с частями запроса и ответа через ---.
- Применяют для команд и запросов состояния, где нужен ответ.
- Для длительных задач сервис не подходит — нужен action.