Part 4: ROS 2 Services
Introduction¶
Exercises: X
Estimated Completion Time: Y hours
Aims¶
In this part you will learn about ROS Services, a communication method that facilitates request-response interactions between nodes. You will understand how to use ROS services in combination with standard publisher/subscriber principles to enhance control for specific operations. Additionally, you'll create custom messages and services for tailored communication.
Intended Learning Outcomes¶
By the end of this session you will be able to:
- Recognise how ROS Services differ from the standard topic-based publisher-subscriber approach, and identify appropriate use-cases for this type of messaging system.
- Implement Python node pairs to observe services in action, and understand how they work.
- Invoke different services using a range of service message types.
- Develop Python Service nodes of your own to perform specific robotic tasks.
- Harness Services, in combination with LiDAR data, to implement a basic obstacle avoidance behaviour
- Develop custom ROS messages and services (still need to think about the task for this)
- Demonstrate your understanding of ROS2 so far by developing a Python node which incorporates elements from this and previous parts of this course.
Quick Links¶
Additional Resources¶
Prerequisites¶
Before we begin, ensure that you have the following:
- ROS2 Humble installed on your system
- Cloned the tuos package from github
- Basic understanding of ROS2 concepts like nodes and topics
Getting Started¶
Step 1: Launch your ROS Environment
If you haven't done so already, launch your ROS environment now:
Step 2: Restore your work (todo)
Step 3: Launch VS Code (todo)
Step 4: Make Sure The Course Repo is Up-To-Date
Once again, it's worth quickly checking that the Course Repo is up-to-date before you start on the Part 4 exercises. Go back to Part 1 if you haven't installed it yet (really?!). For the rest, run the following commands:
Now build with colcon:
Finally, re-source the environmentStep 5: Launch the Robot Simulation¶
From TERMINAL 1, launch the TurtleBot3 Waffle "Empty World" simulation:
TERMINAL 1:
...and then wait for the Gazebo window to open:An Introduction to Services¶
So far, we've learnt about ROS topics and messages, and how individual nodes can access data on a robot by simply subscribing to topics that are being published by any other node on the system. In addition to this, we also learnt how any node can publish messages to any topic: this essentially broadcasts the data contained in the message across the ROS Network, making it available to any other node on the network that may wish to access it.
ROS2 uses interface as a communication structure that allow different nodes to exchange data. These interfaces are broadly categorized into three types:
- Messages
- Services
- Actions
We have already learned how to use Messages
in part 2 and now we will learn in detail about ROS2 Services
.
These are different to messages in that "Service calls" (that is, the process of requesting a service) occur only between one node and another:
- One node (a Service Client) sends a Request to another node.
- Another node (a Service Server) processes that request, performs an action and then sends back a Response.
Services are Synchronous (or sequential): When a ROS node sends a request to a service (as a Service Client) it can't do anything else until the service has been completed and the Service Server has sent a response back. This can be useful for a few reasons:
-
There can be multiple service clients using the same service but only one service server for a service.
-
Discrete, short-duration actions: A robot might need to do something before it can move on to something else, e.g.:
- A robot needs to see something before it can move towards it.
- High definition cameras generate large amounts of data and consume battery power, so you may wish to turn a camera on for a specific amount of time (e.g. until an image has been captured) and then turn it off again.
-
Computations: Remember that ROS is network-based, so you might want to offload some computations to a remote computer or a different device on a robot, e.g.:
- A client might send some data and then wait for another process (the server) to process it and send back the result.
It's also worth noting that any number of ROS Client nodes can call a service, but you can only have a single Server providing that particular service at any one time.
Question
Can you think of any other scenarios where this type of communication protocol might be useful?
Exercise 1: Creating a Service Server in Python and calling it from the command-line¶
To start with, let's set up a service and learn how to make a call to it from the command-line to give you an idea of how this all works and why it might be useful.
-
First open up a new terminal instance (TERMINAL 2) and source your ROS2 environment as you did in part 1:
- Now navigate into the Course Repo
ros2_ws/src/tuos_ros
and run the helper script agian as you did in part 1 to create a new package calledpart4_services
:
TERMINAL 2:
Your terminal will return a message verifying the creation of your package.
- Now navigate into the Course Repo
-
Navigate into the new package directory using cd:
TERMINAL 2:
-
Then navigate into the
scripts
folder in the package directory (using cd) and create an empty file calledmove_server.py
usingtouch
command. -
Open the file in VS Code, copy and paste this code and then save it. (todo: need to add the template)
Note
It's really important that you understand how the code above works, so that you know how to build your own service Servers in Python. Also, make sure that the
move_server
file is executable usingchmod +x
command. -
Next, we need to add our
move_server.py
file as an executable to our package'sCMakeLists.txt
. This will ensure that it then gets built when we runcolcon build
(in the next step):In VS Code, open the
CMakeLists.txt
file that is at the root of yourpart4_services
package directory (ros2_ws/src/part4_services/CMakeLists.txt
). Locate the lines (near the bottom of the file) that read:# Install Python executables install(PROGRAMS scripts/minimal_node.py DESTINATION lib/${PROJECT_NAME} )
Replace
minimal_node.py
withmove_server.py
to define this as a Python executable in your package:i. It's a good practice to run# Install Python executables install(PROGRAMS scripts/publisher.py DESTINATION lib/${PROJECT_NAME} )
rosdep
in the root of your workspace (ros2_ws
) to check for missing dependencies before building: -
Then, use Colcon to build your new package and its contents :
TERMINAL 2:
i. Finally re-source yourbashrc
:
-
Now, we are ready to run the node. Use
ros2 run
and observe what is displayed on the terminal
TERMINAL 2:
You should see this message:
-
Open a new terminal window (TERMINAL 3)
-
While the node is running, use
ros2 service
command to view all the currently active services on the system:
TERMINAL 3:
You should see the/move_service
service that we defined in the Python code:
-
We can find out more about this using the
ros2 service type
command:
TERMINAL 3:
Which should provide the following output:This shows that the
move_service_server
node is usingSetBool
service (or interface) type defined in thestd_srv
packageTip
You can also view the type of all services at the same time by adding
-t
to theros2 service list
command. -
We can also call this service from the command line using
ros2 service call
.In this case,
-
Press Enter to issue the command and make a call to the service. You should see the following response:
-
Arrange the terminals 1 and 3 so that you can see both the Gazebo simulation and the terminal that you just issued the
ros2 service call
command in. -
In TERMINAL 3 enter the
ros2 service call
command again, but this time set thedata
input totrue
. You should be able to see the response of the robot in Gazebo simulation. Switch back to TERMINAL 2 and observe the terminal output there as well.
Summary
In the section above, you learned how to create a Service Server node. This node sits idle and keeps waiting for its service to be called. Then you called the service through command line which prompted the Server to carry out the following tasks defined in the Python code, 1. Start a timer 1. Issue a velocity commnad to the robot to move it forward 1. Wait for 5 seconds 1. Issue a velocity command to stop the robot 1. Get the service Response and issue it as an output to the terminal in which the service is called
Understanding key features of ros2 service¶
In Part 2, you learned how to find out more about a particular message type, using the ros2 interface show
command. You can do the same to find out the details of service type as follow:
Terminal 3
which will give the following output:bool data # e.g. for hardware enabling / disabling
---
bool success # indicate successful run of triggered service
string message # informational, e.g. for error messages
The Format of SetBool Service¶
The service above is structured in two parts separated by three hyphens (---
). The part above the hyphens is called the Service Request while the part below is Service Response:
bool data <-- Request
---
bool success <-- Response (Parameter 1 of 2)
string message <-- Response (Parameter 2 of 2)
In order to Call a service, we need to provide data to it in the format specified in the Request section. A service Server (like the Python node we created above) will then send data back to the caller in the format specified in the Response section.
The std_srvs/srv/SetBool
service that we're working with here has one request parameter:
- A boolean input called
data
...which is the only thing we need to send to the Service Server in order to call the service.
There are then two response parameters:
- A boolean flag called
success
- A text string called
message
...both of these will be returned to the client, by the server, once the Service has completed.
Exercise 2: Creating a Python Service Client Node¶
Instead of calling a service from command-line we can also build Python Service Client Nodes to do the same. In this exercise you will learn how this is done.
-
TERMINAL 3 should be idle, so from here navigate to the
scripts
folder within thepart4_services
package that we created earlier:
TERMINAL 3:
-
Create a new file called
move_client.py
-
Now as you did in the previous exercise, open the VS Code, copy and paste this code and then save it.
Note
Once again, be sure to read the code and understand how this Python Service Client Node works too!
-
Return to TERMINAL 3 and launch the node using
ros2 run
:
The response should be exactly the same when we called the service from the command line.
Exercise 3: Learn to create custom services¶
In previous exercises you learned about messages, topics and services by using the predefined definitions of them. While using predefined interfaces is considered a good practice, it is also important to know how you can custom define these interfaces based on your own need. This exercise will teach you, step-by-step, how to create custom service definition and use it to move the robot to the requested position (providing x and y coordinates).
Procedure
- Close down the Service Server that is currently running in TERMINAL 2
-
Navigate to your
part4_services
package
TERMINAL 2:
and make a new directorysrv
by running the following command:
-
Now navigate into the newly created directory
srv
and create new file calledMoveToPosition.srv
Note
It is important that your file name should end with
.srv
extension as this identifies the file as a ROS service. -
As we learned earlier, a service file consists of two parts:
Request
andResponse
. Here we will provide our own definition for each one of these parts as follow:The service takes in two user inputsfloat32 goal_x <-- request parameter 1 of 2 float32 goal_y <-- request parameter 2 of 2 --- bool success <-- response
goal_x
andgoal_y
of typefloat
for thex
andy
coordinates to where the robot needs to move.
-
Open the VS Code, copy and paste the above lines and save the file.
-
We need to add a few lines in the
CMakeList.txt
to convert the defined service into language-specific code (C++ and Python) and make it usable: -
Now open the
Package.xml
file and add the following lines:These lines specify the dependencies required to run the custom service.<build_depend>rosidl_default_generators></build_depend> <exec_depend>rosidl_default_runtime></exec_depend> <member_of_group>rosidl_interface_packages></member_of_group>
-
Next, navigate to the
scripts
folder of thepart4_services
package and create an empty file calledMoveToPosition.py
. -
Open the newly created file in VS Code. Copy and paste the code provided here.
-
Now modify the code as follow:
- Change the imports to utilise the service we just created
- Develop the callback_function() to:
- Process the two parameters that will be provided to the server via the `service request
- Retrieve the current position and calculate the difference to goal
- Generate movement command to the specific coordinates
- Return a correctly formatted service response message to the service caller
- Launch the server node using
ros2 run
command from TERMINAL 2 andcall
the service from the command-line usingros2 service call
in TERMINAL 3 as you did earlier
-
Make sure you build the package in the root directory of ros2 workspace using
colcon
and source the environment:Tip
For convenience, you can use a handy alias
src
instead of writing the wholesource ~/.bashrc
A recap on everything you've learnt so far...¶
You should now hopefully understand how to use the ROS2 Service architecture and understand why, and in what context, it might be useful to use this type of communication method in a robot application.
Remember
Services are synchronous and are useful for one-off, quick actions or for offloading jobs or computations that might need to be done before something else can happen. (Think of it as a transaction that you might make in a shop: You hand over some money, and in return you get a chocolate bar, for example!)
Exercise 4: Creating your own Service¶
In this exercise you will create your own service Server to make the Waffle perform a specific movement for a given amount of time and then stop.