Synchronous Service Calls
Overview
This document explains the simplest way to call services synchronously from a ROS 2 python node.
Constructing the Node
The node's __init__ function is where
- The service client callback group is created
- The services are created
- We wait for the services to become available
- We initially assume that once available, the services will always be available, otherwise there is an error.
from rclpy.callback_groups import MutuallyExclusiveCallbackGroup class AwaitClient(node): def __init__(self): super().__init__('await_client') # Create the callback group self._mux_client = MutuallyExclusiveCallbackGroup() # Create the services self._client = self.create_client(ServiceType, 'service_name', callback_group = self._mux_client) # Other clients with callbacks get the default callback group, for example, a timer self._tmr = self.create_timer(5.0, self.timer_callback) # Wait for the servies to become available. if not self._client.wait_for_service(timeout_sec = 1.0): raise RuntimeError(f"Timeout waiting for service {self._client.service_name}")
Calling the Service From A Callback
- To call the service from a callback, the callback should be defined as an
asyncfunction - At the site of the call, use
await, which will suspend the callback until the result is available. - This mode can be dangerous in real code since it will wait forever (e.g., if the node providing the service crashes)
async def timer_callback(self): # Call the service, waiting indefinitely for the response # If you are not using the response, omit the result = result = await self._client.call_async(Request())
Timeouts
- Adding a timeout to the
awaitis good practice, because it is possible that the node providing the service server can crash or become unreachable- Without a timeout,
awaitwill block the client node forever.
- Without a timeout,
- The Python asyncio library provides a few methods for adding timeouts to
awaitcalls - For a single call,
result = await asyncio.wait_for(client.call_async(Request()), timeout = 0.5) For several service calls that should all be governed by the same (cumulative) timeout:
async with asyncio.timeout(4.5): await client.call_async(Request()) await client.call_async(Request())
- If the async call does not complete before the timeout elapses, a
TimeoutErroris raised
Calling a Service during Setup
- If you need to call services during the setup of your node, you can use
spin_until_future_completeto wait for services to return. - Any use of
spin_until_future_completeis restricted to the code prior to callingrclpy.spin()