[ROS in 5 mins] 035 – How to create a ROS Action Client

[ROS in 5 mins] 035 – How to create a ROS Action Client

Hi ROS Developers,

in today’s post, we are going to learn how to create a ROS Action Client, how to call the Action Server and print the feedback and result messages.

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

Let’s get started.

Creating the ROS Action Server

In order to create a ROS Action Client, we first need to create the Action Server. We have already learned how to do that on the previous post: https://www.theconstruct.ai/ros-5-mins-033-create-ros-action-server/

Please make sure you follow that post first since the client we will create here will call that server.

Creating the ROS Action Client

If you followed the post on how to create an Action Server, we should have the following output after running the tree . command on the ~/catkin_ws/srcfolder.

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

4 directories, 5 files

As you can see we have a scripts folder with our action_server.py there. We will create our action_client.py in the same folder. Let’s use the commands below for that:

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

With the code above we created an empty python file and gave it execute permissions. With the tree ~/catkin_ws/src command you should have something like:

tree ~/catkin_ws/src
/home/user/catkin_ws/src
├── actions_tutorial
│   ├── action
│   │   └── WashTheDishes.action
│   ├── CMakeLists.txt
│   ├── package.xml
│   ├── scripts
│   │   ├── action_client.py
│   │   └── action_server.py
│   └── src
└── CMakeLists.txt -> /opt/ros/kinetic/share/catkin/cmake/toplevel.cmake

4 directories, 6 files

Now let’s paste the following code on the action_client.py file:

#! /usr/bin/env python

import rospy
import actionlib
from actions_tutorial.msg import WashTheDishesAction, WashTheDishesGoal

def feedback_cb(msg):
 print 'Feedback received:', msg

def call_server():

    client = actionlib.SimpleActionClient('wash_dishes_as', WashTheDishesAction)

    client.wait_for_server()

    goal = WashTheDishesGoal()
    goal.number_of_minutes = 7

    client.send_goal(goal, feedback_cb=feedback_cb)

    client.wait_for_result()

    result = client.get_result()

    return result

if __name__ == '__main__':

    try:
        rospy.init_node('action_client')
        result = call_server()
        print 'The result is:', result
    except rospy.ROSInterruptException as e:
        print 'Something went wrong:', e

In this file what we do is basically call the Action Server defined in the previous post and print the feedback and the result messages sent by the server.

Running the ROS Action Client

With everything in place, let’s now launch our server and our client and see they work together. To launch the server we can use the command below:

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

and in a different shell we run the next command:

source catkin_ws/devel/setup.bash
rosrun actions_tutorial action_client.py

Everything should have run smoothly and you should see in the output something like:

Feedback received: last_dish_washed: "bowl-0"
Feedback received: last_dish_washed: "bowl-1"
Feedback received: last_dish_washed: "bowl-2"
Feedback received: last_dish_washed: "bowl-3"
Feedback received: last_dish_washed: "bowl-4"
Feedback received: last_dish_washed: "bowl-5"
Feedback received: last_dish_washed: "bowl-6"
The result is: dishes_washed: [bowl-0, bowl-1, bowl-2, bowl-3, bowl-4, bowl-5, bowl-6]

So, congratulations, you now know how to create an Action Server as well as an Action Client.

To make your life easier, we have also prepared a video showing all the process explained in this post.

I hope you have enjoyed the post and the video, if so, please consider giving us a thumbs up. You can also subscribe to our channel and press the bell so that you won’t miss our updates.

We love feedback, so, whether you like the video or not, please share your thoughts on the comments section of the video.

Keep pushing your ROS Learning.

[ROS in 5 mins] 034 – What is ROS Action?

[ROS in 5 mins] 034 – What is ROS Action?

In this post, we will see what a ROS Action is. We’ll also see the difference between a ROS Service and a ROS Action.

Let’s go!

Step 1: Understand blocking and non-blocking activities: two possible ways to buy Pizza

Let’s assume you wish to order some pizza. There are two possible ways you could go about it:

  1. Go the Pizza shop
    1. Place your order.
    2. Wait for the order.
    3. Get the Pizza.
  2. Order online.
    1. Place your order.
    2. Order confirmation notice.
    3. Possibly cancel the order.
    4. Check up your order status once in a while.
    5. Do other things.
    6. Pizza is delivered.

Option 1 is a blocking activity because you have to wait (in theory not able to do anything else) for the pizza, while option 2 is non-blocking because you can do some other things while your order is being processed.

Step 2: Apply blocking and non-blocking concept to ROS

Option 1 (Going to Pizza shop) is similar to a ROS Service, while option 2 is similar to a ROS Action. Not 100% the same, but I suppose you get the point. But what is a ROS Action specifically, in ROS terminology?

An action is an asynchronous call to another node’s functionality. “Asynchronous” means you don’t have to wait for the result. You can do other things pending the result.

The node that provides the functionality has to implement an Action Server (Pizza shop). The node that uses the functionality has to use an Action Client (online ordering system).

Step 3: Dive a bit deeper – ROSified pizza ordering

Now, let’s see what ROS calls each of the steps for buying pizza:

  1. Go the Pizza shop
    1. Place your order (request).
    2. Wait for the order.
    3. Get the Pizza (response).
  2. Order online
    1. Place your order (request).
    2. Order confirmation notice (feedback).
    3. Possibly cancel the order (cancel).
    4. Check up your order status once in a while (status).
    5. Do other things.
    6. Pizza is delivered (response).

To wrap up, consider the image below, saying roughly saying the same thing we have been saying, only in a “thousand words”:

ros_action

 

Extra: Video

Prefer to listen to the “sights and sounds” version of this post? We have one for you below; happy watching!

Further Learning

If you are a ROS beginner and want to learn ROS basics fast, we recommend you take any of the following courses on Robot Ignite Academy:

Feedback

Did you like this post? Whatever the case, please leave a comment in the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS topics, please let us know on the comments area and we will do a post or video about it.

Thank you!

[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:

[ROS Q&A] 131 – Compilation error in ROS Action Server

[ROS Q&A] 131 – Compilation error in ROS Action Server

 

In this video we are going to see why a user gets a compilation error when trying to compile his ROS Action Server node.

This is a video trying to answer the following question posted at the ROS answers forum: https://answers.ros.org/question/294052/compilation-error-in-ros-action-server-code/

Step 1. Create a project in ROS Development Studio(ROSDS)

ROSDS helps you follow our tutorial in a fast pace without dealing without setting up an environment locally. If you haven’t had an account yet, you can create a free account here. Let’s create a new project and call it testing_action.

Step 2. Create package

At first, let’s create the package with dependencies

cd ~/catkin_ws/src
catkin_create_pkg test_action roscpp actionlib actionlib_msgs robot_calibration_msgs

Then we create a file called action_code.cpp under test_action/src folder with the following content from the question

#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include <robot_calibration_msgs/GripperLedCommandAction.h>

typedef actionlib::SimpleActionServer<robot_calibration_msgs::GripperLedCommandAction> led_actn_srvr_t;

class led_action
{

ros::NodeHandle nh;
led_actn_srvr_t ls;
std::string action_name;
public:

led_action(std::string name):
    ls(nh, name, boost::bind(&led_action::execute_cb, this, _1), false),
    action_name(name)
{
    ls.start();
}
void execute_cb(const robot_calibration_msgs::GripperLedCommandActionConstPtr &action)
{
}
};

int main(int argc, char** argv)
{
    return 0;
}

To compile the code, we have to add the following code into BUILD part of the CMakeLists.txt file

add_executable(action_code src_action_code.cpp)
add_dependencies(action_code ${action_code_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(action_code
  ${catkin_LIBRARIES}
)

If we try to compile the code with the following command

cd ~/catkin_ws
catkin_make

We got some error.

Step 3. Solve the problem

It turns out the actionlib generates topics for us in the following format

GripperLedCommandAction

But inside the code, we have to directly use

GripperLedCommand…

So we only have to change one line in the source file to make it work

...
void execute_cb(const robot_calibration_msgs::GripperLedCommandGoalConstPtr &action)
...

Then it compiles successfully!

 

 

Edit by: Tony Huang

// RELATED LINKS

▸ Original question: https://answers.ros.org/question/294052/compilation-error-in-ros-action-server-code/
Robot Ignite Academy
ROS Basics in 5 days (C++)
ROS Development Studio (RDS)


Feedback

Did you like this video? Whatever the case, please leave a comment on the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS topics, please let us know on the comments area and we will do a video about it.

Pin It on Pinterest