Skip to main content

Section 3: Services - Request and Response

Estimated Reading Time: 7 minutes Bloom's Level: Understand, Apply Key Learning: Synchronous request/response communication


When Topics Aren't Enough

Topics are great for streaming data, but what if you need:

  • A response to a request?
  • Guaranteed delivery?
  • Synchronous wait-for-answer behavior?

That's where services come in.

Topics vs. Services

AspectTopicService
CommunicationOne-way (async)Two-way (sync)
PatternPublish/SubscribeRequest/Response
WaitingNoYes (caller waits)
Use CaseSensor data, streamsCommands, queries
ExampleCamera imagesMove robot to X,Y

What is a Service?

A service is a synchronous request/response call between nodes.

Like a phone call:

  • Client: Calls and waits for answer
  • Server: Receives call and responds
  • Service Name: The phone number (e.g., /add_two_ints)
  • Request/Response: What's asked and what's answered

Code Example 3: Service Client and Server

Location: code-examples/ros2_packages/service_example_py/

Generated by: ros2-code-generator skill

Service Definition

First, define what the service looks like:

# File: service_example_py/service_example_py/add_two_ints.srv
int64 a
int64 b
---
int64 sum

(The --- separates request from response)

Service Server

# File: service_example_py/service_example_py/server.py
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts

class AddServerNode(Node):
def __init__(self):
super().__init__('add_server')

# Create a service
self.srv = self.create_service(
AddTwoInts,
'/add_two_ints',
self.add_two_ints_callback
)

def add_two_ints_callback(self, request, response):
# Handle incoming request
response.sum = request.a + request.b
self.get_logger().info(
f'Incoming request: a={request.a}, b={request.b}'
)
self.get_logger().info(f'Sending back response: {response.sum}')
return response

def main(args=None):
rclpy.init(args=args)
node = AddServerNode()
self.get_logger().info('Service ready')
rclpy.spin(node)
rclpy.shutdown()

if __name__ == '__main__':
main()

Service Client

# File: service_example_py/service_example_py/client.py
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts

class AddClientNode(Node):
def __init__(self):
super().__init__('add_client')

# Create a client
self.client = self.create_client(
AddTwoInts,
'/add_two_ints'
)

# Wait for service to be available
while not self.client.wait_for_service(timeout_sec=1.0):
self.get_logger().info('Service not available, waiting...')

# Make a request
self.send_request()

def send_request(self):
# Create request
request = AddTwoInts.Request()
request.a = 5
request.b = 3

# Send request (blocks until response)
self.future = self.client.call_async(request)

# Continue processing (don't block main thread)
rclpy.spin_until_future_complete(
self,
self.future
)

# Handle response
if self.future.result() is not None:
response = self.future.result()
self.get_logger().info(f'Result: 5 + 3 = {response.sum}')
else:
self.get_logger().error('Service call failed!')

def main(args=None):
rclpy.init(args=args)
node = AddClientNode()
rclpy.shutdown()

if __name__ == '__main__':
main()

Using Services from Command Line

Start Server (Terminal 1)

ros2 run service_example_py server

Output:

[INFO] [add_server]: Service ready
[INFO] [add_server]: Incoming request: a=5, b=3
[INFO] [add_server]: Sending back response: 8

Call Service (Terminal 2)

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

Output:

requester: making request #1 of 1 with values
a: 5
b: 3

response:
sum: 8

Run Client (Terminal 3)

ros2 run service_example_py client

Output:

[INFO] [add_client]: Service not available, waiting...
[INFO] [add_client]: Result: 5 + 3 = 8

Services in the Real World

Example 1: Robot Navigation

Client Node: User says "go to kitchen"

Service: /navigate

Server Node: Navigation system processes request

Response: "Arrived at kitchen"

Example 2: Sensor Calibration

Client: "Calibrate IMU"

Service: /calibrate_imu

Server: Runs calibration routine

Response: "Calibration complete, offsets: ..."

Example 3: Get Robot Status

Client: "What's your battery level?"

Service: /get_status

Server: Reads sensors

Response: "Battery: 85%, Temp: 45C"

Services vs. Topics: Decision Guide

Use Topic when:

  • Streaming continuous data (sensors, state)
  • Multiple subscribers needed
  • Asynchronous is fine
  • High-frequency messages

Use Service when:

  • Request/response pattern
  • Synchronous answer required
  • Single requester
  • Configuration or command
  • Query for information

Key Takeaways

✅ Services enable synchronous request/response ✅ Server listens for requests and responds ✅ Client sends request and waits for response ✅ Services are typed with request/response definitions ✅ Use services for commands and queries ✅ Use topics for streaming data


Try It Yourself

Try modifying the service example:

  • Add two numbers with different values
  • Use ros2 service call from command line
  • Modify server to do division instead of addition
  • Create a service that returns multiple values in response
  • Handle error cases (e.g., division by zero)

Next: Putting It Together

You now understand: ✅ Nodes (Section 1) ✅ Topics - streaming communication (Section 2) ✅ Services - request/response (Section 3)

It's time to practice with exercises!

👉 Next: Exercises

Or review:


Section Status: Complete ✅ Skills Used: ros2-code-generator, code-explainer