Skip to main content

Chapter 6: Services & Clients

Service Overview​

Services provide synchronous request-response communication. Use services for infrequent operations like configuration, triggering actions, or querying state.

Service Types​

# List service types
ros2 interface list | grep srv

# Common types
std_srvs/srv/SetBool
std_srvs/srv/Trigger
example_interfaces/srv/AddTwoInts

Service Server​

from example_interfaces.srv import AddTwoInts

class AddTwoIntsServer(Node):
def __init__(self):
super().__init__('add_two_ints_server')
self.srv = self.create_service(
AddTwoInts,
'add_two_ints',
self.add_callback
)
self.get_logger().info('Service ready')

def add_callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info(f'{request.a} + {request.b} = {response.sum}')
return response

Service Client (Synchronous)​

class AddTwoIntsClient(Node):
def __init__(self):
super().__init__('add_client')
self.client = self.create_client(AddTwoInts, 'add_two_ints')

while not self.client.wait_for_service(timeout_sec=1.0):
self.get_logger().info('Waiting for service...')

def send_request(self, a, b):
request = AddTwoInts.Request()
request.a = a
request.b = b

future = self.client.call_async(request)
rclpy.spin_until_future_complete(self, future)

if future.result() is not None:
return future.result().sum
else:
self.get_logger().error('Service call failed')
return None

Service Client (Async)​

class AsyncClient(Node):
def __init__(self):
super().__init__('async_client')
self.client = self.create_client(AddTwoInts, 'add_two_ints')

async def send_request_async(self, a, b):
request = AddTwoInts.Request()
request.a = a
request.b = b

future = self.client.call_async(request)
result = await future
return result.sum

Custom Service​

Create srv/ComputeRectangle.srv:

float64 length
float64 width
---
float64 area
float64 perimeter

Use in code:

from my_package.srv import ComputeRectangle

def compute_callback(self, request, response):
response.area = request.length * request.width
response.perimeter = 2 * (request.length + request.width)
return response

CLI Usage​

# List services
ros2 service list

# Service type
ros2 service type /add_two_ints

# Call service
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5, b: 3}"

Error Handling​

def add_callback(self, request, response):
try:
if request.a < 0 or request.b < 0:
self.get_logger().warn('Negative values provided')
response.sum = request.a + request.b
return response
except Exception as e:
self.get_logger().error(f'Error: {e}')
return response

Exercise: Create service to set robot speed limits (min/max velocity).

Next: Actions