Inspecting services (ros2 service)#
ROS2 has a tool to help us inspect services. It is just as helpful as the tools for topics.
ros2 service -h
which outputs the detailed information of the tool, as shown below. In particular, the highlighted fields are used quite frequently in practice.
usage: ros2 service [-h] [--include-hidden-services] Call `ros2 service <command> -h` for more detailed usage. ...
Various service related sub-commands
options:
-h, --help show this help message and exit
--include-hidden-services
Consider hidden services as well
Commands:
call Call a service
echo Echo a service
find Output a list of available services of a given type
info Print information about a service
list Output a list of available services
type Output a service's type
Call `ros2 service <command> -h` for more detailed usage.
Start a service server#
Similar to the discussion about topics, it is good to be able to test service servers without having to develop a complete service client. Let’s start by running the service server we created just now.
Warning
Be sure to terminate the Nodes we used in the past section before proceeding (e.g. with CTRL+C), otherwise, the output will look different from what is described here.
ros2 run python_package_that_uses_the_services add_points_service_server_node
Getting all services with ros2 service list#
To see all currently active services, we run
ros2 service list
which, in this case, outputs
/add_points
/add_points_service_server/describe_parameters
/add_points_service_server/get_parameter_types
/add_points_service_server/get_parameters
/add_points_service_server/get_type_description
/add_points_service_server/list_parameters
/add_points_service_server/set_parameters
/add_points_service_server/set_parameters_atomically
To everyone’s surprise, there are a lot of services beyond the one we created. We can address those when we talk about ROS2 parameters, for now, we ignore them.
Testing your service servers with ros2 service call#
Like the discussion about topics, ROS2 has a tool to call a service from the terminal, called ros2 service call. The service must be specified and an instance of its request must be written using YAML. Back to our example, we can do
ros2 service call /add_points \
package_with_interfaces/srv/AddPoints \
'{
a: {
x: 10,
y: 11,
z: 12
},
b: {
x: -10,
y: -10,
z: 22
}
}'
which results in
requester: making request: package_with_interfaces.srv.AddPoints_Request(a=geometry_msgs.msg.Point(x=10.0, y=11.0, z=12.0), b=geometry_msgs.msg.Point(x=-10.0, y=-10.0, z=22.0))
response:
package_with_interfaces.srv.AddPoints_Response(result=geometry_msgs.msg.Point(x=0.0, y=1.0, z=34.0))
Testing your service clients#
To the best of my knowledge, there is no tool inside ros2 service to allow us to experiment with service clients. For service clients the only way to test them is to make a minimal service server to interact with them. We’ve already done that, so this topic ends here.
How about ros2 service echo#
Added in version Jazzy: Added this section.
We’ll be working in these files
python_package_that_uses_the_services
|-- package.xml
|-- python_package_that_uses_the_services
| |-- __init__.py
| |-- add_points_service_client_introspection_node.py
| |-- add_points_service_client_node.py
| |-- add_points_service_server_introspection_node.py
| `-- add_points_service_server_node.py
|-- resource
| `-- python_package_that_uses_the_services
|-- setup.cfg
|-- setup.py
`-- test
|-- test_copyright.py
|-- test_flake8.py
`-- test_pep257.py
It might be good to know of the existence of service introspection. Although it seems to work the documentation might be ongoing, according to this discussion: ros-infrastructure/rep#360.
After proper configuration of the server and client, ros2 service echo allows us to look at what information is being exchanged between participants.
It helps not having to add endless print functions throughout the code that hurt performance.
In the folder ~/ros2_tutorial_workspace/src/python_package_that_uses_the_services/python_package_that_uses_the_services we add the following two files.
Note that we are using inheritance from the previously written AddPointsServiceClientNode and AddPointsServiceServerNode to minimize code replication.
For the Node with the service client, we must use the configure_introspection method. The argument requires a service_event_qos_profile and a introspection_state.
The quality-of-service profile is within the same scope as the quality-of-service for messages and has a similar meaning. The introspection_state defines how much of the service can be introspected.
Less information can be made accessible if that is necessary.
add_points_service_client_introspection_node.py
1from .add_points_service_client_node import AddPointsServiceClientNode
2
3import rclpy
4from rclpy.service import ServiceIntrospectionState
5from rclpy.qos import qos_profile_system_default
6
7class AddPointsServiceClientIntrospectionNode(AddPointsServiceClientNode):
8
9 def __init__(self):
10 super().__init__()
11
12 # https://github.com/ros2/demos/blob/rolling/demo_nodes_py/demo_nodes_py/services/introspection.py
13 self.service_client.configure_introspection(
14 clock=self.get_clock(),
15 service_event_qos_profile=qos_profile_system_default,
16 introspection_state=ServiceIntrospectionState.CONTENTS)
17
18def main(args=None):
19 """
20 The main function.
21 :param args: Not used directly by the user, but used by ROS2 to configure
22 certain aspects of the Node.
23 """
24 try:
25 rclpy.init(args=args)
26
27 add_points_service_client_introspection_node = AddPointsServiceClientIntrospectionNode()
28
29 rclpy.spin(add_points_service_client_introspection_node)
30 except KeyboardInterrupt:
31 pass
32 except Exception as e:
33 print(e)
34
35
36if __name__ == '__main__':
37 main()
The service server has the same syntax and requirements to activate introspection as the service client.
add_points_service_server_introspection_node.py
1from .add_points_service_server_node import AddPointsServiceServerNode
2
3import rclpy
4from rclpy.service import ServiceIntrospectionState
5from rclpy.qos import qos_profile_system_default
6
7class AddPointsServiceServerIntrospectionNode(AddPointsServiceServerNode):
8
9 def __init__(self):
10 super().__init__()
11
12 # https://github.com/ros2/demos/blob/rolling/demo_nodes_py/demo_nodes_py/services/introspection.py
13 self.service_server.configure_introspection(
14 clock=self.get_clock(),
15 service_event_qos_profile=qos_profile_system_default,
16 introspection_state=ServiceIntrospectionState.CONTENTS)
17
18def main(args=None):
19 """
20 The main function.
21 :param args: Not used directly by the user, but used by ROS2 to configure
22 certain aspects of the Node.
23 """
24 try:
25 rclpy.init(args=args)
26
27 add_points_service_server_introspection_node = AddPointsServiceServerIntrospectionNode()
28
29 rclpy.spin(add_points_service_server_introspection_node)
30 except KeyboardInterrupt:
31 pass
32 except Exception as e:
33 print(e)
34
35
36if __name__ == '__main__':
37 main()
Build and source#
Before we proceed, let us build and source once.
cd ~/ros2_tutorial_workspace
colcon build
source install/setup.bash
Note
For additional explanation and troubleshooting tips, see Always source after you build.
Warning
colcon will not work properly if your terminal has an active venv.
Finally the ros2 service echo#
To be able to use ros2 service echo, we must be sure that the service server and ros2 service echo are active before the service client requests the service we want to inspect the entire exchange.
It might be a bit of a handful, but we will need three (properly sourced) terminals. In this order.
ros2 run python_package_that_uses_the_services add_points_service_server_introspection_node
ros2 service echo /add_points
ros2 run python_package_that_uses_the_services add_points_service_client_introspection_node
Each program will have it’s own output, shown below. For the purposes of this section we can focus on the output of ros2 service echo. The other two outputs repeat what we have seen in the previous session, further guaranteeing that the introspection works without affecting the overall behavior of the nodes too much.
As you can see, we will receive a profoundly detailed output showing all aspects of the service, including the request sent, an acknowledgement of what was received, what was replied, and the acknowledgement of the reply!
info:
event_type: REQUEST_SENT
stamp:
sec: 1761419221
nanosec: 796638805
client_gid:
- 1
- 15
- 235
- 125
- 217
- 40
- 45
- 142
- 0
- 0
- 0
- 0
- 0
- 0
- 20
- 3
sequence_number: 1
request:
- a:
x: 104.79864813644902
y: 422.844185397337
z: 0.0
b:
x: 531.6723658565676
y: 761.3967957778456
z: 357.29827376582836
response: []
---
info:
event_type: REQUEST_RECEIVED
stamp:
sec: 1761419221
nanosec: 800073888
client_gid:
- 1
- 15
- 235
- 125
- 217
- 40
- 45
- 142
- 0
- 0
- 0
- 0
- 0
- 0
- 19
- 4
sequence_number: 1
request:
- a:
x: 104.79864813644902
y: 422.844185397337
z: 0.0
b:
x: 531.6723658565676
y: 761.3967957778456
z: 357.29827376582836
response: []
---
info:
event_type: RESPONSE_SENT
stamp:
sec: 1761419221
nanosec: 801308763
client_gid:
- 1
- 15
- 235
- 125
- 217
- 40
- 45
- 142
- 0
- 0
- 0
- 0
- 0
- 0
- 19
- 4
sequence_number: 1
request: []
response:
- result:
x: 636.4710139930166
y: 1184.2409811751827
z: 357.29827376582836
---
info:
event_type: RESPONSE_RECEIVED
stamp:
sec: 1761419221
nanosec: 801765763
client_gid:
- 1
- 15
- 235
- 125
- 217
- 40
- 45
- 142
- 0
- 0
- 0
- 0
- 0
- 0
- 20
- 3
sequence_number: 1
request: []
response:
- result:
x: 636.4710139930166
y: 1184.2409811751827
z: 357.29827376582836
---
For the service client, the output is the same as with the service client without introspection.
[INFO] [1761419221.816188138] [add_points_service_client]: The result was (636.4710139930166, 1184.2409811751827, 357.29827376582836)
Nothing is output in the service server, because our service server is not supposed to output anything to the screen.