Roslisp Basic Usage
Description: This tutorial gives a brief intro to roslisp. For more, see the API documentation, and the code in the roslisp_tutorials ROS package.Tutorial Level: BEGINNER
Next Tutorial: Organizing files for roslisp
Contents
Setup
To set up roslisp see the installation instructions.
To test that it works,
$ rosmake roslisp_tutorials
then in 3 separate terminals run
$ roscore
$ rosrun roslisp_tutorials talker
$ rosrun roslisp_tutorials listener
The listener should start printing messages received from the talker.
Common error with lisp script generation
You might see error reported here. In a nutshell, workaround is:
1 Set up cram overlay environment. See the instruction in the answer here.
2 Install from source roslisp_tutorials by following this answer.
Example for publishing and subscribing
We will use topics by creating a talker node publishing regularly on a topic, and a subscriber node listening to the messages and pringtin them. Here's the code for the talker, that can also be found within roscd roslisp_tutorials/src/. It shows how to use topics, and the code follows the examples given in the basic ROS tutorials for Python or c++.
https://code.ros.org/svn/ros/stacks/roslisp_support/trunk/roslisp_tutorials/src/talker.lisp
(in-package :roslisp-tutorials)
(defun talker ()
"Periodically print a string message on the /chatter topic"
(with-ros-node ("talker")
(let ((i 0) (pub (advertise "chatter" "std_msgs/String")))
(loop-at-most-every .1
(publish-msg pub :data (format nil "foo ~a" (incf i)))))))
Ros node code will usually be wrapped in a with-ros-node call as shown here, which handles command line arguments, makes sure to close subscriptions after shutdown, etc. The node name "talker" is arbitrary.
To publish on a topic, we need to first advertise the topic with a name and a topic type. The (advertise) function does that and returns a topic client that we can use for publishing.
The talker node then and loops forever publishing to the topic. The publish-msg command creates a message (inferring the type from the publication object) and publishes it. Here's the corresponding listener:
(in-package :roslisp-tutorials)
(defun listener ()
(with-ros-node ("listener" :spin t)
(subscribe "chatter" "std_msgs/String" #'print)))This node subscribes to the chatter topic, then spins. The callback to print just prints the data field of any received message. Note that the Lisp pretty printer works for ROS messages.
To modify this code, checkout the sources from http://www.ros.org/wiki/roslisp_tutorials into your home directory first, somewhere into your ROS_PACKAGE_PATH.
Example for service and client
These examples can also be found in the roslisp_tutorials package. They use the service definitions from ROS/Tutorials/CreatingMsgAndSrv
Based on the service definitinos, a service provider can be created like this:
(in-package :roslisp-tutorials)
;; "AddTwoInts" must be the service type
(def-service-callback AddTwoInts (a b)
(ros-info (roslisp-tutorials) "Returning [~a + ~a = ~a]" a b (+a b))
(make-response :sum (+ a b)))
(defun add-two-ints-server ()
(with-ros-node ("two_ints_server" :spin t)
(register-service
;;"add_two_ints" is the service name clients need to use
"add_two_ints"
;; 'AddTwoInts is the service type, declared in a .srv file
'AddTwoInts)
(ros-info (roslisp-tutorials) "Ready to add two ints.")))As long as the node stays alive, requests to the service will be handled by calling the callback function. Its return value must be of the message response type of the service.
The code for a client looks like this:
(in-package :roslisp-tutorials)
(defun add-two-ints-client (a b)
"adds by calling ros service"
(with-ros-node ("two_ints_client")
(if (not (wait-for-service "add_two_ints" 10))
(ros-warn nil "Timed out waiting for service add_two_ints")
(format t "~a + ~a = ~a~&"
a
b
(sum (call-service "add_two_ints" 'AddTwoInts :a a :b b))))))
(defun add-two-ints-client-main ()
;; parse command line args
(let ((args (cdr sb-ext:*posix-argv*)))
(if (not (= 2 (length args)))
(ros-info (roslisp-tutorials) "Error ~a~%usage: add_two_ints_client X Y" args)
;; else
(add-two-ints-client (parse-integer (first args))
(parse-integer (second args))))))In case the service is provided by a different ROS package, you would need to load the autogenerated lisp bindings for the services, and specify the service type with the according LISP package namespace.
E.g.:
;; in asd file, also needs change to manifest.xml to include other ROS pkg :depends-on (:beginner_tutorials-srv) ;; in REPL (ros-load:load-system "beginner_tutorials" "beginner_tutorials-srv") ;; in code 'beginner_tutorials-srv:AddTwoInts instead of just AddTwoInts
Also there are multiple ways of defining service callback. E.g.
;; specifies a different callback function name (def-service-callback (add-two-ints-cb AddTwoInts) (a b) ...)
Interactive use
LISP is most fun when used interactively. It is recommended to use Emacs with SLIME and rosemacs slime support.
After starting emacs, run M-x slime-ros to start a LISP REPL. (In Emacs, "M-x" means pressing the Meta Key usually labelled "Alt" and while keeping it pressed pressing the "x" key. Then enter "slime-ros" in the minibuffer at the bottom of the emacs window.
New in ROS electric:
If you do not want to setup your own emacs copy with slime, there is a ROS package provided with roslisp. Start a configured emacs instance with an interactive lisp session open by calling
rosrun roslisp_repl repl
Starting a LISP shell (REPL) instead of using rosemacs
You can run roslisp nodes interactively from within Lisp. To start sbcl
$ rosrun sbcl run-sbcl.sh
This starts a LISP REPL. This has a prompt looking like this: "*", whereas the slime REPL in emacs has a prompt like this "CL-USER>". The latter will be used in tutorials.
Also note that if you have rlwrap installed (sudo aptitude install rlwrap), using this primitive shell will be more tolerable, e.g.
$ rlwrap rosrun sbcl run-sbcl.sh
Outside rosemacs, the first step for using roslisp is to load the roslisp init file:
* (load #P"/PATH/TO/ROSLISP/scripts/roslisp-sbcl-init")
You can find out your path to roslip using rospack find roslisp
for cturtle, this is by default:
* (load #P"/opt/ros/cturtle/ros/core/roslisp/scripts/roslisp-sbcl-init")
for diamondback, it is by default:
* (load #P"/opt/ros/diamondback/stacks/ros_comm/clients/roslisp/scripts/roslisp-sbcl-init")
Note that the sbcl REPL can be quit by pressing Ctrl+d at the prompt.
See the installation instructions for details.
Running the installed tutorial examples
Once lisp is started and the init script has been loaded, you can use specific ros packages by loading the asdf system found in that package's asdf directory. For example, to load roslisp_tutorials,
CL-USER> (ros-load:load-system "roslisp_tutorials" "roslisp-tutorials")
This will take care of locating the roslisp_tutorials package and all its dependencies as well as loading the corresponding asd files.
The (talker) and (listener) functions shown above can be invoked using
CL-USER> (in-package :roslisp-tutorials) #<PACKAGE "ROSLISP-TUTORIALS"> ROSLISP-TUTORIALS> (talker)
and
CL-USER> (in-package :roslisp-tutorials) #<PACKAGE "ROSLISP-TUTORIALS"> ROSLISP-TUTORIALS> (listener)
in two separate LISP REPLs. You will need two terminals or two running emacs instances to run both the talker and the listener interactively.
Calling a service interactively
Let's start a ROS node. Make sure a roscore is running in a terminal.
$ roscore
In the REPL, do
;; load roslisp unless already done CL-USER> (ros-load:load-system "roslisp-tutorials" "roslisp-tutorials") CL-USER> (in-package :roslisp-tutorials) #<PACKAGE "ROSLISP-TUTORIALS"> ROSLISP-TUTORIALS> (start-ros-node "my-node")
Calling (start-ros-node) sets up a ros node in the context, this is good for interactive use, but else (with-ros-node) as used above is more comfortable, as it closes the node when not used anymore.
You can now call the various ROS commands, set up callbacks (which will run in the background), etc. For example, start up a roscore and a add_two_ints_server in a shell, one of those developed in the ROS tutorials on services
(LISP) $ rosrun roslisp_tutorials add-two-ints-server (C++) $ rosrun beginner_tutorials add_two_ints_server (Python) $ rosrun beginner_tutorials add_two_ints_server.py
Then, in the Lisp REPL type
;; load srv system for message format
ROSLISP-TUTORIALS> (ros-load:load-system "beginner_tutorials" "beginner_tutorials-srv")
;; create client node and call service
ROSLISP-TUTORIALS> (roslisp:call-service
"add_two_ints" ; name of service
'beginner_tutorials-srv:AddTwoInts ; message type
:a 42
:b 24)If all goes well, this should print out a message containing the answer 66.
Shutting down a running ros node
Remember we started a ros node earlier with (start-ros-node) ? When done, remember to shut the ROS node down using
ROSLISP-TUTORIALS> (shutdown-ros-node)
Logging
For Debugging purposes it is useful to add logging to certain functions. To use the ROS logging, it is possible to use
(roslisp:ros-debug ...) (roslisp:ros-info ...) (roslisp:ros-warn ...) (roslisp:ros-error ...)
If roscore is running and there is a node launched in lisp, the messages will go to /rosout and can be seen in rxconsole.
Try it. In a terminal start rxconsole:
$ rxconsole
Then in the REPL, tye
ROSLISP-TUTORIALS> (ros-warn my-debug-topic "Hello world") [(MY-DEBUG-TOPIC) WARN] 1296084013.543: world 1
In the rxconsole, you should see the message appearing as well. The name my-debug-topic specifies a logging topic, whose log level can be higher or lower than warn, by default warn should be enabled, but debug not.
Log levels cannot currently be set by rxloggerlevel, but must be set within lisp using (roslisp:set-debug-level <logger> <level>), or via a service interface (see the doxygen documentation).
Working with ROS messages
For the examples, we shall use geometry messages. In order for roslisp to have access to their structure, first load the ros package:
(ros-load:load-system "geometry_msgs" "geometry_msgs-msg")
To create a ros message for publishing on a topic, use (roslisp:make-message ...) or its alias (make-msg ...) The first argument is the name of the message type, then an even list of message slots and message contents are expected.
E.g.
(roslisp:make-msg "geometry_msgs/Point" (y) 2 (x) 1)
[GEOMETRY_MSGS-MSG:<POINT>
X:
1
Y:
2
Z:
0.0]Note how the order of arguments is arbitrary, and how arguments not given are still filled according to the message structure. The '(x)' notation uses a list because that's how we can set values deeper in a hierarchy.
It's also easy to create and access nested message types, e.g. Pose contains a Point and a quaternion, the hard way looks like this:
(roslisp:make-message "geometry_msgs/Pose" (position) (roslisp:make-msg "geometry_msgs/Point" (x) 3)) (orientation) (roslisp:make-msg "geometry_msgs/Quaternion" (w) 1))
However, we can also directly create the hierarchy. Try, for example,
(defvar pose-message (make-msg "geometry_msgs/Pose" (x position) 3 (w orientation) 1))
This creates a Pose message.
You can get at the fields of a message you received (or the one we just created above) using calls like
(roslisp:with-fields ((x (x position)) orientation) pose-message
(format t "~&X is ~a and orientation is ~a" x orientation))
X is 3 and orientation is [<QUATERNION>
X:
0.0
Y:
0.0
Z:
0.0
W:
1]Modified objects can be created using
(defvar pose2 (modify-message-copy pose (z position) 1.0))
Messages are actually CLOS objects, and to maximize efficiency, you can also access that representation directly: see the API for details.
Accessing the parameter server
Use
(roslisp:get-param ...) (roslisp:set-param ...) (roslisp:has-param ...)
etc.
See also
To continue your learning of roslisp, try out the following tutorials:
Actionlib Tutorial contains a tutorial explaining how to use the ros actionlib package from lisp
CL_TF Tutorial contains a tutorial explaining how to use the ros TF package from lisp






