Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
doc:beginner:controlling_turtlesim [2013/12/10 09:25] bbrieberdoc:beginner:controlling_turtlesim [2014/01/19 12:51] (current) gkazhoya
Line 4: Line 4:
  
 **Next Tutorial:** [[doc:beginner:simple_plans|Implementing simple plans to move a turtle]] **Next Tutorial:** [[doc:beginner:simple_plans|Implementing simple plans to move a turtle]]
 +
 +In this tutorial we will re-use the package ''cram_beginner_tutorial'' that you have created in the previous tutorial. For controlling the turtle, we need to depend on the ''turtlesim'' package, since it contains the message definitions we will use. Further, we will want to use the communication functionality of ROS to talk to the ''turtlesim'' from inside Lisp, so we depend on ''roslisp''. Finally, we will have to deal with poses. For that, we want to use the package ''cl_transforms'' and ''geometry_msgs'', the latter one is only needed if you're working with Hydro or a newer ROS version. 
 +
  
 ===== Updating the dependencies ===== ===== Updating the dependencies =====
  
 +==== Updating ROS dependecies ====
  
-In this tutorial we will re-use the package ''cram_tutorial'' that you have created in the previous tutorial. For controlling turtlesim, we need to depend on its package since it contains the message definitions we will use. Further, we will have to deal with poses. For that, we want to use the package ''cl_transforms'' and ''geometry_msgs'' the later one is only needed if your working with hydro or a newer ROS version. +==== catkin ====
  
- +Open ''package.xml'' from the root directory of your ''cram_beginner_tutorial'' package and add the lines 
-==== Updating ros dependecies ====+<code xml> 
 +  <build_depend>roslisp</build_depend> 
 +  <build_depend>turtlesim</build_depend> 
 +  <build_depend>cl_transforms</build_depend> 
 +  <build_depend>geometry_msgs</build_depend> 
 +</code> 
 +under ''<build_depend>cram_language</build_depend>'' and 
 +<code xml> 
 +  <run_depend>roslisp</run_depend> 
 +  <run_depend>turtlesim</run_depend> 
 +  <run_depend>cl_transforms</run_depend> 
 +  <run_depend>geometry_msgs</run_depend>   
 +</code> 
 +under ''<run_depend>cram_language</run_depend>''.
  
 ==== rosbuild ==== ==== rosbuild ====
 Open ''manifest.xml'' and add the lines Open ''manifest.xml'' and add the lines
-<code>+<code xml> 
 +  <depend package="roslisp"/>
   <depend package="turtlesim"/>   <depend package="turtlesim"/>
   <depend package="cl_transforms"/>   <depend package="cl_transforms"/>
   <depend package="geometry_msgs"/>   <depend package="geometry_msgs"/>
 </code> </code>
- +under ''<depend package="cram_language">''.
-==== catkin ==== +
- +
-Open ''package.xml'' and add the lines +
-<code> +
-  <build-depend>turtlesim</build-depend> +
-  <build-depend>cl_transforms</build-depend> +
-  <build-depend>geometry_msgs</build-depend> +
-  <run-depend>turtlesim</run-depend> +
-  <run-depend>cl_transforms</run-depend> +
-  <run-depend>geometry_msgs</run-depend>   +
-</code>+
  
 ==== Updating cram dependecies ==== ==== Updating cram dependecies ====
  
 +Now open ''cram-beginner-tutorial.asd'' and update the system dependencies to include the system  ''roslisp'', ''turtlesim-msg'', ''geometry_msgs-msg'' and ''cl-transforms''. The systems that correspond to the messages of a package are always named like the package name with a ''-msg'' suffix. Your system should now look like this:
  
- +<code lisp
-Now open ''cram-tutorial.asd'' and update the system dependencies to include the system ''turtlesim-msg'', ''geometry_msgs-msg'' and ''cl-transforms''. The systems that correspond to the messages of a package are always named like the package name with a ''-msg'' suffix. Your system should now look like this: +(defsystem cram-beginner-tutorial
- +
-<code> +
-(defsystem cram-tutorial+
   :depends-on (roslisp cram-language turtlesim-msg cl-transforms geometry_msgs-msg)   :depends-on (roslisp cram-language turtlesim-msg cl-transforms geometry_msgs-msg)
   :components   :components
Line 47: Line 52:
             ((:file "package")             ((:file "package")
              (:file "tutorial" :depends-on ("package"))))))              (:file "tutorial" :depends-on ("package"))))))
-</code>        +</code>
-===== Updating the the Lisp Package =====+
  
 +===== Updating the Lisp Package =====
  
-We also need to add ''cl-transforms'' as a dependency in the file ''package.lisp''+We also want to add ''roslisp'' and ''cl-transforms'' to our namespace in the file ''package.lisp'' so that we don't have to specify the namespace each time we use a function from that package in our code
-<code> +<code lisp
-(defpackage cram-tutorial+(defpackage cram-beginner-tutorial
   (:nicknames :tut)   (:nicknames :tut)
   (:use #:cpl #:roslisp #:cl-transforms))   (:use #:cpl #:roslisp #:cl-transforms))
-</code>  +</code> 
 ===== Writing the communication glue code ===== ===== Writing the communication glue code =====
  
- +Now it's time to use roslisp to connect to turtlesim. Turtlesim creates one ROS topic namespace per turtle. For each turtle name (turtle1, turtle2, etc.it publishes the pose of the turtle on a ''~/pose'' topic, publishes the color value under the turtle (the background color) on a ''~/color_sensor'' topic. For each turtle turtlesim subscribes to a ''~/cmd_vel'' topic (or ''~/command_velocity'' before hydro) waiting for commands to move the turtle. This is the topic through which we will be controlling our turtles from Lisp.
-Now it's time to use roslisp to connect to turtlesim. Turtlesim creates one namespace per turtle. For each turtle name turtle1, turtle2, etc. it publishes the pose of the turtles on a ''~/pose topic'', publishes the color value under the turtle on a ''~/color_sensor'' topic. For each turtle subscribes to a ''~/cmd_vel'' topic(or ''~/command_velocity'' before hydro) waiting for commands to move the turtle.+
  
 ==== The code ==== ==== The code ====
- 
  
 Open the file ''tutorial.lisp'' and add the following code. Open the file ''tutorial.lisp'' and add the following code.
Line 69: Line 73:
 === hydro === === hydro ===
  
-<code>+<code lisp>
 (in-package :tut) (in-package :tut)
  
-(defvar *color-value* (make-fluent :name :color-value) "current color of turtle")+(defvar *color-value* (make-fluent :name :color-value) "current color under the turtle")
 (defvar *turtle-pose* (make-fluent :name :turtle-pose) "current pose of turtle") (defvar *turtle-pose* (make-fluent :name :turtle-pose) "current pose of turtle")
  
-(defvar *color-sub* nil "color subscription client") +(defvar *color-sub* nil "color ROS subscriber") 
-(defvar *pose-sub* nil "pose subscription client") +(defvar *pose-sub* nil "pose ROS subscriber") 
-(defvar *cmd-vel-pub* nil "command velocity subscription client")+(defvar *cmd-vel-pub* nil "velocity commands ROS publisher")
  
 (defun init-ros-turtle (name) (defun init-ros-turtle (name)
Line 88: Line 92:
                                #'pose-cb))                                #'pose-cb))
   (setf *cmd-vel-pub* (advertise (format nil "~a/cmd_vel" name)   (setf *cmd-vel-pub* (advertise (format nil "~a/cmd_vel" name)
-                                 "geometry_msgs/Twist ")))+                                 "geometry_msgs/Twist")))
  
 (defun color-cb (msg) (defun color-cb (msg)
-  "Callback for color values"+  "Callback for color values. Called by the color topic subscriber."
   (setf (value *color-value*) msg))   (setf (value *color-value*) msg))
  
 (defun pose-cb (msg) (defun pose-cb (msg)
-  "Callback for pose values"+  "Callback for pose values. Called by the pose topic subscriber."
   (setf (value *turtle-pose*) msg))   (setf (value *turtle-pose*) msg))
  
 (defun send-vel-cmd (lin ang) (defun send-vel-cmd (lin ang)
-  "function to send velocity commands"+  "Function to send velocity commands"
   (publish *cmd-vel-pub* (make-message "geometry_msgs/Twist"   (publish *cmd-vel-pub* (make-message "geometry_msgs/Twist"
-                                      (linear(make-msg "geometry_msgs/Vector3" (xlin) +                                       :linear (make-msg "geometry_msgs/Vector3" 
-                                      (angular(make-msg "geometry_msgs/Vector3" (zang) ))) +                                                         :x lin) 
-</code>             +                                       :angular (make-msg "geometry_msgs/Vector3" 
 +                                                          :z ang)))) 
 +</code>
  
 === groovy and older === === groovy and older ===
  
-<code>+<code lisp>
 (in-package :tut) (in-package :tut)
  
Line 145: Line 151:
 === The code explained === === The code explained ===
  
 +In ''(defun init-ros-turtle ...)'' we subscribe to the pose and the color sensor topic and create a function that can publish to the ''cmd_vel'' (or ''command_velocity'' in old ROS) topic. The topic clients are stored in global variables we defined before.
  
-In ''(defun init-ros-turtle ...)'' we subscribe to the pose and the color sensor topic and create function that can publish the ''cmd_vel''/''command_velocity'' topic. The topic clients are stored in global variables we defined before.+We use callback functions ''*-cb'' for all updates on the pose of the turtle and the color sensor value to fluent in order to be able to use those values in a reactive control program.
  
-We use callback function ...-cb for all updates on the pose of the turtle and the color sensor value to a fluent in order to be able to use those values in a reactive control program. +A fluent is a proxy around a value with support for notification on change. Further, fluents can be combined to so-called fluent networks. There will be some more explanations about fluents later in this tutorial.
- +
-A fluent is a proxy around a value with support for notification on change. Further, fluents can be combined to so-called fluent networks.+
  
 ===== Experimenting in the REPL ===== ===== Experimenting in the REPL =====
  
 +Now let's try it out. Open your Lisp REPL and make sure that you loaded the system ''cram-beginner-tutorial'' and switched to the package ''tut'', hint:
  
-Now let's try it out. Open your Lisp REPL and make sure that you loaded the system cram-tutorial and switched to the package tut, hint: +<code lisp
-<code> +CL-USER> (ros-load:load-system "cram_beginner_tutorial" :cram-beginner-tutorial)
-CL-USER> (ros-load:load-system "cram_tutorial" :cram-tutorial)+
 ... ...
 CL-USER> (in-package :tut) CL-USER> (in-package :tut)
 </code> </code>
 +
 For experiments in the REPL, we want to initialize a Lisp ROS node and then call ''init-ros-turtle''. For experiments in the REPL, we want to initialize a Lisp ROS node and then call ''init-ros-turtle''.
  
-Make sure ''roscore'' is running in a terminal. Else we cannot create ros nodes.+Make sure ''roscore'' is running in a terminal. Else we cannot create ROS nodes.
  
 Enter the following commands: Enter the following commands:
-<code>+ 
 +<code lisp>
 TUT> (start-ros-node "cram_tutorial_client") TUT> (start-ros-node "cram_tutorial_client")
 [(ROSLISP TOP) INFO] 1292688669.674: Node name is cram_tutorial_client [(ROSLISP TOP) INFO] 1292688669.674: Node name is cram_tutorial_client
Line 175: Line 182:
 [(ROSLISP TOP) INFO] 1292688670.875: Node startup complete [(ROSLISP TOP) INFO] 1292688670.875: Node startup complete
 </code> </code>
-The name of the node is arbitrary, we need to call ''start-ros-node'' before we can use the functions subscribe and advertise. 
  
-<code>+The name of the node is arbitrary. We need to call ''start-ros-node'' before we can use the functions subscribe and advertise. 
 + 
 +<code lisp>
 TUT> (init-ros-turtle "/turtle1") TUT> (init-ros-turtle "/turtle1")
 </code> </code>
 +
 This calls our function ''init-ros-turtle'', with the name that the turtlesim uses for the first turtle spawned. This calls our function ''init-ros-turtle'', with the name that the turtlesim uses for the first turtle spawned.
  
 Now we should start up turtlesim in a new terminal: Now we should start up turtlesim in a new terminal:
-<code>+ 
 +<code bash>
 $ rosrun turtlesim turtlesim_node $ rosrun turtlesim turtlesim_node
 </code> </code>
-You will see the turtlesim window: 
  
 +(Note: if you're using turtlesim for the first time, you might need to compile the package first: ''$ rosmake turtlesim''.)
  
-{{:doc:beginner:turtlesim.png?800|}}+You will see the turtlesim window: 
 + 
 +{{:doc:beginner:turtlesim.png?400|}}
  
 Notice that thanks to ROS, we could subscribe to the topics even before they had been advertised by the turtlesim. Notice that thanks to ROS, we could subscribe to the topics even before they had been advertised by the turtlesim.
  
 We are ready for experiments now. Let's see if the fluents are containing the right values: We are ready for experiments now. Let's see if the fluents are containing the right values:
-<code>+ 
 +<code lisp>
 TUT>  (value *turtle-pose*) TUT>  (value *turtle-pose*)
 [TURTLESIM-MSG:<POSE> [TURTLESIM-MSG:<POSE>
Line 208: Line 221:
      0.0d0]      0.0d0]
 TUT>  (value *color-value*) TUT>  (value *color-value*)
- 
 [TURTLESIM-MSG:<COLOR> [TURTLESIM-MSG:<COLOR>
    R:    R:
Line 217: Line 229:
      255]      255]
 </code> </code>
 +
 You see that the value of the fluents contain structured data, which contains the pose and color information as provided by the turtlesim simulator. You see that the value of the fluents contain structured data, which contains the pose and color information as provided by the turtlesim simulator.
  
-Let's see if the pose changes if we move the turtle. Start up ''turtle_teleop_key'' and move the turtle a little bit: +Let's see if the pose changes if we move the turtle. Start up ''turtle_teleop_key'' and move the turtle a little bit using the arrows on your keyboard: 
-<code> + 
-$ rosrun turtlesim turtle_teleop_key +<code bash>$ rosrun turtlesim turtle_teleop_key</code> 
-</code>+
 Try evaluating ''*turtle-pose*'' in the REPL again: Try evaluating ''*turtle-pose*'' in the REPL again:
-<code>+<code lisp>
 TUT>  (value *turtle-pose*) TUT>  (value *turtle-pose*)
- 
 [TURTLESIM-MSG:<POSE> [TURTLESIM-MSG:<POSE>
    X:    X:
Line 241: Line 253:
 As the pose fluent has been updated, the subscribers seem to work. As the pose fluent has been updated, the subscribers seem to work.
  
-==== Fluents ==== 
  
 +==== Fluents ====
  
 Now let's play around with fluents a little bit more. Now let's play around with fluents a little bit more.
Line 248: Line 260:
 Notice how in the checks before, we used the function ''(value *turtle-pose*)'', instead of accessing the variable directly. This is because fluents are proxies, and to get the values we need to use the value function. Notice how in the checks before, we used the function ''(value *turtle-pose*)'', instead of accessing the variable directly. This is because fluents are proxies, and to get the values we need to use the value function.
  
-Fluents allow us to wait for specific events. For instance, we can wait for the ''x'' coordinate of the turtle being smaller than 5: +Fluents allow us to wait for specific events. For instance, we can wait for the ''x'' coordinate of the turtle being smaller than ''5''
-<code>+<code lisp>
 TUT> (wait-for (< (fl-funcall #'turtlesim-msg:x *turtle-pose*) TUT> (wait-for (< (fl-funcall #'turtlesim-msg:x *turtle-pose*)
                   5.0))                   5.0))
 </code> </code>
 The code above constructs a fluent network. The expression The code above constructs a fluent network. The expression
-<code>+<code lisp>
 (fl-funcall #'turtlesim-msg:x *turtle-pose*) (fl-funcall #'turtlesim-msg:x *turtle-pose*)
 </code> </code>
-returns a new fluent that contains the x-value of the turtle pose. So from a proxy that wrapped the whole pose of a turtle, we got a new proxy that wraps just the x coordinate. The expression +returns a new fluent that contains the ''x''-value of the turtle pose. So from a proxy that wrapped the whole pose of a turtle, we got a new proxy that wraps just the ''x'' coordinate. The expression 
-<code>+<code lisp>
 (< (fl-funcall #'turtlesim-msg:x *turtle-pose*) (< (fl-funcall #'turtlesim-msg:x *turtle-pose*)
    5.0)    5.0)
Line 264: Line 276:
 returns a fluent that is either ''T'' or ''NIL'' indicating if the x-coordinate is either smaller or bigger than 5.0. Finally, ''wait-for'' waits for the fluent to become ''T''. returns a fluent that is either ''T'' or ''NIL'' indicating if the x-coordinate is either smaller or bigger than 5.0. Finally, ''wait-for'' waits for the fluent to become ''T''.
  
-Execute the above expression. If the turtle's x position is already smaller than 5, the expression should return ''T'' immediately. If not, the expression should block until you move the turtle far enough to the left using the teleop.+Execute the above expression. If the turtle'''x'' position is already smaller than 5, the expression should return ''T'' immediately. If not, the expression should block until you move the turtle far enough to the left using the teleop.
  
-=== Moving the turtle === 
  
 +=== Moving the turtle ===
  
 Let's see if we can also move the turtle from LISP. Try the following: Let's see if we can also move the turtle from LISP. Try the following:
-<code>+<code lisp>
 TUT> (dotimes (i 10) (send-vel-cmd 1 1) (sleep 1)) TUT> (dotimes (i 10) (send-vel-cmd 1 1) (sleep 1))
 </code> </code>
Line 278: Line 290:
  
  
-Now that we have functions and fluents to connect to the turtlesim, let'implementing some simple plans.+Now that we have functions and fluents to connect to the turtlesim, let'implement some simple plans.
  
 [[doc:beginner:simple_plans|Implementing simple plans to move a turtle]] [[doc:beginner:simple_plans|Implementing simple plans to move a turtle]]