Skip to content
Tom Howard edited this page Jan 19, 2021 · 24 revisions

Week 4: ROS Services

You should be able to complete this week's work within the allotted 2-hour lab session.

Introduction

This week you will learn about another method to transfer data between nodes and invoke actions on a ROS Robot using 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 actions or operations.

Intended Learning Outcomes

By the end of this week's lab session, you will be able to:

  1. Recognise how ROS Services differ to the standard the standard publisher-subscriber approach and identify appropriate use-cases for this type of messaging system
  2. Implement Python node pairs to observe how services work
  3. Develop Python Service nodes of your own to implement some basic obstacle avoidance using data from the LiDAR sensor as the feedback signal

Getting Started

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).

Restoring your Environment

Remember that any work that you do within this WSL/Ubuntu environment will not be preserved between sessions and across different University computers. Follow these steps to restore the work that you have done so far in these lab sessions:

  1. TODO...

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:

A Gazebo Simulation of a TurtleBot3 Waffle in an empty world

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 it can't continue until it receives a response. This can be useful for a range of different scenarios, for example:

  • 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, or until an image has been captured, and then turn it off again
  • Remember that ROS is network-based so you might want to offload some computations to a remote computer when a certain piece of data is available, and then get the remote computer to send back a result once it's done all the hard work

You will observe 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\n",

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 service_exercises 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 service_exercises package and define ropsy, geometry_msgs and srv_examples as dependencies:

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

     [TERMINAL 2] $ roscd service_exercises/src
    
  3. Create a file called move_server.py:

     [TERMINAL 2] $ touch move_server.py
    
  4. and set this to be executable:

     [TERMINAL 2] $ chmod +x move_server.py
    
  5. Open the file in Gedit, 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.

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

     [TERMINAL 2] $ rosrun service_exercises move_server.py
    

    You should see the message:

     [INFO] [timestamp]: the move_service server is ready to be called...
    
  7. Then open up a new terminal window (TERMINAL 3)

  8. 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 above listed here.

  9. 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: [not important]
     Type: srv_examples/SetBool
     Args: boolean_request
    

    You will notice that the node name is as we set in our Python code:

    rospy.init_node('move_service_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)

  10. 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:

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

    which should autocomplete the rest of the command for us:

     rosservice call /move_service "boolean_request: false"
    
  11. Press [ENTER] to issue this command and make a call to the service. You should see the following response:

     boolean_response: False
     response_message: "Nothing happened, set boolean_request to true next time."
    
  12. Arrange your ROSDS windows so that you can see:

    1. The terminal where you launched the service (TERMINAL 2)
    2. The gazebo simulation with your robot in
    3. The terminal that you just issued the rosservice call command (TERMINAL 3)

    In TERMINAL 3 enter the rosservice call command again, but this time setting the input argument to True. Observe the response to both the simulated robot in Gazebo and TERMINAL 2.

     **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: [not important]
Type: srv_examples/SetBool
Args: boolean_request

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

srv_examples/SetBool
  1. The service message is part of a package called srv_examples
  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 show srv_examples/SetBool:

bool boolean_request
---
bool boolean_response
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 boolean_request
---             ---
Response        bool boolean_response
                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 earlier) will then send data back to the caller in the format specified in the Response section of the message. In the case of the srv_examples/SetBool service, we need to provide a boolean input called boolean_request in order to call the service, and our Server then needs to provide a response which has two variables: a boolean flag called boolean_response and a text string called response_message.

← Week 3: Odometry-Based Navigation and SLAM | Week 5: TODO... →