[ROS in 5 mins] 033 – How to create a ROS Action Server

[ROS in 5 mins] 033 – How to create a ROS Action Server

Hello ROS Developers!

In today’s video we are going learn how to create a ROS Action Server.

The commands used in this video can be typed on your own computer if you already has ROS installed, but to make things easier, I’ll use Robot Ignite Academy, which is the best tool if you want to Learn ROS Really Fast.

Before we start, if you are new to ROS, I highly recommend you taking any of the following courses on Robot Ignite Academy:

Ok, let’s get started.

Create your own ROS Action Messages

When creating an Action Server you can use existing Action Messages, but since we are here to learn, we are going to create and use our own ROS Messages. We already have a post (https://www.theconstruct.ai/ros-5-mins-032-compile-ros-action-messages/) showing how to create your own ROS Messages and compile them. Please refer to it before proceeding in this post, since we will use the package and messages created there.

Creating the ROS Action Server

If you followed the post aforementioned on how to create your own ROS Messages, with the tree . command on the ~/catkin_ws/src folder you should have a package called actions_tutorial with the WashTheDishes.action action message,  something like the output below:

user:~/catkin_ws/src$ tree .
.
├── actions_tutorial
│ ├── action
│ │ └── WashTheDishes.action
│ ├── CMakeLists.txt
│ ├── package.xml
│ └── src
└── CMakeLists.txt -> /opt/ros/kinetic/share/catkin/cmake/toplevel.cmake

3 directories, 4 files

In order to create the action server, let’s create a folder called scripts on our actions_tutorial package. On the scripts folder let’s create a file called action_server.py and make it executable. Let’s use the commands below to accomplish that:

cd ~/catkin_ws/src/actions_tutorial
mkdir scripts
cd scripts/
touch action_server.py
chmod +x action_server.py

Let’s insert the code below on our action_server.pyand see what it really does:

#! /usr/bin/env python

import rospy
import actionlib
from actions_tutorial.msg import WashTheDishesAction, WashTheDishesFeedback, WashTheDishesResult


class ActionServer():

    def __init__(self):
        self.a_server = actionlib.SimpleActionServer(
            "wash_dishes_as", WashTheDishesAction, execute_cb=self.execute_cb, auto_start=False)
        self.a_server.start()

    def execute_cb(self, goal):

        success = True
        last_dish_washed = ''
        feedback = WashTheDishesFeedback()
        result = WashTheDishesResult()
        rate = rospy.Rate(1)

        for i in range(0, goal.number_of_minutes):
            if self.a_server.is_preempt_requested():
                self.a_server.set_preempted()
                success = False
                break

            last_dish_washed = 'bowl-' + str(i)
            feedback.last_dish_washed = last_dish_washed
            result.dishes_washed.append(last_dish_washed)
            self.a_server.publish_feedback(feedback)
            rate.sleep()

        if success:
            self.a_server.set_succeeded(result)


if __name__ == "__main__":
    rospy.init_node("action_server")
    s = ActionServer()
    rospy.spin()

Let’s check what the first lines do in our code:

#! /usr/bin/env python

import rospy
import actionlib
from actions_tutorial.msg import WashTheDishesAction, WashTheDishesFeedback, WashTheDishesResult

In the first line, we tell Linux that our code is executed with Python. We then import rospy because we are creating our ROS program using python. Additionally, we import actionlib because it is necessary to create our ROS Action Server. Lastly, we import our own Action Messages that will allow our robot to “wash the dishes”.

Let’s now analyze the other lines of code:

class ActionServer():

    def __init__(self):
        self.a_server = actionlib.SimpleActionServer(
            "wash_dishes_as", WashTheDishesAction, execute_cb=self.execute_cb, auto_start=False)
        self.a_server.start()

Here we create our action server class, which we called ActionServer. We then have our default constructor where we define our a_server, which is an instance of actionlib.SimpleActionServer. The first parameter is the name of our server once it is running (we called wash_dishes_as), the second parameter is the specification of our action, in this case, WashTheDishesAction. The execute_cb parameter defines the function that will be executed once a client calls our server.

Now let’s dive into the execute_cb method:

def execute_cb(self, goal):

        success = True
        last_dish_washed = ''
        feedback = WashTheDishesFeedback()
        result = WashTheDishesResult()
        rate = rospy.Rate(1)

        for i in range(0, goal.number_of_minutes):
            if self.a_server.is_preempt_requested():
                self.a_server.set_preempted()
                success = False
                break

            last_dish_washed = 'bowl-' + str(i)
            feedback.last_dish_washed = last_dish_washed
            result.dishes_washed.append(last_dish_washed)
            self.a_server.publish_feedback(feedback)
            rate.sleep()

        if success:
            self.a_server.set_succeeded(result)

Once the action server is called, our execute_cb method is executed with the goal provided by the user. In the first lines, we basically define the variables we are going to use.

Our action is supposed to wash dishes for the number of minutes provided by the user on the goal.number_of_minutes parameter, but since we are not really washing dishes, we are basically iterating on that number and sending a feedback message to the user.

A ROS Action normally takes a long time to finish, so the user can change his mind and cancel it at any time. We check if this is the case with if self.a_server.is_preempt_requested(): . If so, we break the loop and our server finishes its work.

If the user didn’t cancel, we keep doing our job, which will be washing dishes. We fill the feedback and resultvariables with the last dish washed. If you remember the post mentioned at the beginning, we see that our action has int32 number_of_minutes as the goal, string last_dish_washed as the feedback and string[] dishes_washed as a result.

Once the robot worked the for the time asked on the goal.number_of_minutesparameter, we just send the result to the user with self.a_server.set_succeeded(result).

The last part of the code (shown below) we basically instantiate our ROS Node as well as the Action Server. We call ros.spin() to keep our program running forever, until someone kills it with ctrl+c .

if __name__ == "__main__":
    rospy.init_node("action_server")
    s = ActionServer()
    rospy.spin()

Awesome, we now have everything in place. Let’s now run our server and send it a goal.

Running the ROS Action Server

To launch our server, the messages need to be compiled. If you missed this step when creating your message, you can just do it with the commands below:

cd ~/catkin_ws/
catkin_make

If everything went ok, you should have a message at the end saying  [100%] Built target actions_tutorial_generate_messages.

With the messages compiled, we need to source our catkin_ws just to make sure ROS can find our compiled messages. We will do that on the same shell (terminal) used to launch our server.

Ok, let’s launch our server:

source ~/catkin_ws/devel/setup.bash
rosrun actions_tutorial action_server.py

If no error message is shown, our service is running. We can confirm that by running the command rosnode list | grep server which should output /action_server, since is the name we defined on our default constructor.

We can confirm that our node is providing the wash the dishes service with rosnode info /action_server, which should output something like:

user:~$ rosnode info /action_server
--------------------------------------------------------------------------------
Node [/action_server]
Publications:
 * /rosout [rosgraph_msgs/Log]
 * /wash_dishes_as/feedback [actions_tutorial/WashTheDishesActionFeedback]
 * /wash_dishes_as/result [actions_tutorial/WashTheDishesActionResult]
 * /wash_dishes_as/status [actionlib_msgs/GoalStatusArray]

Subscriptions:
 * /clock [rosgraph_msgs/Clock]
 * /wash_dishes_as/cancel [unknown type]
 * /wash_dishes_as/goal [unknown type]

We see that our node publishes feedback, status and result. If we subscribe to the feedback topic we will see the feedback messages published when the user sends a goal. Let’s subscribe with the following command in a different shell: rostopic echo /wash_dishes_as/feedback

We can also see the result once the action is finished by subscribing to the result topic in a different shell. Let’s do it with the next command: rostopic echo /wash_dishes_as/result

With everything in place, we can now send our goal. With the rostopic info /action_server aforementioned, we see that our node is subscribed to the /wash_dishes_as/goal topic. This is exactly the topic we use to send goals.

In order to send a goal we can publish on that topic with rostopic pub /wash_dishes_as/goal TAB+TAB+TAB

By pressing TAB+TAB ROS automatically completes the type of the message as well as its fields. If everything goes ok you should have something like the output below:

rostopic pub /wash_dishes_as/goal actions_tutorial/WashTheDishesActionGoal "header:
  seq: 0
  stamp:
    secs: 0
    nsecs: 0
  frame_id: ''
goal_id:
  stamp:
    secs: 0
    nsecs: 0
  id: ''
goal:
  number_of_minutes: 0"

You can see that in the last line we have number_of_minutes: 0. With the left arrow key you can reach that field and change its value. Let’s change it to 5 and after the " we write --once to publish the message only once. After that we press enter. To make sure we are doing right, before pressing enter we should have something like:

rostopic pub /wash_dishes_as/goal actions_tutorial/WashTheDishesActionGoal "header:
  seq: 0
  stamp:
    secs: 0
    nsecs: 0
  frame_id: ''
goal_id:
  stamp:
    secs: 0
    nsecs: 0
  id: ''
goal:
  number_of_minutes: 5" --once

You will see nothing on the shell you execute this command, but if you go to the shell were you executedrostopic echo /wash_dishes_as/feedback, you should see 5 feedback messages saying that the dishes are being washed.

Also, if you go to the shell where you typed rostopic echo /wash_dishes_as/result, you should see the result message, like the one below:

user:~$ rostopic echo  /wash_dishes_as/result
WARNING: no messages received and simulated time is active.
Is /clock being published?
header:
  seq: 2
  stamp:
    secs: 4155
    nsecs: 895000000
  frame_id: ''
status:
  goal_id:
    stamp:
      secs: 4150
      nsecs: 891000000
    id: "/action_server-2-4150.891000000"
  status: 3
  text: ''
result:
  dishes_washed: [bowl-0, bowl-1, bowl-2, bowl-3, bowl-4]
---

Note that on the last line we have the name of the dishes washed: dishes_washed: [bowl-0, bowl-1, bowl-2, bowl-3, bowl-4]

A video to make your life easier

Congratulations. You now have an action server fully operational. If you liked the post, great, but if you want also to watch a video showing all the process described in this post, we have it here:

Now that we know how to create an Action Server, let’s learn how to create an Action Client? If you are interested, please have a look at the next post: https://www.theconstruct.ai/ros-5-mins-035-create-ros-action-client/

To conclude, if you liked the video, please give us a thumbs up on YouTube. Additionally, should you have any feedback messages, please leave them on the comments section of the video. You can also subscribe to our youtube channel and press the bell to be notified about the videos we publish every day.

Keep pushing our ROS Learning.

[ROS in 5 mins] 032 – How to compile ROS Action messages

[ROS in 5 mins] 032 – How to compile ROS Action messages

Hello ROS Developers!

In today’s tutorial we are going learn how to create a ROS Action specification file, how to compile ROS Action messages and how to import them.

You can run the commands used along the video in your own computer if you have ROS installed, but you can also use Robot Ignite Academy in case you don’t want to deal with ROS installation. Robot Ignite Academy, by the way, is the best tool if you want to Learn ROS Fast.

Before we start, if you are new to ROS and want complete courses on that subject, I highly recommend you taking any of the following courses on Robot Ignite Academy:

Ok, let’s get started.

Creating a ROS Package

In order to create our custom ROS Action we need a package on the catkin_ws (catkin workspace), so let’s create one called actions_tutorial using the commands below:

source /opt/ros/$ROS_DISTRO/setup.bash

mkdir ~/catkin_ws/src/ -p

cd ~/catkin_ws/src

catkin_create_pkg actions_tutorial rospy std_msgs actionlib_msgs

Ok, so far so good. We now have package called actions_tutorial, now let’s move to the next step.

Creating a ROS Action file

Let’s suppose our robot is able to wash dishes, so, let’s create an action file called WashTheDishes.action and place it inside a folder called action which will be on the package we just created.

cd ~/catkin_ws/src/actions_tutorial

mkdir action

cd action/

touch WashTheDishes.action

Now let’s put some content on that WashTheDishes.action file. Please use the content below:

# Goal
int32 number_of_minutes

---
# Result
string[] dishes_washed

---
# Feedback
string last_dish_washed

If you look carefully on the content of the WashTheDishes.action file you can find 3 sections. Goal, Result and Feedback. Goal is what we send to the Action Server, which in this case will be the number_of_minutes that our robot need to work, the Result is the list named dishes_washed that we will receive once the robot finishes working. Given that an action can take a long time depending on the goal parameter, we have a Feedback section that we will receive whenever the robot finishes washing a dish. The feedback in this case we named last_dish_washed.

Now let’s go to the next step

Compiling a ROS Action file

On our actions_tutorial package you have a file called CMakeLists.txt that is created automatically when we create a package. That file is used to compile our package. If we look at that file you can see some instructions on how to modify it to compile our own messages. The instructions on that file says that we have to modifly the package.xmlfile that is located inside our package folder and add message_generation and message_runtime on that file. To make it easier, below we have the full content of the  package.xml after the modifications.

<?xml version="1.0"?>
<package format="2">
<name>actions_tutorial</name>
<version>0.0.0</version>
<description>The actions_tutorial package</description>

<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<maintainer email="user@todo.todo">user</maintainer>


<!-- One license tag required, multiple allowed, one license per tag -->
<!-- Commonly used license strings: -->
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
<license>TODO</license>

<!-- Use build_depend for packages you need at compile time: -->
<build_depend>message_generation</build_depend>
<!-- Use exec_depend for packages you need at runtime: -->
<exec_depend>message_runtime</exec_depend>

<buildtool_depend>catkin</buildtool_depend>
<build_depend>actionlib_msgs</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<build_export_depend>actionlib_msgs</build_export_depend>
<build_export_depend>rospy</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<exec_depend>actionlib_msgs</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>


<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->

</export>
</package>

The CMakeLists.txt also says that we have to modify that file itself by doing the following:

  1. Add message_generation to thefind_package() section
  2. Add message_runtime to the catkin_package section
  3. Add WashTheDishes.action to the add_action_files section
  4. Uncomment the generate_messages section.

To make your life easier, below we have t he full content of that actions_tutorial/CMakeLists.txt file after our modifications:

cmake_minimum_required(VERSION 2.8.3)
project(actions_tutorial)

## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
actionlib_msgs
rospy
std_msgs
message_generation
)

## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)


## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()

################################################
## Declare ROS messages, services and actions ##
################################################

## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
## * add a build_depend tag for "message_generation"
## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
## but can be declared for certainty nonetheless:
## * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
## * add "message_generation" and every package in MSG_DEP_SET to
## find_package(catkin REQUIRED COMPONENTS ...)
## * add "message_runtime" and every package in MSG_DEP_SET to
## catkin_package(CATKIN_DEPENDS ...)
## * uncomment the add_*_files sections below as needed
## and list every .msg/.srv/.action file to be processed
## * uncomment the generate_messages entry below
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

## Generate messages in the 'msg' folder
# add_message_files(
# FILES
# Message1.msg
# Message2.msg
# )

## Generate services in the 'srv' folder
# add_service_files(
# FILES
# Service1.srv
# Service2.srv
# )

## Generate actions in the 'action' folder
add_action_files(
FILES
WashTheDishes.action
)

## Generate added messages and services with any dependencies listed here
generate_messages(
DEPENDENCIES
actionlib_msgs# std_msgs
)

################################################
## Declare ROS dynamic reconfigure parameters ##
################################################

## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
## * add "dynamic_reconfigure" to
## find_package(catkin REQUIRED COMPONENTS ...)
## * uncomment the "generate_dynamic_reconfigure_options" section below
## and list every .cfg file to be processed

## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
# cfg/DynReconf1.cfg
# cfg/DynReconf2.cfg
# )

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
# INCLUDE_DIRS include
LIBRARIES actions_tutorial
CATKIN_DEPENDS actionlib_msgs rospy std_msgs
DEPENDS system_lib
DEPENDS message_runtime
)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
${catkin_INCLUDE_DIRS}
)

## Declare a C++ library
# add_library(${PROJECT_NAME}
# src/${PROJECT_NAME}/actions_tutorial.cpp
# )

## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/actions_tutorial_node.cpp)

## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
# ${catkin_LIBRARIES}
# )

#############
## Install ##
#############

# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html

## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# install(PROGRAMS
# scripts/my_python_script
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark executables and/or libraries for installation
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
# FILES_MATCHING PATTERN "*.h"
# PATTERN ".svn" EXCLUDE
# )

## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
# # myfile1
# # myfile2
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_actions_tutorial.cpp)
# if(TARGET ${PROJECT_NAME}-test)
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()

## Add folders to be run by python nosetests
# catkin_add_nosetests(test)

Ok, now that we have our CMakeLists.txt and package.xml files modified, we can compile our message using the commands below:

cd ~/catkin_ws/

catkin_make

If everything goes ok, at the end you should see a message saying that your package has been 100% compiled:

user:~/catkin_ws$ catkin_make
Base path: /home/user/catkin_ws
Source space: /home/user/catkin_ws/src
Build space: /home/user/catkin_ws/build
Devel space: /home/user/catkin_ws/devel
Install space: /home/user/catkin_ws/install
####
#### Running command: "make cmake_check_build_system" in "/home/user/catkin_ws/build"
####
-- Using CATKIN_DEVEL_PREFIX: /home/user/catkin_ws/devel
-- Using CMAKE_PREFIX_PATH: /opt/ros/kinetic
-- This workspace overlays: /opt/ros/kinetic
-- Using PYTHON_EXECUTABLE: /usr/bin/python
-- Using Debian Python package layout
-- Using empy: /usr/bin/empy
-- Using CATKIN_ENABLE_TESTING: ON
-- Call enable_testing()
-- Using CATKIN_TEST_RESULTS_DIR: /home/user/catkin_ws/build/test_results
-- Found gmock sources under '/usr/src/gmock': gmock will be built
-- Found gtest sources under '/usr/src/gmock': gtests will be built
-- Using Python nosetests: /usr/local/bin/nosetests-2.7
-- catkin 0.7.14
-- BUILD_SHARED_LIBS is on
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~ traversing 1 packages in topological order:
-- ~~ - actions_tutorial
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- +++ processing catkin package: 'actions_tutorial'
-- ==> add_subdirectory(actions_tutorial)
-- Using these message generators: gencpp;geneus;genlisp;gennodejs;genpy
-- Generating .msg files for action actions_tutorial/WashTheDishes /home/user/catkin_ws/src/actions_tutorial/action/WashTheDishes.action
-- actions_tutorial: 7 messages, 0 services
CMake Warning at /opt/ros/kinetic/share/catkin/cmake/catkin_package.cmake:166 (message):
catkin_package() DEPENDS on 'system_lib' but neither
'system_lib_INCLUDE_DIRS' nor 'system_lib_LIBRARIES' is defined.
Call Stack (most recent call first):
/opt/ros/kinetic/share/catkin/cmake/catkin_package.cmake:102 (_catkin_package)
actions_tutorial/CMakeLists.txt:105 (catkin_package)

...

Scanning dependencies of target actions_tutorial_generate_messages_cpp
[ 83%] Generating C++ code from actions_tutorial/WashTheDishesFeedback.msg
[ 86%] Generating C++ code from actions_tutorial/WashTheDishesGoal.msg
[ 89%] Generating C++ code from actions_tutorial/WashTheDishesActionResult.msg
[ 91%] Generating C++ code from actions_tutorial/WashTheDishesAction.msg
[ 94%] Generating C++ code from actions_tutorial/WashTheDishesResult.msg
[ 97%] Generating C++ code from actions_tutorial/WashTheDishesActionGoal.msg
[100%] Generating C++ code from actions_tutorial/WashTheDishesActionFeedback.msg
[100%] Built target actions_tutorial_generate_messages_cpp
Scanning dependencies of target actions_tutorial_generate_messages
[100%] Built target actions_tutorial_generate_messages

Once our package is successfully compiled, we have to source our catkin_ws in order to let ROS know how to locate our custom action. Let’s do it with the command below:

source ~/catkin_ws/devel/setup.bash 

Ok, so far we have seen how to create and how to compile our custom action. Now let’s import it to make sure it has been successfully compiled. An easy way to do that is by running the command below:

python -c 'from actions_tutorial.msg import WashTheDishesAction; print "Hey, it worked!!!"' 

After running the command I just mentioned you should be able to see the message “Hey, it worked!!!”.

This messages confirms that our message was successfully compiled and imported.

You can also use rosmsg list | grep Dishes to find your message. The output should be something like:

user:~/catkin_ws$ rosmsg list | grep Dishes
actions_tutorial/WashTheDishesAction
actions_tutorial/WashTheDishesActionFeedback
actions_tutorial/WashTheDishesActionGoal
actions_tutorial/WashTheDishesActionResult
actions_tutorial/WashTheDishesFeedback
actions_tutorial/WashTheDishesGoal
actions_tutorial/WashTheDishesResult

So, that is the tutorial for today, guys. Now that you know how to compile your ROS Action Messages, you should keep pushing and learn how to create your own ROS Action Server: https://www.theconstruct.ai/ros-5-mins-033-create-ros-action-server/

We are constantly adding new tutorials here and videos in our YouTube channel.  If you like the content, please consider subscribing to our channel on YouTube, giving us a thumbs up there and even leaving comments on the comments section of the video.

By the way, below we have a video that shows the content of this post:

How to create a ROS Service Client

How to create a ROS Service Client

Learn how to create a ROS Service Client in less than five minutes, and understand the relationship between a ROS Service Server and a ROS Service Client.

Let’s go!

Step1: Create an account and/or Login to Robot Ignite Academy (RIA)

On RIA, you get access to the best online ROS courses and environment. No need to install and set up ROS locally – the only thing you need is a browser!

  • Create an account and/or login here.
  • Launch the ROS Basics in 5 Days Python course.
  • You’ll now have access to the simulation screen with a Notebook, an Editor, four Shells, and a Simulation Window. We are only using the Editor and Shells for this demo.

Step 2: Create a ROS Service Client and execute it!

ROS Services use a client/server approach. A server provides services, a client calls the server to use the services. In this post, we will create a Python-based client that will call a Python-based server in five easy steps minus one!

(1) I wrote about how to create a ROS service server in another post. We will use the server code here. Please follow steps 1 to 5 of the section named “Step 2: Create a ROS Service Server and call it” in that post to create and run the service server.

(2) Create a sos_service_client.py file on Shell #3:

user:~$ cd catkin_ws/src
user:~/catkin_ws/src$ touch sos_service_client.py && chmod +x sos_service_client.py

(3) Open sos_service_client.py in the Editor and copy-paste the following code into it.

#! /usr/bin/env python

import rospy
from std_srvs.srv import Trigger, TriggerRequest

# init a node as usual
rospy.init_node('sos_service_client')

# wait for this sevice to be running
rospy.wait_for_service('/fake_911')

# Create the connection to the service. Remember it's a Trigger service
sos_service = rospy.ServiceProxy('/fake_911', Trigger)

# Create an object of the type TriggerRequest. We nned a TriggerRequest for a Trigger service
sos = TriggerRequest()

# Now send the request through the connection
result = sos_service(sos)

# Done
print result

(4) Run sos_service_client.py in Shell #3:

user:~/catkin_ws/src$ ./sos_service_client.py
success: True
message: "Hey, roger that; we'll be right there!"

Easy-peasy, right? Let’s consolidate the learning in the next step.

Step 3: Master the Concept: Creating a ROS Service Server

  • A ROS Service Server accepts a request and returns a response. In this case, our server is the Python code.
  • A ROS Service Client makes requests to a ROS Service Server. Our “client” in this case was the Python script!

Please see the inline comments for an explanation of the code.

Done!

Extra: Video

Prefer to see the ‘sights and sounds’ version of this post? We made this video just for you!

Feedback

If you are interested in this topic, please check our ROS Basics in 5 Days course where you’ll learn how to create topic, service, and action in ROS.

Did you like this post? Please leave a comment on the comments section below, so we can interact and learn from each other. Thank you!

[ROS in 5 mins] 030 – How to compile ROS Service messages

[ROS in 5 mins] 030 – How to compile ROS Service messages

 

Hello ROS Developers!

In today’s video we are going learn how to create a ROS Service file, how to compile and how to import it.

For that we are going to use Robot Ignite Academy, which is the best tool if you want to Learn ROS Fast.

Before we start, if you are new to ROS, I highly recommend you to take any of the following courses on Robot Ignite Academy:

We love feedback, so, whether you like the video or not, please share your thoughts on the comments section below:)

Step1. Create a project in Robot Ignite Academy(RIA)

We have the best online ROS course available in RIA. It helps you learn ROS in the easiest way without setting up ROS environment locally. The only thing you need is a browser! Create an account here and start to browse the trial course for free now! We’ll use the ROS Basics in 5 Days course unit3 as an example today.

Step2. Create a package

We’ll put our new service message in a new package called services_tutorial

catkin_create_pkg services_tutorial rospy

Then we create the definition of the message called AddTwoNumbers.srv and put it into a folder called srv with the following content.

float32 a
float32 b
---
float32 sum

In the package.xml file, you have to uncomment the following part.

...
<build_depend>message_generation</build_depend>
...
<run_depend>message_runtime</run_depend>
...

You’ll also need to change the following part in the CMakeLists.txt

...
find_package(catkin REQUIRED COMPONENTS
  rospy
  std_msgs
  message_generation
)
...
catkin_package(
  DEPENDS message_runtime
)
...
add_service_files(
  FILES
  AddTwoNumbers.srv
)
...
generate_messages(
  DEPENDENCIES
  std_msgs
)
...

Then we can finally compile the package.

cd ~/catkin_ws
catkin_make
source devel/setup.bash

You can now use your new message type in your code. For example, you can do the following to import it in a python script.

from services_tutorial.srv import AddTwoNumbers

Want to learn more?

You can learn more about ROS topic, service and action in our ROS Basics in 5 Days course.

 

Edit by: Tony Huang

How to Create a ROS Service Server

How to Create a ROS Service Server

Hello ROS Developers! In this post (and embedded video), we will see how to create a ROS Service Server (or simply ROS Service) in just five minutes! We’ll see in it action as well as learn some theory behind it.

Let’s go!

Step1: Create an account and/or Login to Robot Ignite Academy (RIA)

On RIA, you get access to the best online ROS courses and environment. No need to install and set up ROS locally; the only thing you need is a browser!

  • Create an account and/or login here.
  • Launch the ROS Basics in 5 Days Python course.
  • You’ll now have access to the simulation screen with a Notebook, an Editor, four Shells, and a Simulation Window. We are only using the Editor and Shells for this demo.

Step 2: Create a ROS Service Server and call it!

ROS Services use a client/server approach. A server provides services, a client calls the server to use the services.

Here, we’ll create a simple service server based on Python and then call it from the command line, all in five easy steps! (In another post, we’ll see how to call the service server from Python code).

(1) On Shell #1, change to the source directory and create an executable python file (since this is a simple demo, we are not creating a ROS package):

user:~$ cd catkin_ws/src
user:~/catkin_ws/src$ touch sos_service.py
user:~/catkin_ws/src$ chmod +x sos_service.py
user:~/catkin_ws/src$

(2) Open sos_service.py in the Editor and copy-paste the following code into it.

#! /usr/bin/env python
import rospy                                      # the main module for ROS-python programs
from std_srvs.srv import Trigger, TriggerResponse # we are creating a 'Trigger service'...
                                                  # ...Other types are available, and you can create
                                                  # custom types
def trigger_response(request):
    ''' 
    Callback function used by the service server to process
    requests from clients. It returns a TriggerResponse
    '''
    return TriggerResponse(
        success=True,
        message="Hey, roger that; we'll be right there!"
    )

rospy.init_node('sos_service')                     # initialize a ROS node
my_service = rospy.Service(                        # create a service, specifying its name,
    '/fake_911', Trigger, trigger_response         # type, and callback
)
rospy.spin()                                       # Keep the program from exiting, until Ctrl + C is pressed

(3) Run the service server in Shell #1:

user:~/catkin_ws/src$ ./sos_service.py # press enter

(4) In Shell #2, see a list of available services, you should find /fake_911:

user:~$ rosservice list
...
/fake_911
...
user:~$

(5) Call your service in Shell #2:

user:~$ rosservice call /fake_911 "{}"
success: True
message: "Hey, roger that; we'll be right there!"

Let’s wrap up with a bit of “small talk” in the next section.

Step 3: Master the Concept: Creating a ROS Service Server

  • A ROS Service Server accepts a request and returns a response. In this case, our server is the Python code.
  • A ROS Service Client makes requests to a ROS Service Server. Our “client” in this case was the shell!

Please see the inline comments for an explanation of the code.

Done!

Extra: Video

Prefer to see the ‘sights and sounds’ version of this post? We made this video just for you!

Feedback

If you are interested in this topic, please check our ROS Basics in 5 Days course where you’ll learn how to create topic, service, and action in ROS.

Did you like this post? Please leave a comment on the comments section below, so we can interact and learn from each other. Thank you!

Pin It on Pinterest