This is an old revision of the document!


Creating action designators for the TurtleSim

Description: In this tutorial you will learn what action designators are and write high-level plans: you will learn how to define action designators and how to use them.

Previous Tutorial: Implementing simple plans to move a turtle

Designators: Actions vs Motions

On a pragmatic level there is not a big difference between action and motion designators since both are a set of key-value pairs. But the difference lies in the semantics. As mentioned in an earlier tutorial about motion designators, they are meant to represent actual motions of the robot. Like Moving an arm or driving around. The action designators on the other hand should be used to describe abstract plans (e.g. tidying up a room).

This difference comes to play when designators are performed. The perfom function handles action designators differently than motion designators. Both are first referenced and then motion designators are executed by passing them to a matching process module. Action designators are executed by looking up a function with the same name as the resolved designator. The rest of the designator is passed as arguments to the function. Let's take a designator (desig:an action (type tidying-up) (what the-room)). This could be resolved to something like (tidy-up the-room). So there has to be a function tidy-up which takes one parameter (the-room), for perform to be able to execute the designator. Action designators can optionally contain a goal-key which would be checked to see if the goal has been already achieved, so the action doesn't have to be performed. But this will not be covered in this tutorial.

The plan: Drawing a house

The plan we want to implement is for drawing a house using the turtles pen. We already have all the low-level functions we need to achieve this. Now we need to define action designators for this plan. In simple terms a house consists of some rectangles and a triangle for the roof. We can define a plan to simply trace a set of vertices to draw the simple shapes. In our overall plan, we will call this other plan to draw the parts of the house.

Defining the action designators

Let's create new source files for the code in this tutorial (under the src directory), call them action-designators.lisp and high-level-plans.lisp. We will need to add the files to the :components of the src module in your cram-beginner-tutorial.asd, which should now look something like this:

(defsystem cram-beginner-tutorial
  :depends-on (roslisp cram-language turtlesim-msg turtlesim-srv cl-transforms geometry_msgs-msg cram-designators cram-prolog
                       cram-process-modules cram-language-designator-support cram-executive)
  :components
  ((:module "src"
            :components
            ((:file "package")
             (:file "control-turtlesim" :depends-on ("package"))
             (:file "simple-plans" :depends-on ("package" "control-turtlesim"))
             (:file "motion-designators" :depends-on ("package"))
             (:file "location-designators" :depends-on ("package"))
             (:file "action-designators" :depends-on ("package"))
             (:file "process-modules" :depends-on ("package"
                                                   "control-turtlesim"
                                                   "simple-plans"
                                                   "motion-designators"))
             (:file "selecting-process-modules" :depends-on ("package"
                                                             "motion-designators"
                                                             "process-modules"))
             (:file "high-level-plans" :depends-on ("package"
                                                    "motion-designators"
                                                    "location-designators"
                                                    "action-designators"
                                                    "process-modules"))))))

Now, reload the tutorial in roslisp_repl (which also loads the newly added dependencies).

Since we already covered creating designators in previous tutorials here's just a quick example of what we will be using for the plans.

TUT> (desig:an action (type drawing) (shape rectangle) (width 5) (height 4))
 
#<ACTION-DESIGNATOR ((:TYPE :DRAWING) (:SHAPE :RECTANGLE) (:WIDTH 5)
                     (:HEIGHT 4)) {100B819903}>

Defining inference rules for designators

Append the following to your action-designators.lisp file:

(in-package :tut)
 
(defun get-shape-vertices (type &rest parameters)
  (with-fields (x y)
      (value *turtle-pose*)
    (ecase type
      (:triangle
       (let ((base-width (first parameters))
             (height (second parameters)))
         (list
          (list x y 0)
          (list (+ x base-width) y 0)
          (list (/ (float (+ x base-width)) 2) (+ y height) 0))))
      (:rectangle
       (let ((width (first parameters))
             (height (second parameters)))
         (list
          (list x y 0)
          (list (+ x width) y 0)
          (list (+ x width) (+ y height) 0)
          (list x (+ y height) 0)))))))
 
(def-fact-group turtle-action-designators (action-desig)
  (<- (desig:action-desig ?desig (draw-house))
    (desig-prop ?desig (:type :drawing))
    (desig-prop ?desig (:shape :house)))
 
  (<- (desig:action-desig ?desig (draw-simple-shape ?vertices))
    (desig-prop ?desig (:type :drawing))
    (desig-prop ?desig (:shape :rectangle))
    (desig-prop ?desig (:width ?width))
    (desig-prop ?desig (:height ?height))
    (lisp-fun get-shape-vertices :rectangle ?width ?height ?vertices))
 
  (<- (desig:action-desig ?desig (draw-simple-shape ?vertices))
    (desig-prop ?desig (:type :drawing))
    (desig-prop ?desig (:shape :triangle))
    (desig-prop ?desig (:base-width ?base-width))
    (desig-prop ?desig (:height ?height))
    (lisp-fun get-shape-vertices :triangle ?base-width ?height ?vertices)))

Let's see what this code does. The function get-shape-vertices simply returns a list of vertices for the draw-simple-shape plan to trace. It uses the current position of the turtle to determine those points.

The inference is simple again: it maps the shape value to the right call to get-shape-vertices to get the right list of vertices.

Reload the tutorial package in roslisp_repl. This will also load the newly defined inference rules.

Let's try to create and reference designators in the REPL command line (don't forget to initialize the turtle first):

TUT> (init-ros-turtle "turtle1")
"/turtle1/set_pen"
TUT> (reference (desig:an action (type drawing) (shape rectangle) (width 5) (height 4)))
(DRAW-SIMPLE-SHAPE
 ((3.077953338623047d0 11.088889122009277d0 0)
  (8.077953338623047d0 11.088889122009277d0 0)
  (8.077953338623047d0 15.088889122009277d0 0)
  (3.077953338623047d0 15.088889122009277d0 0)))
TUT> (reference (desig:an action (type drawing) (shape house)))
(DRAW-HOUSE)

Writing the plans

Append the following to your high-level-plans.lisp file:

<code lisp> (in-package :tut)

(defun move-without-pen (?goal)

(exe:perform (a motion (type setting-pen) (off 1)))
(exe:perform (a motion (type moving) (goal ?goal)))
(exe:perform (a motion (type setting-pen) (off 0))))

(defun draw-house ()

(with-fields (x y)
    (value *turtle-pose*)
  (exe:perform (an action (type drawing) (shape rectangle) (width 5) (height 4.5)))
  (move-without-pen (list (+ x 3) y 0))
  (exe:perform (an action (type drawing) (shape rectangle) (width 1) (height 2.5)))
  (move-without-pen (list (+ x 0.5) (+ y 2) 0))
  (exe:perform (an action (type drawing) (shape rectangle) (width 1) (height 1)))
  (move-without-pen (list x (+ y 4) 0))
  (exe:perform (an action (type drawing) (shape triangle) (base-width 5) (height 4)))))

(defun draw-simple-shape (vertices)

(roslisp:ros-info (turtlesim) "Drawing a simple shape (V: ~a)." vertices)
(dolist (?v (append vertices (list (car vertices))))
  (exe:perform (a motion (type moving) (goal ?v)))))

</code lisp>

Here we define three plans. move-without-pen is a helper-plan for moving the turtle between the pieces of the house. It just performs some motions to achieve this. In draw-house we use the turtle-pose to move the turtle to the position of the next piece by calling move-without-pen. To draw the house we call perform with the action designators we defined above. These get resolved and draw-simple-shape is called. There we use the dolist macro to move the turtle by performing a moving motion for every vertex in vertices. We append the first vertex to vertices, to let the turtle move back to the first position at the end.

Now we can test the plan.

<code lisp> TUT> (top-level

  (with-process-modules-running (turtlesim-navigation turtlesim-pen-control)
    (move-without-pen '(1 1 0))
    (exe:perform (an action (type drawing) (shape house)))))

[(TURTLE-PROCESS-MODULES) INFO] 1502976852.289: TurtleSim pen control invoked with motion designator `#<MOTION-DESIGNATOR 1)

1)
TYPE
                                                                          SETTING-PEN)
                                                                         (OFF
                                                                          1)) {100B1F2D13}>'.
[(TURTLE-PROCESS-MODULES) INFO] 1502976852.307: TurtleSim navigation invoked with motion designator `#<MOTION-DESIGNATOR ((TYPE
                                                                         MOVING)
                                                                        (GOAL
                                                                         (1 1
                                                                          0))) {100CFF8DC3}>'.
[(TURTLE-PROCESS-MODULES) INFO] 1502976852.598: TurtleSim pen control invoked with motion designator `#<MOTION-DESIGNATOR ((TYPE
                                                                          SETTING-PEN)
                                                                         (OFF
                                                                          0)) {100D2AFED3}>'.
[(TURTLESIM) INFO] 1502976852.610: Drawing a simple shape (V: ((1.0125720500946045d0 1.082411289215088d0 0)
                          (6.0125720500946045d0 1.082411289215088d0 0)
                          (6.0125720500946045d0 5.582411289215088d0 0)
                          (1.0125720500946045d0 5.582411289215088d0 0))).
[(TURTLE-PROCESS-MODULES) INFO] 1502976852.614: TurtleSim navigation invoked with motion designator `#<MOTION-DESIGNATOR ((TYPE
                                                                         MOVING)
                                                                        (GOAL
                                                                         (1.0125720500946045d0
                                                                          1.082411289215088d0
                                                                          0))) {100D8036B3}>'.
                                                                          
[ … ] [(TURTLESIM) INFO] 1502976879.586: Drawing a simple shape (V: ((1.059867262840271d0 5.014044284820557d0 0)
                          (6.059867262840271d0 5.014044284820557d0 0)
                          (3.0299336314201355d0 9.014044284820557d0 0))).
[(TURTLE-PROCESS-MODULES) INFO] 1502976879.590: TurtleSim navigation invoked with motion designator `#<MOTION-DESIGNATOR ((TYPE
                                                                         MOVING)
                                                                        (GOAL
                                                                         (1.059867262840271d0
                                                                          5.014044284820557d0
                                                                          0))) {100A8326D3}>'.
                                                                          
[ … ] [(TURTLE-PROCESS-MODULES) INFO] 1502976886.567: TurtleSim navigation invoked with motion designator `#<MOTION-DESIGNATOR ((TYPE
                                                                         MOVING)
                                                                        (GOAL
                                                                         (1.059867262840271d0
                                                                          5.014044284820557d0
                                                                          0))) {100B6259E3}>'.
NIL </code lisp> The turtle should move to the lower-left corner (this is not part of the plan itself, but otherwise the house might not fit in the frame). Then it should start by drawing a big rectangle, two smaller ones and finally a triangle. In the end the drawing should resemble a house, albeit an abtract one. == Next == Now that we know how to write plans, let's find out how to handle failures in the plans. Handling Failures