Service and Client - dhanushshettigar/Getting-Started-With-ROS2 GitHub Wiki

Service and Client (Python)

Table of Contents

  1. Background
  2. What is Service?
  3. What is Client?
  4. Example Workflow
  5. Benefits
  6. Create a Package
  7. Build the Package
  8. Conclusion

Background

In ROS2, the Service and Client mechanism provides a request/response communication model between nodes. This is useful when you need to perform a specific task on-demand, where one node (the client) sends a request, and another node (the service) processes that request and sends back a response.

Service

A Service in ROS2 acts like a server that waits for requests from clients. When a request is received, the service processes it and returns the result. Services are defined by a custom message type which contains two parts:

  1. Request: The input parameters for the service.
  2. Response: The output returned after processing the request.

Example:

  • A robot node that offers a service to move the robot to a specified location.
  • The service could take the desired position as the request and return the success status as the response.

To create a service, you define a service type (usually in a .srv file), implement a callback function in the server node that handles the incoming requests, and advertise the service.

Client

A Client is the counterpart to the service. It sends requests to a service and waits for a response. The client can initiate the communication at any time, and the service must be available to respond.

Example:

  • A controller node that requests the robot to move to a specific position by calling the robot's movement service.

The client node creates a request message, sends it to the service, and then waits for the response.

Example Workflow

  1. The client sends a request to the service.
  2. The service processes the request.
  3. The service sends back a response to the client.

This mechanism is synchronous: the client waits for the service to complete the task before receiving the result.

Benefits

  • On-Demand: Use this model when the task is not continuous but needs to be triggered as needed.
  • Two-Way Communication: The client gets a direct response back, ensuring that the task has been executed.

Writing a simple service and client (Python)

1. Create a Package

Navigate into the ros2_ws directory.

Recall that packages should be created in the src directory, not the root of the workspace. So, navigate into ros2_ws/src .

cd ros2_ws/src

Run the package creation command:

ros2 pkg create --build-type ament_python --license Apache-2.0 py_srvcli --dependencies rclpy example_interfaces

Your terminal will return a message verifying the creation of your package py_srvcli and all its necessary files and folders.

The --dependencies argument will automatically add the necessary dependency lines to package.xml. example_interfaces is the package that includes the .srv file you will need to structure your requests and responses:

int64 a
int64 b
---
int64 sum

The first two lines are the parameters of the request, and below the dashes is the response.

2. Update Package File

Because you used the --dependencies option during package creation, you don’t have to manually add dependencies to package.xml.

<description>Python client server tutorial</description>
<maintainer email="[email protected]">Your Name</maintainer>
<license>Apache-2.0</license>

3. Update Setup File

Add the same information to the setup.py file for the maintainer, maintainer_email, description and license fields:

maintainer='Your Name',
maintainer_email='[email protected]',
description='Python client server tutorial',
license='Apache-2.0',

4. Write The Service Node

Locate to ros2_ws/src/py_srvcli/py_srvcli directory, and create new file.

sudo nano service_member_function.py

Paste the following code.

from example_interfaces.srv import AddTwoInts

import rclpy
from rclpy.node import Node


class MinimalService(Node):

    def __init__(self):
        super().__init__('minimal_service')
        self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)

    def add_two_ints_callback(self, request, response):
        response.sum = request.a + request.b
        self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))

        return response


def main():
    rclpy.init()

    minimal_service = MinimalService()

    rclpy.spin(minimal_service)

    rclpy.shutdown()


if __name__ == '__main__':
    main()

5. Write The Client Node

Create new file for client.

sudo nano client_member_function.py

Paste the following code.

import sys

from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node


class MinimalClientAsync(Node):

    def __init__(self):
        super().__init__('minimal_client_async')
        self.cli = self.create_client(AddTwoInts, 'add_two_ints')
        while not self.cli.wait_for_service(timeout_sec=1.0):
            self.get_logger().info('service not available, waiting again...')
        self.req = AddTwoInts.Request()

    def send_request(self, a, b):
        self.req.a = a
        self.req.b = b
        return self.cli.call_async(self.req)


def main():
    rclpy.init()

    minimal_client = MinimalClientAsync()
    future = minimal_client.send_request(int(sys.argv[1]), int(sys.argv[2]))
    rclpy.spin_until_future_complete(minimal_client, future)
    response = future.result()
    minimal_client.get_logger().info(
        'Result of add_two_ints: for %d + %d = %d' %
        (int(sys.argv[1]), int(sys.argv[2]), response.sum))

    minimal_client.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

Now the directory should have these files:

__init__.py service_member_function.py client_member_function.py

6. Add an Entry Point

Link the Service and Client node in setup file.

cd ..

The entry_points field of your setup.py file should look like this:

entry_points={
    'console_scripts': [
        'service = py_srvcli.service_member_function:main',
        'client = py_srvcli.client_member_function:main',
    ],
},

7. Build & Test Package

Go to the workspace's root.

cd ../..

Build your new package:

colcon build --packages-select py_srvcli

Open a new terminal, navigate to ros2_ws, and run the service node:

source install/setup.bash
ros2 run py_srvcli service

The node will wait for the client’s request.

Open another terminal, navigate to ros2_ws, and run the client node:

source install/setup.bash
ros2 run py_srvcli client 2 3

If you chose 2 and 3, for example, the client would receive a response like this:

[INFO] [minimal_client_async]: Result of add_two_ints: for 2 + 3 = 5

Return to the terminal where your service node is running. You will see that it published log messages when it received the request:

[INFO] [minimal_service]: Incoming request
a: 2 b: 3

Enter Ctrl+C in each terminal to stop the nodes from spinning.

Conclusion

You created two nodes to request and respond to data over a service. You added their dependencies and executables to the package configuration files so that you could build and run them, allowing you to see a service/client system at work.

⚠️ **GitHub.com Fallback** ⚠️