Skip to content
Tom Howard edited this page Apr 6, 2022 · 24 revisions

ROS Services

You should be able to complete most of the exercises on this page within a two-hour lab session, but you might wish to spend a bit more time on the final exercise.

Quick Links

Exercises

Additional Resources

Introduction

In this session you will learn about another communication method that can be used to transmit data/information and invoke actions across a ROS Network: ROS Services. You will learn how ROS Services can be used in combination with the standard publisher/subscriber principles that you already know about to control a robot more effectively for certain operations.

Intended Learning Outcomes

By the end of this session you will be able to:

  1. Recognise how ROS Services differ from the standard topic-based publisher-subscriber approach, and identify appropriate use-cases for this type of messaging system.
  2. Implement Python node pairs to execute services and observe how they work.
  3. Apply the same principles to a number of different service message types.
  4. Develop Python Service nodes of your own to perform specific robotic tasks.
  5. Harness Services, in combination with LiDAR data, to implement some basic obstacle-avoidance.
  6. Demonstrate your understanding of ROS so far by developing a Python node which incorporates elements from this and previous parts of this course.

Getting Started

  1. Once again, launch your WSL-ROS environment by running the WSL-ROS shortcut in the Windows Start Menu, which will build the WSL-ROS environment and launch an Ubuntu terminal instance in the Windows Terminal (TERMINAL 1).
  2. Also get VS Code up and running correctly.

Restoring your Environment

Remember to restore your work from last week by running the restore script in TERMINAL 1 now:

[TERMINAL 1] $ wsl_ros restore

Launching the Robot Simulation

From TERMINAL 1, launch the TurtleBot3 Waffle "Empty World" simulation:

[TERMINAL 1] $ roslaunch turtlebot3_gazebo turtlebot3_empty_world.launch

...and wait for the Gazebo window to open:

Remember: You could also use the tb3_empty_world command-line alias to launch the simulation instead!

Services

In weeks 1 & 2 we 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, where this essentially broadcasts the data contained in the message to any other node on the system that wants to access it.

Another way to pass data between ROS Nodes is by using 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:

  1. One node (a Service Client) sends a Request to another node.
  2. Another node (a Service Server) processes that request, performs an action and then sends back a Response.

Services are therefore Synchronous: 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 number of different scenarios:

  • Discrete, short-duration actions:
    • A robot might need to do something before it can move on to something else, i.e.: it 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 is also worth noting that any number of ROS Client nodes are able to call a single service, but you can only have a single Server node providing a given service.

Think about some other scenarios where it might be useful to use the ROS Service architecture.

You will explore how all this works in the next two exercises, where you will create service Server and Client nodes in Python, launch them from the command-line and observe the outcomes.

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.

  1. First open up a new terminal instance (TERMINAL 2) and create a package called week4_services using the catkin_create_pkg tool as you have done in previous weeks:

    1. Navigate to the catkin_ws/src directory:

       [TERMINAL 2] $ cd ~/catkin_ws/src
      
    2. Create the week4_services package and define rospy, geometry_msgs and com2009_msgs as dependencies:

       [TERMINAL 2] $ catkin_create_pkg week4_services rospy geometry_msgs com2009_msgs
      
    3. Run catkin build on the package:

       [TERMINAL 2] $ catkin build week4_services
      
    4. And then re-source your environment:

       [TERMINAL 2] $ src
      
  2. Then, navigate to the package src folder that should have just been created:

     [TERMINAL 2] $ roscd week4_services/src
    
  3. Create a file called move_server.py (using touch) and set this to be executable (using chmod).

  4. Then, open the file in VS Code, copy and paste the code provided here and then save it.

    Note: It is important that you understand how the code above works so that you know how to build your own service Servers in Python. For more information have a look at the Explainer below the code.

  5. Return to the terminal window and launch the node using rosrun:

     [TERMINAL 2] $ rosrun week4_services move_server.py
    

    You should see the message:

     [INFO] [#####]: the 'move_service' Server is ready to be called...
    
  6. Then open up a new terminal window (TERMINAL 3)

  7. We can use the rosservice command to view all of the services that are currently active on our system:

     [TERMINAL 3] $ rosservice list
    

    You should see the /move_service service that we defined in the Python code:

    service_name = "move_service"
  8. We can find out more about this using the rosservice info command:

     [TERMINAL 3] $ rosservice info /move_service
    

    Which should provide the following output:

     Node: /move_service_server
     URI: #####
     Type: com2009_msgs/SetBool
     Args: request_signal
    

    You will notice that the node name is /move_service_server, as set in our Python code when we initialised the node:

    rospy.init_node(f"{service_name}_server")

    Type tells us the type of message this service uses and we'll look at this in more detail later. Args tells us what input arguments we need to supply to the service in order to make a valid service call (or Request).

  9. We can now call this service from the command-line using the rosservice command again. The autocomplete functionality in the terminal can help us format this message correctly. Type the following text followed by a space and two tabs as illustrated:

     [TERMINAL 3] rosservice call /move_service[SPACE][TAB]
    

    which should autocomplete the rest of the command for us:

     [TERMINAL 3] $ rosservice call /move_service "request_signal: false"
    
  10. Press [ENTER] to issue this command and make a call to the service. You should see the following response:

     response_signal: False
     response_message: "Nothing happened, set request_signal to 'true' next time."
    
  11. Arrange your windows so that you can see both the Gazebo simulation with your robot in, and the terminal that you just issued the rosservice call command (TERMINAL 3).

  12. In TERMINAL 3 enter the rosservice call command again, but this time setting the input argument to true. Observe the response to the simulated robot in Gazebo. Switch back to TERMINAL 2 and observe the terminal output here too.

What happens??

Summary:

You have just created a node in Python to launch a service. This node acted as a Server and waited indefinitely for the service to be called. We then issued the call to the service via the command-line, which then prompted our Service Server to carry out the tasks that we had defined within the Python code, namely:

  1. Start a timer.
  2. Issue a velocity command to the robot to make it move forwards.
  3. Wait for 5 seconds.
  4. Issue a velocity command to make the robot stop.
  5. Prepare a Service Response and issue this to the terminal in which we called the service (TERMINAL 3).

Using rossrv

In the previous exercise we used rosservice list to identify all the services that were currently active on the ROS system. We then used rosservice info to find out a bit more about the service that we had launched with our Python node (which we called /move_service).

$ rosservice info /move_service:

Node: /move_service_server
URI: #####
Type: com2009_msgs/SetBool
Args: request_signal

Type tells us the type of message this service uses. Just like a topic message there are two parts to this definition:

com2009_msgs/SetBool
  1. The service message is part of a package called com2009_msgs
  2. The message itself is called SetBool

We can find out more about this using the rossrv command, which has the same usage as the rosmsg command that you have already used previously (for interrogating topic messages). rossrv gives us information about all the service messages that are installed on our system and that are available for us to use in our ROS programs:

$ rossrv info com2009_msgs/SetBool:

bool request_signal
---
bool response_signal
string response_message

The Format of a Service Message

As you can see from above, service messages have two parts to them, separated by three hyphens (---). Above the separator is the Service Request portion of the message, and below it is the Service Response:

Request         bool request_signal
---             ---
Response        bool response_signal
                string response_message

In order to Call a service, we need to provide data to it in the format specified in the Request section of the message. 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 of the message. In the case of the com2009_msgs/SetBool service, we need to provide a boolean input called request_signal in order to call the service, and our Server then needs to provide a response which has two variables: a boolean flag called response_signal and a text string called response_message.

Exercise 2: Creating a Python Service Client Node

As well as calling a service from the command-line we can also build Python nodes to do the same thing (i.e. we can build Python Service Client Nodes). In this exercise you will learn how this is done.

  1. TERMINAL 3 should be idle, so from here navigate to the src folder within the week4_services package that we created earlier:

     [TERMINAL 3] $ roscd week4_services/src
    
  2. Create a new file called move_client.py and make sure that this is executable.

  3. Launch the file in VS Code, copy and paste the code from here and then save the file.

    Note: Once again, be sure to read the explainer below the code, and make sure that you understand how this Python Service Client Node works too!

  4. Return to TERMINAL 3 and launch the node using rosrun:

     [TERMINAL 3] $ rosrun week4_services move_client.py
    

    The response should be exactly the same as observed in Exercise 1.

Exercise 3: Making and calling your own Service

In this exercise you will create your own service Server to make the TurtleBot3 perform a specific movement for a given amount of time and then stop.

A service message called com2009_msgs/TimedMovement has already been set up to help you do this. Interrogate this using the rossrv command (as described above) to work out how to use this message in your Python Server node.

The service should respond to four different movement commands to invoke four different actions:

  1. Move forwards.
  2. Move backwards.
  3. Turn left.
  4. Turn right.

The Server should make the robot perform the desired action for a duration that is also specified within the service message (in seconds).

Procedure:

  1. Close down the Service Server that is currently running in TERMINAL 2.

  2. Create a new node in the week4_services package that you have been working in so far.

    1. Navigate to the week4_services/src folder using roscd:

       [TERMINAL 2] $ roscd week4_services/src
      
    2. You can use the move_server.py node that you created earlier as a starting point if you want to. Copy the file and rename it timed_move_server.py using the cp command:

       [TERMINAL 2] $ cp move_server.py timed_move_server.py
      
  3. Launch your new timed_move_server.py file in VS Code and modify it as follows:

    1. Change the imports to utilise the correct service message type (com2009_msgs/TimedMovement).
    2. Modify the rospy.Service call to use the TimedMovement service message type.
    3. Develop the callback_function() to:
      1. Process the two bits of data that will be provided to it via the service_request input argument (see the Request portion of the TimedMovement service message).
      2. Make the robot perform the correct action.
      3. Return a correctly formatted service response message to the service caller.
  4. Launch your server node using rosrun from TERMINAL 2 and call the service from the command-line using the rosservice call command in TERMINAL 3, as you did earlier).

What You Have Learnt So Far

You should now understand how to use the ROS 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!)

Over the last four weeks you have learnt how to use a range of key ROS tools, and you should now understand some of the ways that ROS works and how you might approach a robot programming task using this framework. In the final exercise this week we will consolidate some of the things that you should now know:

  • How to publish and subscribe to topics.
  • How to make a robot move.
  • How to interpret Laser Displacement Data from the LiDAR sensor.
  • How to invoke an action using a ROS Service.
  • How to develop ROS Nodes in Python, and how to use the Python Class Structure.

Manipulating the Environment in Gazebo

In order to carry out the last exercise you will also need to be able to manipulate the robot's simulated environment using some basic tools in Gazebo. First, make sure that there are no active processes running in TERMINALS 2 or 3, but leave the Gazebo simulation in TERMINAL 1 running.

In the Gazebo simulation window, use the "Box" tool in the top toolbar to place a box in front of the robot:

Use the "Scale Mode" button to resize the box and use the "Translation Mode" button to reposition it.

Once you are happy with this, right click on the object and select "Delete" to remove it from the world.

Exercise 4: Approaching an object using a Service and closed-loop control

For this exercise you will build another Python Server node which must perform the following tasks:

  1. Set the robot moving forward towards an object placed in front of it in its simulated world. This should be achieved by publishing a velocity command to the /cmd_vel topic.
  2. The server node must then stop the robot before it hits the obstacle that you have placed in front of it by subscribing to the /scan topic and monitoring distance information from the LiDAR sensor telling us how far away the object is.
  3. The server must do this by considering two inputs received from a Service Request:
    1. The speed (in m/s) at which to approach the object.
    2. The distance (in meters) at which the robot must stop in front of it.
  4. A service message called com2009_msgs/Approach is available for you to use for this exercise. Use this to build your service server. Remember, you can find out more about this message using rossrv info.
  5. You haven't really had to work with the LiDAR data published to the /scan topic yet, so you might want to consider this suggested approach for building a /scan callback function.

Note: You should use a class structure in your Python code here. Consider the design of the subscriber node from Week 1 or the move_square node from Week 3 to give you an idea of how to approach this.

Wrapping Up

In this session you have learnt about ROS Services and why they might be useful for a Robot:

  • Services differ from standard topic-based communication methods in ROS in that they are a direct form of communication between one node and another.
  • The communication between the two nodes is sequential or synchronous: once a service Caller has called a service, it cannot continue until it has received a response.
  • This is useful for controlling quick, short-duration tasks or for offloading computations (which could perhaps also be considered decision making).

Having completed this week's exercises, you should now be able to:

  • Create and execute Python Service Servers.
  • Create and execute Python Service Callers, as well as call services from the command-line.
  • Implement these principles with a range of different service message types to perform a number of different robot tasks.
  • Use LiDAR data effectively for basic closed-loop robot control.
  • Develop Python nodes which also incorporate principles from the previous three weeks of this course:
    • Publishing and subscribing to topics.
    • Controlling the velocity and position of a robot.
    • Using the Python Class architecture.
    • Harnessing ROS and Linux command-line tools .

Saving your work

Remember, the work you have done in this WSL-ROS environment today will not be preserved for future sessions or across different University machines automatically! To save the work you have done here today you should now run the following script in any idle WSL-ROS Terminal Instance:

$ wsl_ros backup

Navigating This Wiki:
← Week 3: Advanced Navigation and SLAM [Previous] | [Next] Week 5: ROS Actions →

Clone this wiki locally