| Note: This tutorial assumes that you have completed the previous tutorials: Writing actionlib clients. |
Writing actionlib servers
Description: This tutorial presents the macros offered by actionlib_lisp.Tutorial Level: INTERMEDIATE
Next Tutorial: Using TF with CL_TF
Writing a Fibonacci numbers server
The actionlib_lisp package provides some useful macros for writing actionlib servers with minimum effort.
The code of this example can be found in the actionlib_lisp_examples repository
You can find a full description of creating action messages in the actionlib_tutorial.
Consider the action defined in
$ rosed actionlib_tutorials Fibonacci.action #goal definition int32 order --- #result definition int32[] sequence --- #feedback int32[] sequence
We need to refer to the message structure names "order" and "sequence" later. Note the example does not define an own action, but relies on the action defined in actionlib_tutorials. The procedure is the same for action regardless of whether the server is written in C++, Python or LISP, so refer to the actionlib tutorial to generate the action messages.
The code
Here is the code for a fibonacci server compatible to the examples for c++ and python:
(def-exec-callback fib-callback (order)
"This function takes in the FibonacciGoal message and pursues the action."
(ros-debug (fib callback) "entering callback with goal ~a" order)
;; using a and B to count in Fibonacci steps
(let ((a 1)
(b 1)
(seq (make-array 0 :adjustable t :fill-pointer 0)))
(dotimes (i order)
(when (cancel-request-received)
(ros-debug (fib callback) "goal ~a canceled" order)
(preempt-current :sequence seq)) ;; Note that this exits the callback
;; independently set a to value of b, and b to value of (+ a b)
(psetq
a b
b (+ a b))
;; extend the fibonacci sequence by new element
(vector-push-extend a seq)
(ros-debug (fib callback) "publishing feedback for goal ~a" order)
(publish-feedback :sequence seq)
;; sleeping just to represent long running action
(sleep 1.0))
(ros-debug (fib callback) "succeeding on goal ~a" order)
(succeed-current :sequence seq)))
(defun fib-server ()
(with-ros-node ("fib")
(start-action-server
;; action namespace
"fibonacci"
;; Action Name
"actionlib_tutorials/Fibonacci"
#'fib-callback)))
The code explained
Let's first look at the main function fib-server.
(start-action-server
;; action namespace
"fibonacci"
;; Action Name
"actionlib_tutorials/Fibonacci"
#'fib-callback)This function requires a ROS node to be started, and it creates all necessary topics to communicate with actionlib clients. It gets a namespace and the actiontype ac parameters, and a callback that will be executed for each client call.
The namespace is arbitrary, but this is what clients will need to refer to.
The actiontype is a concatenation of the packaname of the .action file, and the base filename of the .action file. In this case, the action is defined in actionlib_tutorials/actions/Fibonacci.action. Hence the action type is "actionlib_tutorials/Fibonacci".
The callback function calculates the fibonacci numbers in an array. Here are the parts relevant for actionlib:
(def-exec-callback fib-callback (order)
We use the def-exec-callback macro instead of a defun as it provides several convenient properties. As you can see, we could just specify (order) as an argument.
The argument list is treated like the argument list of with-fields: it binds (possibly nested) fields of the goal object. The goal object with field "order" was defined in the .action file, see above.
When using def-exec-callback, it is important that the body of this function must not terminate normally, this will be interpreted as a failure by the macro. Instead, one of:
- (succeed-current ...)
- (preempt-current ...)
- (abort-current ...)
must be called. They all do a dynamic nonlocal exit, meaning the function terminates.
Succeed and abort have obvious semantics. Preempt is special. An action should be preemtable, meaning the client should be able to terminate the action before it finished, leaving a robot in a safe state.
While calculating the fibonacci numbers is not unsafe, the example shows how this can be achieved in a loop:
(when (cancel-request-received)
...
(preempt-current :sequence seq))Here, cancel-request-received is a special variable provided by def-exec-callback, and it is T if the action server got a preempt request. We use the preempt-current macro to terminate the callback.
All of succeed-current, preempt-current, abort-current allow to send results in the result message format by giving a structure with keyword arguments matching the names in the .action file, for convenience, such that roslisp:make-message does not need to be called explicitly.
Finally, the call
(publish-feedback :sequence seq)
is interesting because it publishes a feedback message, and it is given :sequence as a keyword argument, matching the feedback message of the .action file. This publishes feedback to the client.
Calling the server
Once you loaded the code, you can invoke the server by calling:
CL-USER> (fib-server)
Not much will happen by just doing so, you will also need to call the action using an actionlib client. You can do this with any of the C++, Python or LISP clients. The LISP client was presented in the previous tutorial.






