ROS Basics – Implement Action (Action Server)
In previous blog, we have define an action message named “Timer.action”. Now we will learn how to implement it by writing a program for action server. Like topics and services, actions are also implemented as callback-based mechanism. In order to implement action, a package “actionlib” is used.
Creating Action Server
We can create an action server that may implement any action available in ROS. However, here we will use “Timer.action”, that we have defined ourselves in previous blog. We will use the class “SimpleActionServer” of the package “actionlib” to implement a basic action server.
Again we will create action server file in the same package “ros_basics” that we are already using for explaining various examples.
Go to the package
Run the command
(To verify that the action_server file is not already created.
Now create the file “action_server.py”
For this run the command
Run the command to give execute permission
chmod +x action_server.py
Open the file using “gedit” command
Run in terminal
Copy and paste following code in it.
#! /usr/bin/env python import rospy import time import actionlib from ros_basics.msg import TimerAction, TimerGoal, TimerResult def do_timer(goal): start_time = time.time() time.sleep(goal.time_to_wait.to_sec()) result = TimerResult() result.time_elapsed = rospy.Duration.from_sec(time.time() - start_time) result.updates_sent = 0 server.set_succeeded(result) rospy.init_node('timer_action_server') server = actionlib.SimpleActionServer('timer', TimerAction, do_timer, False) server.start() rospy.spin()
All above executed commands are shown in figure below
The file “action_server.py” will look like in figure below
Save and close the file
Explaining the Code in Python file “action_server.py”
Now we will go through the key parts of the code
(“time” package is imported to use the timer functionality of the server)
(“actionlib” package is imported as it provides the class SimpleActionServer, that we will use in our code)
from ros_basics.msg import TimerAction, TimerGoal, TimerResult
(We have already learned that after executing “catkin_make”, many classes were auto-generated from our “Timer.action” file. We are importing three of those classes)
start_time = time.time()
(A function do_timer() is defined. This function will be invoked when the server node receives a new goal. The argument passed to this function is an object of TimerGoal class. It corresponds to the goal part of the file “Timer.action” that we defined in previous blog. The “time_to_wait” field is a variable in goal part of the file “Timer.action”. We used standard python time.sleep() function to sleep for the time requested in the goal.)
result = TimerResult()
result.time_elapsed = rospy.Duration.from_sec(time.time() – start_time)
result.updates_sent = 0
(In these code of line, we created an object result of the class TimerResult. This corresponds to the result part of the file Timer.action. This result has two variables “time_elapsed”, and “updates_sent” as defined in Timer.action file. We calculated the time by subtracting saved start time from current time. We also set updates_sent equal to 0. Because so far we are not sending any updates).
(In the final line of the function, we tell SimpleActionServer that we achieved the goal successfully by calling the function set_succeeded, and pass the result.
(Initialize the node timer_action_server)
server = actionlib.SimpleActionServer(‘timer’, TimerAction, do_timer, False)
(We are now creating an object “server” for SimpleActionServer class. The first argument “timer” is the name of the server that will be advertised (we will see it when server is running). The second argument “TimerAction “ is the type of the action that server is handling. The third argument “do_timer” is a callback function. The fourth argument is “False” which means we disabled auto-starting feature of the server. An then we started the server explicitly using start() function of the server in next line)
(we used ROS spin() loop to wait for the goals to arrive)
Executing “action_server.py” file
Now we will execute the file action_server.py, and will see if every thing is working fine.
Open a terminal and run the command.
rosrun ros_basics action_server.py
The command terminal will look like figure below
Open a new terminal and run the command
you can see a list of topics as follows
/rosout /rosout_agg /timer/cancel /timer/feedback /timer/goal /timer/result /timer/status
here, timer is the action server (or we can say timer name-space) and five topics are listed which are used to manage the action.
We can further see the types of topics.
Run the command
rostopic info /timer/goal
We can see the information about topic in figure below
It can be viewed that the topic timer/goal is of type “ros_basics/TimerActionGoal” message.
We can check the details of the message as follows
run the command
rosmsg show ros_basics/TimerActionGoal
We can see that there is a variable named time_to_wait of type duration which can be accessed as “goal.time_to_wait”. However, we can see extra fields in the message. These extra fields are used by action server and action client to keep track of the process.
However, we do not need to bother about extra fields, we only work with the TimerGoal message, that we have defined in our “.action” file.
We can also see the details of the TimerGoal message
Run the command in terminal
rosmsg show TimerGoal
You can see in figure below that only our defined fields will be displayed now.
As you have successfully implemented action server and verified that it is running. In next blog we will create an action client that will communicate with the action server.