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
| Aspect | Topic | Service |
|---|---|---|
| Communication | One-way (async) | Two-way (sync) |
| Pattern | Publish/Subscribe | Request/Response |
| Waiting | No | Yes (caller waits) |
| Use Case | Sensor data, streams | Commands, queries |
| Example | Camera images | Move 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 callfrom 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