Legacy Forum: Preserving Nearly 20 Years of Community History - A Time Capsule of Discussions, Memories, and Shared Experiences.

Program / Control your Robobuilder robot with LISP !

Korean company maker of Robot kits and servos designed for of articulated robots. Re-incarnation of Megarobotics.
93 postsPage 2 of 71, 2, 3, 4, 5 ... 7
93 postsPage 2 of 71, 2, 3, 4, 5 ... 7

What do you think of using Lisp / L# and Robobuilder?

Poll ended at Fri Dec 25, 2009 12:13 am

(it love !)
3
75%
Bring back GOTO !
0
No votes
"Emacs is written in Lisp, which is the only computer language that is beautiful. "
1
25%
"the most intelligent way to misuse a computer"
0
No votes
 
Total votes : 4

Day 15 - 10 days to Go!

Post by l3v3rz » Tue Dec 15, 2009 12:27 am

Post by l3v3rz
Tue Dec 15, 2009 12:27 am

Day 15 - 10 days to Go!
======================

If we want to manage a lot of motion data - the best way to access would be if the positions were stored in a file. Robobuilder use a file form called rbm, but here I've used Comma separated values. The big plus is that you can then manipulate the data easily in excel or openoffice calc. There are many ways to read a CSV files using .NET libraries, the method used here uses a simple character based parsing looking for commas and then assumes the cell contains a number. So first we need to create a CSV file using a favorite text editor (or spreadsheet such OpenOffice Calc). Here is an example using the "punch left" data from yesterday ...

Code: Select all
"test.csv"
#comment
0,0,   125, 179, 199,  88, 108, 126,  72,  49, 163, 141,  51,  47,  49, 199, 205, 205
1,70,  125, 179, 199,  88, 108, 126,  72,  49, 163, 141,  51,  47,  49, 199, 205, 205   
8,310, 107, 164, 233, 106,  95, 108,  80,  29, 155, 129,  56,  62,  40, 166, 206, 208
1,420, 107, 164, 233, 106,  95, 145,  74,  40, 163, 154, 117, 124, 114, 166, 206, 208
5,200, 126, 164, 222, 100, 107, 125,  80,  29, 155, 142,  79,  44,  40, 166, 206, 208
15,300,125, 179, 199,  88, 108, 126,  72,  49, 163, 141,  51,  47,  49, 199, 205, 205


The first two columns contain the number of frames and time in ms for the frame and the remaining columns contain servo position data. To read this data requires two functions. The first readcsv takes a filename as a parameter and then uses the .NET function File.ReadAllLines to create a string array, one line per record. So f will be a list containing '("line1" "line2" "line3") for example. Using for the each command then sets line to each row intern. If the line starts with a # the line is ignores - so that comments can be added to the csv file - as in the example above the #comment line is ignored. The line is passed to the second function splitline that separates the entries using the comma into a list of numeric fields. splitline also separates out the first two entries so that it follows the format used yesterday. It also defaults any empty cells to zero.

Code: Select all
(def readcsv (filename)
  (with (f "" z ())
    (= f (System.IO.File.ReadAllLines filename))
    (each line f
      (if (not (is #\# (car line)) )
         (do (= z (cons (splitline line) z)))
       )
     )
     (reverse z)
  )
)

(def splitline (text)
(with (l "0" z () r 0)
     (each a (toarray text )
        (= a (str a))
        (if (is a  ",")
             (do (= z (cons (coerce l "Int32") z)) (= l "0") )
             ;else
             (not (is a " ")) (= l (+ l a))
         )
     )
     (= z (cons (coerce l "Int32") z)) ;; last arg
     (= r (reverse z))
     (cons (cadr r) (cons (car r) (list (cdr (cdr r)))))
))


Here is an example of it in action.
Code: Select all
> (= data (readcsv "test.csv"))
((0 0 (125 179 199 88 108 126 72 49 163 141 51 47 49 199 205 205)) (70 1 (125 179 199 88 108 126 72 49 163 141 51 47 49
199 205 205)) (310 8 (107 164 233 106 95 108 80 29 155 129 56 62 40 166 206 208)) (420 1 (107 164 233 106 95 145 74 40 1
63 154 117 124 114 166 206 208)) (200 5 (126 164 222 100 107 125 80 29 155 142 79 44 40 166 206 208)) (300 15 (125 179 1
99 88 108 126 72 49 163 141 51 47 49 199 205 205)))


The first entry needs the two zero stripped out and this is achieved using a combination of : (car (cdr ( cdr (car data)))). The result can then be passed to the playmotion as outlined yesterday by constructing a new list called punchleft

Code: Select all
(= punchleft (cons (car (cdr ( cdr (car data)))) (cdr data)))
(playmotion cur punchleft)


So using L# it is possible to create preprogrammed motions - stored in csv file format and then loaded on demand and played in real time. L# is not only capable of text and list processing but through use of .NET functions can manipulate graphics and windows to enable real time display of the robot status.

Tomorrow: Windows and graphics
Day 15 - 10 days to Go!
======================

If we want to manage a lot of motion data - the best way to access would be if the positions were stored in a file. Robobuilder use a file form called rbm, but here I've used Comma separated values. The big plus is that you can then manipulate the data easily in excel or openoffice calc. There are many ways to read a CSV files using .NET libraries, the method used here uses a simple character based parsing looking for commas and then assumes the cell contains a number. So first we need to create a CSV file using a favorite text editor (or spreadsheet such OpenOffice Calc). Here is an example using the "punch left" data from yesterday ...

Code: Select all
"test.csv"
#comment
0,0,   125, 179, 199,  88, 108, 126,  72,  49, 163, 141,  51,  47,  49, 199, 205, 205
1,70,  125, 179, 199,  88, 108, 126,  72,  49, 163, 141,  51,  47,  49, 199, 205, 205   
8,310, 107, 164, 233, 106,  95, 108,  80,  29, 155, 129,  56,  62,  40, 166, 206, 208
1,420, 107, 164, 233, 106,  95, 145,  74,  40, 163, 154, 117, 124, 114, 166, 206, 208
5,200, 126, 164, 222, 100, 107, 125,  80,  29, 155, 142,  79,  44,  40, 166, 206, 208
15,300,125, 179, 199,  88, 108, 126,  72,  49, 163, 141,  51,  47,  49, 199, 205, 205


The first two columns contain the number of frames and time in ms for the frame and the remaining columns contain servo position data. To read this data requires two functions. The first readcsv takes a filename as a parameter and then uses the .NET function File.ReadAllLines to create a string array, one line per record. So f will be a list containing '("line1" "line2" "line3") for example. Using for the each command then sets line to each row intern. If the line starts with a # the line is ignores - so that comments can be added to the csv file - as in the example above the #comment line is ignored. The line is passed to the second function splitline that separates the entries using the comma into a list of numeric fields. splitline also separates out the first two entries so that it follows the format used yesterday. It also defaults any empty cells to zero.

Code: Select all
(def readcsv (filename)
  (with (f "" z ())
    (= f (System.IO.File.ReadAllLines filename))
    (each line f
      (if (not (is #\# (car line)) )
         (do (= z (cons (splitline line) z)))
       )
     )
     (reverse z)
  )
)

(def splitline (text)
(with (l "0" z () r 0)
     (each a (toarray text )
        (= a (str a))
        (if (is a  ",")
             (do (= z (cons (coerce l "Int32") z)) (= l "0") )
             ;else
             (not (is a " ")) (= l (+ l a))
         )
     )
     (= z (cons (coerce l "Int32") z)) ;; last arg
     (= r (reverse z))
     (cons (cadr r) (cons (car r) (list (cdr (cdr r)))))
))


Here is an example of it in action.
Code: Select all
> (= data (readcsv "test.csv"))
((0 0 (125 179 199 88 108 126 72 49 163 141 51 47 49 199 205 205)) (70 1 (125 179 199 88 108 126 72 49 163 141 51 47 49
199 205 205)) (310 8 (107 164 233 106 95 108 80 29 155 129 56 62 40 166 206 208)) (420 1 (107 164 233 106 95 145 74 40 1
63 154 117 124 114 166 206 208)) (200 5 (126 164 222 100 107 125 80 29 155 142 79 44 40 166 206 208)) (300 15 (125 179 1
99 88 108 126 72 49 163 141 51 47 49 199 205 205)))


The first entry needs the two zero stripped out and this is achieved using a combination of : (car (cdr ( cdr (car data)))). The result can then be passed to the playmotion as outlined yesterday by constructing a new list called punchleft

Code: Select all
(= punchleft (cons (car (cdr ( cdr (car data)))) (cdr data)))
(playmotion cur punchleft)


So using L# it is possible to create preprogrammed motions - stored in csv file format and then loaded on demand and played in real time. L# is not only capable of text and list processing but through use of .NET functions can manipulate graphics and windows to enable real time display of the robot status.

Tomorrow: Windows and graphics
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Day16 Windows graphics

Post by l3v3rz » Wed Dec 16, 2009 12:01 am

Post by l3v3rz
Wed Dec 16, 2009 12:01 am

Day16 - 9 Days to Go !
=======================================

Windows based graphics using L# !

To create a graphics window using .NET is straightforward in L#. The function createwindow creates an instance of a Windows Form object. The global variable form1 is the handle for the window. This creates the frame and standard windows controls such as min, max and close. In this example they won't work as they won't have functions behind them. Within the form the code creates a Panel object for the graphics to be drawing on. The exact size can be passed into the application. It could also add buttons, labels, menus etc using the same technique. Once the panel is created it then gets a handle to the graphics object which is used to send graphic primitive commands too.

Here's the code:

Code: Select all
(reference "System.Windows.Forms"
           "System.Drawing")

(using     "System.Drawing"
           "System.Windows.Forms")


(def createwindow (title x y)
 (do
   (= form1 (new "Form"))
   (.set_text form1 title)   
   (.set_autosize form1 true)

   ( = pb (new "System.Windows.Forms.Panel"))
   (.set_size     pb (new "Size" x y))
   (.set_Location pb (new "Point" 24 16))
   (.add (.controls form1) pb)

   (= g (.CreateGraphics pb))
 )
)


Notice how using and reference take multiple arguments. Once this has been created its possible to draw into the object using the Graphic handle (in this case g). All the .NET draw methods are available such as lines, arc, curves, polygons, rectangles etc. The following function demo draws x and y axes and then a circle at the coordinates specified. It also displays text passed to it. You can see it sets up different colour pens, red and black, for different draw commands. It also sets up a Font for the text. The background is set white by the clear method.

Code: Select all
(def demo( txt x y )
  (= w (.width pb))
  (= h (.height pb))
  (= h2 (/ h 2))
  (= w2 (/ w 2))
  (= x (+ x w2))
  (= y (- h2 y))
  (.clear g (Color.FromName "White"))
  (= axis (new "Pen" (Color.FromName "Black")))
  (= pen  (new "Pen" (Color.FromName "Red")))
  (= font (new "Font" "Arial" (coerce 8.25 "Single" )))
  (.drawline    g axis 0 h2 w h2)
  (.drawline    g axis w2 0 w2 h)
  (.drawellipse g pen (- x 6)  (- y 6) 14 14)
  (.drawstring  g txt font (.Brush pen) (new "PointF" 10 10))
)


The demo function can auto detect the size of the window by reading the width and height properties. Now to run the demo. All the code necessary is here in this post - no extra files are required today. First there must be a window visible using (.show) before demo is called. The window doesn't save or cache any information so it must be at the front.

Code: Select all
(createwindow "Demo" 250 250)  ; create graphics window 250 x 250 pixels in size
(.show form1)            ; display window
(demo "test" 0 0)       ; draw demo - cross with circle and text

; admire output :)

(.close form1)       ; finished


This will display an image that looks like this -

Image

Look to see what happens if you add multiple demo calls with different x and y values. This will give you a clue as to tomorrows post.

Tomorrow: Plotting data and animation
Day16 - 9 Days to Go !
=======================================

Windows based graphics using L# !

To create a graphics window using .NET is straightforward in L#. The function createwindow creates an instance of a Windows Form object. The global variable form1 is the handle for the window. This creates the frame and standard windows controls such as min, max and close. In this example they won't work as they won't have functions behind them. Within the form the code creates a Panel object for the graphics to be drawing on. The exact size can be passed into the application. It could also add buttons, labels, menus etc using the same technique. Once the panel is created it then gets a handle to the graphics object which is used to send graphic primitive commands too.

Here's the code:

Code: Select all
(reference "System.Windows.Forms"
           "System.Drawing")

(using     "System.Drawing"
           "System.Windows.Forms")


(def createwindow (title x y)
 (do
   (= form1 (new "Form"))
   (.set_text form1 title)   
   (.set_autosize form1 true)

   ( = pb (new "System.Windows.Forms.Panel"))
   (.set_size     pb (new "Size" x y))
   (.set_Location pb (new "Point" 24 16))
   (.add (.controls form1) pb)

   (= g (.CreateGraphics pb))
 )
)


Notice how using and reference take multiple arguments. Once this has been created its possible to draw into the object using the Graphic handle (in this case g). All the .NET draw methods are available such as lines, arc, curves, polygons, rectangles etc. The following function demo draws x and y axes and then a circle at the coordinates specified. It also displays text passed to it. You can see it sets up different colour pens, red and black, for different draw commands. It also sets up a Font for the text. The background is set white by the clear method.

Code: Select all
(def demo( txt x y )
  (= w (.width pb))
  (= h (.height pb))
  (= h2 (/ h 2))
  (= w2 (/ w 2))
  (= x (+ x w2))
  (= y (- h2 y))
  (.clear g (Color.FromName "White"))
  (= axis (new "Pen" (Color.FromName "Black")))
  (= pen  (new "Pen" (Color.FromName "Red")))
  (= font (new "Font" "Arial" (coerce 8.25 "Single" )))
  (.drawline    g axis 0 h2 w h2)
  (.drawline    g axis w2 0 w2 h)
  (.drawellipse g pen (- x 6)  (- y 6) 14 14)
  (.drawstring  g txt font (.Brush pen) (new "PointF" 10 10))
)


The demo function can auto detect the size of the window by reading the width and height properties. Now to run the demo. All the code necessary is here in this post - no extra files are required today. First there must be a window visible using (.show) before demo is called. The window doesn't save or cache any information so it must be at the front.

Code: Select all
(createwindow "Demo" 250 250)  ; create graphics window 250 x 250 pixels in size
(.show form1)            ; display window
(demo "test" 0 0)       ; draw demo - cross with circle and text

; admire output :)

(.close form1)       ; finished


This will display an image that looks like this -

Image

Look to see what happens if you add multiple demo calls with different x and y values. This will give you a clue as to tomorrows post.

Tomorrow: Plotting data and animation
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Day17 Plotting and animation

Post by l3v3rz » Thu Dec 17, 2009 12:31 am

Post by l3v3rz
Thu Dec 17, 2009 12:31 am

Day17 - 8 Days to go!
=================

Building on the windows display of yesterday its simple to generate a animated real time display of data. To demonstrate this is a function called anim which displays an oscilloscope style plot of a sine wave. L# itself doesn't have maths routines, but it can access the C# directly. To calculate the Sin of an angle (in radians) it can call the Math class.

Code: Select all
L Sharp 2.0.0.0 on 2.0.50727.3603
Copyright (c) Rob Blackwell. All rights reserved.
> (= Pi 3.1415)
3.1415
> (Math.Sin (/ Pi 2))
0.999999998926914
> (* 100 (Math.Sin (/ Pi 2)))
99.9999998926914


It could access PI directly as well using Math.Pi.

The demo relies on two main functions, plot and drawlist. The plot function is essentially the same as demo from yesterdays post. drawlist takes a list of x-y coordinates and creates a line linking the points together. An example would be (drawlist g '((0 0) (20 20) (30 120) (140 20) (50 50)) "Red") which draws a red line of 5 segments. The g is the handle to the graphics object created by createwindow explained yesterday. drawlist recursively calls drawline to draw the lines. As each call is made is calls its self with one less element on the list until the list is empty, which is tested for by the logic function and. Here is the code:

Code: Select all
(def drawlist (g l c)
      (= from (car  l))
      (= to   (cadr l))
      (if (and from to)
        (do     
        (drawline g (car from) (cadr from) (car to) (cadr to) c)
        (drawlist g (cdr l) c)
        )
      )
)

(def drawline (g fx fy tx ty c)
      (= lp (new "Pen" (Color.FromName c))) 
       
      (= w (.width pb))
      (= h (.height pb))
      (= h2 (/ h 2))
      (= w2 (/ w 2))
      (= fx (+ (coerce fx "Int32") w2))
      (= fy (- h2 (coerce fy "Int32")))
      (= tx (+ (coerce tx "Int32") w2))
      (= ty (- h2 (coerce ty "Int32")))     
      (.drawline  g lp fx fy tx ty)
)


The main function is anim. It loops or iterates through values of 'n until the 'q' key is pressed. This is achieved by exit?. It checks the Console.keyavailable property. As the code loops it calculates the position of a a sin wave and passes that to the plot via scope routine. This also creates the text label with the co-ordinates. In the main loop there is also a log of all the points that it has generated in a variable called history. It takes the last three items from history and puts them into a variable h using : ((car history) (cadr history) (car (cdr (cdr history))) ). This is passed to drawline to create the blue tail.

Code: Select all
(def exit? ()
   (if (Console.keyavailable)
     (if (is (.key (Console.ReadKey true)) (ConsoleKey.Q)) (err "Quit Pressed"))))

(def scope(x y)
   (= x (coerce x "Int32"))
   (= y (coerce y "Int32"))
   (= text (+ "(" (str x) " " (str y) ")" ))
   (plot text x y)
)

(def anim (f t s)
  (createwindow "Demo" 250 250)
  (.show form1)
  (while true
  (do
  (= history ())
  (= n -125)
  (while (< n 125)
     (= a (* 100 (Math.Sin (/ (* n Pi) f))))
     (scope n a)
     
     (= history (cons (list n a) history))
     (= h (list (car history) (cadr history) (car (cdr (cdr history)))))
     (drawlist g h "Blue")
     
     (sleep t)
     (= n (+ s n))
     (exit?)
  )
  ))
  (.hide form1)
)


To run enter the frequency, the time and the resolution and watch it go! The smaller the time value the faster the dot moves across the screen. Here's a few examples.

Code: Select all
> (anim 50 .5 5)
Exception : Quit Pressed
> (anim 100 .1 10)
Exception : Quit Pressed
>  (.show form1)(drawlist g history "Blue")


Image

To stop it press q at the console window. You can look at the variable history which has the points plotted on the current pass - this can also be display by passing it to drawlist (see last line in above example).

Tomorrow: Creating a real time display of accelerometer data
Day17 - 8 Days to go!
=================

Building on the windows display of yesterday its simple to generate a animated real time display of data. To demonstrate this is a function called anim which displays an oscilloscope style plot of a sine wave. L# itself doesn't have maths routines, but it can access the C# directly. To calculate the Sin of an angle (in radians) it can call the Math class.

Code: Select all
L Sharp 2.0.0.0 on 2.0.50727.3603
Copyright (c) Rob Blackwell. All rights reserved.
> (= Pi 3.1415)
3.1415
> (Math.Sin (/ Pi 2))
0.999999998926914
> (* 100 (Math.Sin (/ Pi 2)))
99.9999998926914


It could access PI directly as well using Math.Pi.

The demo relies on two main functions, plot and drawlist. The plot function is essentially the same as demo from yesterdays post. drawlist takes a list of x-y coordinates and creates a line linking the points together. An example would be (drawlist g '((0 0) (20 20) (30 120) (140 20) (50 50)) "Red") which draws a red line of 5 segments. The g is the handle to the graphics object created by createwindow explained yesterday. drawlist recursively calls drawline to draw the lines. As each call is made is calls its self with one less element on the list until the list is empty, which is tested for by the logic function and. Here is the code:

Code: Select all
(def drawlist (g l c)
      (= from (car  l))
      (= to   (cadr l))
      (if (and from to)
        (do     
        (drawline g (car from) (cadr from) (car to) (cadr to) c)
        (drawlist g (cdr l) c)
        )
      )
)

(def drawline (g fx fy tx ty c)
      (= lp (new "Pen" (Color.FromName c))) 
       
      (= w (.width pb))
      (= h (.height pb))
      (= h2 (/ h 2))
      (= w2 (/ w 2))
      (= fx (+ (coerce fx "Int32") w2))
      (= fy (- h2 (coerce fy "Int32")))
      (= tx (+ (coerce tx "Int32") w2))
      (= ty (- h2 (coerce ty "Int32")))     
      (.drawline  g lp fx fy tx ty)
)


The main function is anim. It loops or iterates through values of 'n until the 'q' key is pressed. This is achieved by exit?. It checks the Console.keyavailable property. As the code loops it calculates the position of a a sin wave and passes that to the plot via scope routine. This also creates the text label with the co-ordinates. In the main loop there is also a log of all the points that it has generated in a variable called history. It takes the last three items from history and puts them into a variable h using : ((car history) (cadr history) (car (cdr (cdr history))) ). This is passed to drawline to create the blue tail.

Code: Select all
(def exit? ()
   (if (Console.keyavailable)
     (if (is (.key (Console.ReadKey true)) (ConsoleKey.Q)) (err "Quit Pressed"))))

(def scope(x y)
   (= x (coerce x "Int32"))
   (= y (coerce y "Int32"))
   (= text (+ "(" (str x) " " (str y) ")" ))
   (plot text x y)
)

(def anim (f t s)
  (createwindow "Demo" 250 250)
  (.show form1)
  (while true
  (do
  (= history ())
  (= n -125)
  (while (< n 125)
     (= a (* 100 (Math.Sin (/ (* n Pi) f))))
     (scope n a)
     
     (= history (cons (list n a) history))
     (= h (list (car history) (cadr history) (car (cdr (cdr history)))))
     (drawlist g h "Blue")
     
     (sleep t)
     (= n (+ s n))
     (exit?)
  )
  ))
  (.hide form1)
)


To run enter the frequency, the time and the resolution and watch it go! The smaller the time value the faster the dot moves across the screen. Here's a few examples.

Code: Select all
> (anim 50 .5 5)
Exception : Quit Pressed
> (anim 100 .1 10)
Exception : Quit Pressed
>  (.show form1)(drawlist g history "Blue")


Image

To stop it press q at the console window. You can look at the variable history which has the points plotted on the current pass - this can also be display by passing it to drawlist (see last line in above example).

Tomorrow: Creating a real time display of accelerometer data
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Day18 Realtime acceleromter

Post by l3v3rz » Fri Dec 18, 2009 12:33 am

Post by l3v3rz
Fri Dec 18, 2009 12:33 am

Day18 - 7 Days to go
================

Plotting data in real time from your Robobuilder robot. Todays' post uses the routines created over the last two days to display graphics to output of the values of the accelerometer.

First its important to know if we have a connection to the robot. So the following code does a couple of tests serial? and remote? to see a connection has been made. They look to see if the global variables exist; sport - which is the handle for the serial port, and pcr the handle to the RobobuilderLib.PCremote class. The functions return the name of a colour which is then passed to a display function called status.

Code: Select all
(def serial? () (if (and (bound 'sport) (.isopen sport)) "Green" "Red"))
(def remote? () (if (and (bound 'pcr) (is "Green" (serial?))) "Green" "Red"))

(def status(x y txt c)
  (= pen  (new "Pen" (Color.FromName c)))
  (.fillellipse g (.Brush pen) (- x 10)  (- y 6) 14 14)
  (= pen  (new "Pen" (Color.FromName "Black")))
  (.drawellipse g pen (- x 10)  (- y 6) 14 14)
  (.drawstring  g txt font (.Brush pen) (new "PointF" (+ x 10) (- y 4)))
)

(def demo2(f g)
 (createwindow "status" 250 250)
 (.show f)
 (.clear g (Color.FromName "White") )
 (status 205 8  "Serial"  (serial?) )
 (status 160 8  "Remote"  (remote?) )
)


If you type (demo2) a window will appear with a couple of red indicators showing not connected yet. You will have need to use the functions created from the last few days post. The function status creates the filled circles using the fillelipse rather than the drawelipse method. Also the bound? function enables to test to see if a variable has been created or set. It returns false if it doesn't exist. very useful!

To debug code its simpler not to have the robot connected, so I simulate the accelerometer using random data. The function readAcc will return the '(X Y Z) values as a list and so by replacing this with a function that generates random numbers means the plot routine can be tested easily. The random numbers are generated using the C# class Random which when instantiated returns random number using the .Next method. (.next r 50) generates a random integer between 0-50. The function then subtracts 25 to generate random number between -25 and +25. It does this 3 times to create a list i.e. '(10 -5 20).

Code: Select all
(= r (new "Random"))
(def readAcc ()
    (list (- (.next r 50) 25)  (- (.next r 50) 25) (- (.next r 50) 25) ))

;replace with this for actual sensor reading
;(def readAcc () (.readXYZ pcr))


Now for the main code for plotaccel. Its very similar to the anim routine. It displays the values but also displays the serial status - showing if the robot is connected - although the serial port can't seem to detect if you turn the robot off!

Code: Select all
(def plotaccel (r)
  "Plot - sample rate rHz"
  (= r (/ 1.0 r))
 
  (with (acc 0)
    (createwindow "Accelerometer Demo" 250 250)
    (.show form1)
    (= n 1)
    (while (< n 1000)
       (= acc (readAcc))
       (scope (car acc) (cadr acc))
       (status 205 8  "Serial" (serial?))
       (status 160 8  "Remote" (remote?))
       (drawlist g '((-125  40) (125   40)) "Black") ; limit
       (drawlist g '((-125 -40) (125 -40))  "Black") ; limit
       
       (= history (cons (list (car acc) (cadr acc)) history))
       (= h (list (car history) (cadr history) (car (cdr (cdr history)))))
       (drawlist g h "Blue")
     
       (sleep r)
       (= n (+ 1 n))
       (exit?)
   )
   (.hide form1)
  )
)


To run simply enter (plotaccel 5) the value 5 represents the frequency the accelerometer is read and plotted. 5Hz works well. If the routine works with dummy readAcc routine then load Day7.lisp and (run_robobuilder) and connect to the COM port. Replace the readAcc with a function to read the accelerometer as described in earlier post (day 9). The scope function actually draws the axes and plot the point. Its is passed in this example X and Y values. But you could pass any 2 of the three values. The plotaccel function also displays a couple of boundary lines - these represent the tipping points (or could do) of the robot. The idea would be when the accelerometer values cross the line would be the point to activate a balance routine for instance. When running move your robobuilder bot around and what the circle move - its great fun!

Not only - but also .. There was a question on another thread about read Joystick on windows machines, L# can do this as well and the outline Ive posted here reference the Joystick note (http://robosavvy.com/forum/viewtopic.php?p=23913#23913)

Next for something a bit different. Developing the power of Lisp/L#

Tomorrow: Property lists
Day18 - 7 Days to go
================

Plotting data in real time from your Robobuilder robot. Todays' post uses the routines created over the last two days to display graphics to output of the values of the accelerometer.

First its important to know if we have a connection to the robot. So the following code does a couple of tests serial? and remote? to see a connection has been made. They look to see if the global variables exist; sport - which is the handle for the serial port, and pcr the handle to the RobobuilderLib.PCremote class. The functions return the name of a colour which is then passed to a display function called status.

Code: Select all
(def serial? () (if (and (bound 'sport) (.isopen sport)) "Green" "Red"))
(def remote? () (if (and (bound 'pcr) (is "Green" (serial?))) "Green" "Red"))

(def status(x y txt c)
  (= pen  (new "Pen" (Color.FromName c)))
  (.fillellipse g (.Brush pen) (- x 10)  (- y 6) 14 14)
  (= pen  (new "Pen" (Color.FromName "Black")))
  (.drawellipse g pen (- x 10)  (- y 6) 14 14)
  (.drawstring  g txt font (.Brush pen) (new "PointF" (+ x 10) (- y 4)))
)

(def demo2(f g)
 (createwindow "status" 250 250)
 (.show f)
 (.clear g (Color.FromName "White") )
 (status 205 8  "Serial"  (serial?) )
 (status 160 8  "Remote"  (remote?) )
)


If you type (demo2) a window will appear with a couple of red indicators showing not connected yet. You will have need to use the functions created from the last few days post. The function status creates the filled circles using the fillelipse rather than the drawelipse method. Also the bound? function enables to test to see if a variable has been created or set. It returns false if it doesn't exist. very useful!

To debug code its simpler not to have the robot connected, so I simulate the accelerometer using random data. The function readAcc will return the '(X Y Z) values as a list and so by replacing this with a function that generates random numbers means the plot routine can be tested easily. The random numbers are generated using the C# class Random which when instantiated returns random number using the .Next method. (.next r 50) generates a random integer between 0-50. The function then subtracts 25 to generate random number between -25 and +25. It does this 3 times to create a list i.e. '(10 -5 20).

Code: Select all
(= r (new "Random"))
(def readAcc ()
    (list (- (.next r 50) 25)  (- (.next r 50) 25) (- (.next r 50) 25) ))

;replace with this for actual sensor reading
;(def readAcc () (.readXYZ pcr))


Now for the main code for plotaccel. Its very similar to the anim routine. It displays the values but also displays the serial status - showing if the robot is connected - although the serial port can't seem to detect if you turn the robot off!

Code: Select all
(def plotaccel (r)
  "Plot - sample rate rHz"
  (= r (/ 1.0 r))
 
  (with (acc 0)
    (createwindow "Accelerometer Demo" 250 250)
    (.show form1)
    (= n 1)
    (while (< n 1000)
       (= acc (readAcc))
       (scope (car acc) (cadr acc))
       (status 205 8  "Serial" (serial?))
       (status 160 8  "Remote" (remote?))
       (drawlist g '((-125  40) (125   40)) "Black") ; limit
       (drawlist g '((-125 -40) (125 -40))  "Black") ; limit
       
       (= history (cons (list (car acc) (cadr acc)) history))
       (= h (list (car history) (cadr history) (car (cdr (cdr history)))))
       (drawlist g h "Blue")
     
       (sleep r)
       (= n (+ 1 n))
       (exit?)
   )
   (.hide form1)
  )
)


To run simply enter (plotaccel 5) the value 5 represents the frequency the accelerometer is read and plotted. 5Hz works well. If the routine works with dummy readAcc routine then load Day7.lisp and (run_robobuilder) and connect to the COM port. Replace the readAcc with a function to read the accelerometer as described in earlier post (day 9). The scope function actually draws the axes and plot the point. Its is passed in this example X and Y values. But you could pass any 2 of the three values. The plotaccel function also displays a couple of boundary lines - these represent the tipping points (or could do) of the robot. The idea would be when the accelerometer values cross the line would be the point to activate a balance routine for instance. When running move your robobuilder bot around and what the circle move - its great fun!

Not only - but also .. There was a question on another thread about read Joystick on windows machines, L# can do this as well and the outline Ive posted here reference the Joystick note (http://robosavvy.com/forum/viewtopic.php?p=23913#23913)

Next for something a bit different. Developing the power of Lisp/L#

Tomorrow: Property lists
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Day19 Property lists

Post by l3v3rz » Fri Dec 18, 2009 10:34 pm

Post by l3v3rz
Fri Dec 18, 2009 10:34 pm

Day19 - 6 days to Go !
================

Property list are a powerful feature of Lisp. All through this series atoms have essential one value. It might be a string or a number or list but it is in essence a single thing. The idea behind property list is to give atoms properties that can be stored and read. So if an atom represents a vehicle such as a car or bike for instance, it can also have other properties such as colour or manufacturer. Back in the 60's this was ground breaking, but with the rise of object oriented languages, today this concept is common place and of course core to .NET, so much so that L# doesn't implement property lists. But of course with Lisp its easy to define our own functions to do the same and at the same time use powerful .NET feature to build it.

Here are the key functions:

Code: Select all
(def props ()
  (if (no (bound 'propdb)) (= propdb (new "System.Collections.Hashtable")) propdb)
)

(def putprop (i v p)
   (props)
   (do
     (if (no (.contains propdb i))
           (.add propdb i (new "System.Collections.Hashtable")) )
     (if (.contains (.Item propdb i) p) (.set_Item (.item propdb i) p v)
        (.add (.Item propdb i) p v))
     v)
)

(def get (i p)   
     (props)
     (.item (.item propdb i) p)
)

(def remprop (i p)
    (props)
    (putprop i null p)
)

(def prprop (i)
   (props)
   (with (x (.item propdb i))
      (each y (.keys x) (Console.Writeline "{0,14} = {1}" y  (.item x y)))
   )
)


A brief explanation of the functions. Property lists are global - they have no local scope so the function props ensures the global list is created first time its used. putprop assigns the property value to the atom. The ways I've implemented it here is as a hashtable. So each atom has its own hashtable which contain the properties and their values. get retrieves the property value and remprop will remove it (actually it just assigns it to null). So what can we do with it? The final function is prprop which prints out all the properties and their values for a particular atom. Going back to our example of vehicles we can create two vehicles C1 and C2 - each with a set of unique properties which can then be retrieved when required.

Code: Select all
(putprop 'C1  'RED   'COLOUR)
(putprop 'C1  'CAR   'TYPE)
(putprop 'C1  'FORD  'MAKE)


(putprop 'C2  'BLUE  'COLOUR)
(putprop 'C2  'BIKE  'TYPE)
(putprop 'C2  'BMW   'MAKE)

> (get 'C1 'COLOUR)
RED
> (get 'C2 'COLOUR)
BLUE

> (prprop 'C2)
          MAKE = BMW
        COLOUR = BLUE
          TYPE = CAR

> (remprop 'C2 'TYPE)
null
> (prprop 'C2)
          MAKE   = BMW
        COLOUR = BLUE
          TYPE    =


This makes storing an manipulating information easy(ier). One of the most famous AI programs written is SHRDLU by Terry Winograd in 1972 and it was written in LISP - http://hci.stanford.edu/~winograd/shrdlu/index.html for more info and even on the program and even the source code. It allows for a dialog between a user and the computer about a virtual world of blocks resting on either each other or table. Here is a simple graphic of Blockworld.

Code: Select all
;-----
;    |
;   HAND
;
;   ----      ^
;   |B2|     /B4\
;   ----     ----
;   ----     ----
;   |B1|     |B3|
;   ----     ----
; ==================



What SHRDLU attempts to do is allow a user to ask the question how do I put block B1 on B3. This requires realising that B2 rests on B1 and B4 rests on B3 etc. SHRDLU encapsulates this data in property lists as follows:

Code: Select all
(putprop 'B1 'BLOCK   'TYPE)
(putprop 'B1 'TABLE   'SUPPORTED-BY)
(putprop 'B1 '(B2)    'DIRECTLY-SUPPORTS)
(putprop 'B1 'RED     'COLOUR)

(putprop 'B2 'BLOCK   'TYPE)
(putprop 'B2 '(1 1 2) 'POSITION)
(putprop 'B2 'B1      'SUPPORTED-BY)
(putprop 'B2 'BLUE    'COLOUR)


Notice how B1 is supported by the table and supports B2, whilst B2 is supported by B1. It then uses functions to manipulate that data.

More on that tomorrow ...
Day19 - 6 days to Go !
================

Property list are a powerful feature of Lisp. All through this series atoms have essential one value. It might be a string or a number or list but it is in essence a single thing. The idea behind property list is to give atoms properties that can be stored and read. So if an atom represents a vehicle such as a car or bike for instance, it can also have other properties such as colour or manufacturer. Back in the 60's this was ground breaking, but with the rise of object oriented languages, today this concept is common place and of course core to .NET, so much so that L# doesn't implement property lists. But of course with Lisp its easy to define our own functions to do the same and at the same time use powerful .NET feature to build it.

Here are the key functions:

Code: Select all
(def props ()
  (if (no (bound 'propdb)) (= propdb (new "System.Collections.Hashtable")) propdb)
)

(def putprop (i v p)
   (props)
   (do
     (if (no (.contains propdb i))
           (.add propdb i (new "System.Collections.Hashtable")) )
     (if (.contains (.Item propdb i) p) (.set_Item (.item propdb i) p v)
        (.add (.Item propdb i) p v))
     v)
)

(def get (i p)   
     (props)
     (.item (.item propdb i) p)
)

(def remprop (i p)
    (props)
    (putprop i null p)
)

(def prprop (i)
   (props)
   (with (x (.item propdb i))
      (each y (.keys x) (Console.Writeline "{0,14} = {1}" y  (.item x y)))
   )
)


A brief explanation of the functions. Property lists are global - they have no local scope so the function props ensures the global list is created first time its used. putprop assigns the property value to the atom. The ways I've implemented it here is as a hashtable. So each atom has its own hashtable which contain the properties and their values. get retrieves the property value and remprop will remove it (actually it just assigns it to null). So what can we do with it? The final function is prprop which prints out all the properties and their values for a particular atom. Going back to our example of vehicles we can create two vehicles C1 and C2 - each with a set of unique properties which can then be retrieved when required.

Code: Select all
(putprop 'C1  'RED   'COLOUR)
(putprop 'C1  'CAR   'TYPE)
(putprop 'C1  'FORD  'MAKE)


(putprop 'C2  'BLUE  'COLOUR)
(putprop 'C2  'BIKE  'TYPE)
(putprop 'C2  'BMW   'MAKE)

> (get 'C1 'COLOUR)
RED
> (get 'C2 'COLOUR)
BLUE

> (prprop 'C2)
          MAKE = BMW
        COLOUR = BLUE
          TYPE = CAR

> (remprop 'C2 'TYPE)
null
> (prprop 'C2)
          MAKE   = BMW
        COLOUR = BLUE
          TYPE    =


This makes storing an manipulating information easy(ier). One of the most famous AI programs written is SHRDLU by Terry Winograd in 1972 and it was written in LISP - http://hci.stanford.edu/~winograd/shrdlu/index.html for more info and even on the program and even the source code. It allows for a dialog between a user and the computer about a virtual world of blocks resting on either each other or table. Here is a simple graphic of Blockworld.

Code: Select all
;-----
;    |
;   HAND
;
;   ----      ^
;   |B2|     /B4\
;   ----     ----
;   ----     ----
;   |B1|     |B3|
;   ----     ----
; ==================



What SHRDLU attempts to do is allow a user to ask the question how do I put block B1 on B3. This requires realising that B2 rests on B1 and B4 rests on B3 etc. SHRDLU encapsulates this data in property lists as follows:

Code: Select all
(putprop 'B1 'BLOCK   'TYPE)
(putprop 'B1 'TABLE   'SUPPORTED-BY)
(putprop 'B1 '(B2)    'DIRECTLY-SUPPORTS)
(putprop 'B1 'RED     'COLOUR)

(putprop 'B2 'BLOCK   'TYPE)
(putprop 'B2 '(1 1 2) 'POSITION)
(putprop 'B2 'B1      'SUPPORTED-BY)
(putprop 'B2 'BLUE    'COLOUR)


Notice how B1 is supported by the table and supports B2, whilst B2 is supported by B1. It then uses functions to manipulate that data.

More on that tomorrow ...
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Day20 Blockworld / SHRDLU

Post by l3v3rz » Sun Dec 20, 2009 12:18 am

Post by l3v3rz
Sun Dec 20, 2009 12:18 am

Day20 - 5 Days to Go !
=======================================

Image

The idea behind blockworld was to enable the user to have a dialog with the computer. To enable this the program specifies a world consisting of different shape on a table, and a set of functions that can manipulate those shapes based on a set of rules. So for example
Code: Select all
((def ungrasp (obj) 
     (remprop 'HAND 'GRASPING)     
     (= plan (cons (list 'ungrasp obj) plan )))

(def grasp (obj)   
     (putprop 'HAND obj 'GRASPING)
     (= plan (cons (list 'grasp obj) plan )))

(def movehand (pos)
      (putprop 'HAND pos 'POSITION)
      (= plan (cons (list 'movehand pos) plan )))

(def putat (obj place)
      (do   (grasp obj)
              (moveobj obj place)
              (ungrasp obj)))

(def moveobj (obj place)
       (movehand place))



grasp will set the property of the HAND so that its grasping the object specified. Similarly with ungrasp it clears the property of the hand and movehand changes the position of the hand. The code also tracks what actions have been taken the atom plan. Here is an example session:
Code: Select all
> (grasp 'B1)                    ;; get hold of the Block
((grasp B1))
> (get 'HAND 'GRASPING)  ;; check if the property has been updated
B1
> (ungrasp 'B1)                 ;; now let go of block
((ungrasp B1) (grasp B1))
> (get 'HAND 'GRASPING)  ;; check the property again
null

;; reset the plan
(= plan ())

> (putat 'B1 '(1 1 1))          ;; combined action
((ungrasp B1) (movehand (1 1 1)) (grasp B1))

> (reverse plan)              ;; plan needs to be reversed
((grasp B1) (movehand (1 1 1)) (ungrasp B1))
>



If we ask the system to move an object (putat 'B1 '(1 1 1)) it creates a plan of actions grasp, moveobj, ungrasp. But what if the space being moved to was taken? And how do we update the virtual world model? This requires a more sophisticated model which can take into account when one object B1 is put onto B2 then it also changes the supports. So for instance B1 is supported by the TABLE and B2 supported by B1. If we move B2 we need to remove the B2 SUPPORTED-BY property on B1 as it isn't any longer, but also remove the DIRECTLY-SUPPORTS property on B1. Both objects need updating. In this model one object can DIRECTLY-SUPPORTS multiple objects - so this requires the del function which removes matching elements from a list:
Code: Select all
(def del (e l)
   (if (not l) null
        (is e (car l)) (del e (cdr l)) (cons (car l) (del e (cdr l)))))

; see how it removes all 'HEADS from list leaving just TAILS
> (del 'HEAD '(HEAD TAIL HEAD TAIL TAIL))
(TAIL TAIL TAIL)


(def removesupport (obj)
   (putprop (= sup (get obj 'SUPPORTED-BY)) (del obj (get sup 'DIRECTLY-SUPPORTS))  'DIRECTLY-SUPPORTS)
   (putprop obj null 'SUPPORTED-BY)
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;  BEFORE
;
> (prprop 'B1)
DIRECTLY-SUPPORTS = B2
          TYPE = BLOCK
        COLOUR = RED
  SUPPORTED-BY = TABLE

> (prprop 'B2)
      POSITION = (1 1 2)
          TYPE = BLOCK
        COLOUR = BLUE
  SUPPORTED-BY = B1

> (removesupport 'B2)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;  AFTER
;

> (prprop 'B1)
DIRECTLY-SUPPORTS =
          TYPE = BLOCK
        COLOUR = RED
  SUPPORTED-BY = TABLE

> (prprop 'B2)
      POSITION = (1 1 2)
          TYPE = BLOCK
        COLOUR = BLUE
  SUPPORTED-BY =



So after the (removesupport 'B2) both B1 and B2 have been updated. If we had tried to move B1 the system would detect that its DIRECTLY-SUPPORTS 'B2. And so it would then first attempt to move B2 before changing the support on B1. Similarly you can create addsupport. Its a bit more complex. It needs to check if the object can be supported. So in Blockworld whilst a BLOCK can support another BLOCK, an object of type BALL or PYRAMID can't. So you couldn't put B1 on B4 (the pyramid - see yesterdays diagram)

By adding more functions (I'll make my simple version "asdfg.lisp" available in the final download) you can create a set of functions that allow a command (puton 'B1 'B3) which then creates a plan of how to put one block on top of another. In the full SHRDLU you can have a dialog along the lines:

> PICK UP THE RED BLOCK

: WHICH RED BLOCK - THERE IS MORE THAN 1
> PICK UP B2
: OK
> PUT ON B3
: (GRASP B4)
: (MOVE TO TABLE)
: (UNGRASP B4)
: (GRASP B2 BLOCK)
: (MOVE HAND TO B3 BLOCK)
: (UNGRASP B2)
: OK

>

Of course this requires natural language parser which s beyond the scope of these posts (look at the link to SHDRLU in yesterdays post for more details and the original source code!).

Why is this useful ? Because for robots to do more the play preprogrammed moves they need the ability to plan ahead. This went out of fashion during the 90's as top down AI was replaced with bottom up AI based on sensory input, but may be its time for a comeback ?

Another famous AI program written in LISP is the simulation of dialog with a psychiatrist http://norvig.com/paip/eliza.lisp.

Tomorrow: Doctor Doctor
Day20 - 5 Days to Go !
=======================================

Image

The idea behind blockworld was to enable the user to have a dialog with the computer. To enable this the program specifies a world consisting of different shape on a table, and a set of functions that can manipulate those shapes based on a set of rules. So for example
Code: Select all
((def ungrasp (obj) 
     (remprop 'HAND 'GRASPING)     
     (= plan (cons (list 'ungrasp obj) plan )))

(def grasp (obj)   
     (putprop 'HAND obj 'GRASPING)
     (= plan (cons (list 'grasp obj) plan )))

(def movehand (pos)
      (putprop 'HAND pos 'POSITION)
      (= plan (cons (list 'movehand pos) plan )))

(def putat (obj place)
      (do   (grasp obj)
              (moveobj obj place)
              (ungrasp obj)))

(def moveobj (obj place)
       (movehand place))



grasp will set the property of the HAND so that its grasping the object specified. Similarly with ungrasp it clears the property of the hand and movehand changes the position of the hand. The code also tracks what actions have been taken the atom plan. Here is an example session:
Code: Select all
> (grasp 'B1)                    ;; get hold of the Block
((grasp B1))
> (get 'HAND 'GRASPING)  ;; check if the property has been updated
B1
> (ungrasp 'B1)                 ;; now let go of block
((ungrasp B1) (grasp B1))
> (get 'HAND 'GRASPING)  ;; check the property again
null

;; reset the plan
(= plan ())

> (putat 'B1 '(1 1 1))          ;; combined action
((ungrasp B1) (movehand (1 1 1)) (grasp B1))

> (reverse plan)              ;; plan needs to be reversed
((grasp B1) (movehand (1 1 1)) (ungrasp B1))
>



If we ask the system to move an object (putat 'B1 '(1 1 1)) it creates a plan of actions grasp, moveobj, ungrasp. But what if the space being moved to was taken? And how do we update the virtual world model? This requires a more sophisticated model which can take into account when one object B1 is put onto B2 then it also changes the supports. So for instance B1 is supported by the TABLE and B2 supported by B1. If we move B2 we need to remove the B2 SUPPORTED-BY property on B1 as it isn't any longer, but also remove the DIRECTLY-SUPPORTS property on B1. Both objects need updating. In this model one object can DIRECTLY-SUPPORTS multiple objects - so this requires the del function which removes matching elements from a list:
Code: Select all
(def del (e l)
   (if (not l) null
        (is e (car l)) (del e (cdr l)) (cons (car l) (del e (cdr l)))))

; see how it removes all 'HEADS from list leaving just TAILS
> (del 'HEAD '(HEAD TAIL HEAD TAIL TAIL))
(TAIL TAIL TAIL)


(def removesupport (obj)
   (putprop (= sup (get obj 'SUPPORTED-BY)) (del obj (get sup 'DIRECTLY-SUPPORTS))  'DIRECTLY-SUPPORTS)
   (putprop obj null 'SUPPORTED-BY)
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;  BEFORE
;
> (prprop 'B1)
DIRECTLY-SUPPORTS = B2
          TYPE = BLOCK
        COLOUR = RED
  SUPPORTED-BY = TABLE

> (prprop 'B2)
      POSITION = (1 1 2)
          TYPE = BLOCK
        COLOUR = BLUE
  SUPPORTED-BY = B1

> (removesupport 'B2)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;  AFTER
;

> (prprop 'B1)
DIRECTLY-SUPPORTS =
          TYPE = BLOCK
        COLOUR = RED
  SUPPORTED-BY = TABLE

> (prprop 'B2)
      POSITION = (1 1 2)
          TYPE = BLOCK
        COLOUR = BLUE
  SUPPORTED-BY =



So after the (removesupport 'B2) both B1 and B2 have been updated. If we had tried to move B1 the system would detect that its DIRECTLY-SUPPORTS 'B2. And so it would then first attempt to move B2 before changing the support on B1. Similarly you can create addsupport. Its a bit more complex. It needs to check if the object can be supported. So in Blockworld whilst a BLOCK can support another BLOCK, an object of type BALL or PYRAMID can't. So you couldn't put B1 on B4 (the pyramid - see yesterdays diagram)

By adding more functions (I'll make my simple version "asdfg.lisp" available in the final download) you can create a set of functions that allow a command (puton 'B1 'B3) which then creates a plan of how to put one block on top of another. In the full SHRDLU you can have a dialog along the lines:

> PICK UP THE RED BLOCK

: WHICH RED BLOCK - THERE IS MORE THAN 1
> PICK UP B2
: OK
> PUT ON B3
: (GRASP B4)
: (MOVE TO TABLE)
: (UNGRASP B4)
: (GRASP B2 BLOCK)
: (MOVE HAND TO B3 BLOCK)
: (UNGRASP B2)
: OK

>

Of course this requires natural language parser which s beyond the scope of these posts (look at the link to SHDRLU in yesterdays post for more details and the original source code!).

Why is this useful ? Because for robots to do more the play preprogrammed moves they need the ability to plan ahead. This went out of fashion during the 90's as top down AI was replaced with bottom up AI based on sensory input, but may be its time for a comeback ?

Another famous AI program written in LISP is the simulation of dialog with a psychiatrist http://norvig.com/paip/eliza.lisp.

Tomorrow: Doctor Doctor
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Day21 Searching and matching

Post by l3v3rz » Sun Dec 20, 2009 11:49 pm

Post by l3v3rz
Sun Dec 20, 2009 11:49 pm

Day21 - 4 Days to Go !
================
Doctor, Doctor I think I'm suffering from Deja Vu!
Didn't I see you yesterday?


A useful feature is to compare two lists, or to search one list for matching entries in a second list. The modern way to do this would be to use regular expression searching but its easy to add a function match to do this in LISP (LSharp.NET) with lists and it also shows how this type of matching works.

The following match function takes two list and if they identical returns true, but if they're different returns null. It also will treat ">" as a wild card that will match with any ONE character. This makes the matching far more powerful. Here's the code with some examples:

Code: Select all
(def match ( p d)
  (if (and (empty? p) (empty? d)) t
      (or  (empty? p) (empty? d)) nil
      (or  (is (car p) '>)  (is (car p) (car d)))
         (match (cdr p) (cdr d))
))

;;examples

(match '( a b c)  '(a b c))
True
> (match '( a b c)  '(a b d)) 
null
> (match '( a > c)  '(a b c)) 
True


Doctor, Doctor I keep thinking there is two of me
One at a time please


So a simple match is fine, but what if we want to match multiple items. This requires a recursive search. When the atom "+" is identified it then continues to match the remaining items. Here is the code:

Code: Select all
(def match ( p d)
  (if (and (empty? p) (empty? d)) t
      (or  (empty? p) (empty? d)) nil
      (or  (is (car p) '>)  (is (car p) (car d)))
         (match (cdr p) (cdr d))
      (is  (car p) '+)
        (if (match (cdr p) (cdr d)) true (match p (cdr d)))
   )
)

;; more examples

> (match '( a > c)  '(a b d c))
null
> (match '( a + c)  '(a b d c))
True
>


So in this example the "+" matched with the b and d, and the function can then return true as the whole list is then matched. This is useful, but it would be even better if it not only did it find the match but also what items in the list matched - and this leads to the final version of match

Code: Select all
;utilities
(def atomcar (z) (str (car (str z))))
(def atomcdr (z) (str (cdr (str z))))
(def setstr  (x y) (LSharp.Runtime.VarSet (LSharp.Symbol.FromName x) y environment))
(def evalstr (x) (eval (LSharp.Symbol.FromName x)))

;match function
(def match ( p d)
  (if (and (empty? p) (empty? d)) t
      (or  (empty? p) (empty? d)) nil
      (or  (is (car p) '>)  (is (car p) (car d)))
         (match (cdr p) (cdr d))
      (and (is (atomcar (car p)) ">") (match (cdr p) (cdr d)))
         (setstr (atomcdr (car p)) (car d))
      (is  (car p) '+)
        (if (match (cdr p) (cdr d)) true (match p (cdr d)))
      (is (atomcar (car p)) "+")
         (if (match (cdr p) (cdr d)) (setstr (atomcdr (car p)) (list (car d)))
            (match p (cdr d))        (setstr (atomcdr (car p)) (cons (car d) (evalstr (atomcdr (car p))))
         )
     )
  )
)

;;even more examples

> (match '( a >L c) '(a b c)) 
b
> (match '( a +G c) '(a b d c))
(b d)


This has introduced a couple of extra functions, atomcar which returns the first character of an atom. So (atomcar 'HELLO) returns "H", whilst (atomcdr 'HELLO) return "ELLO". setstr is the string equivalent of (= H 0) i.e. (setstr "H" 0) will assign the atom H the value 0. The new match will now allow a symbol to be appended to the search character ">L", which if the match succeeds is assigned the values that are matched. In the above examples the L is assigned b and the G assigned (b d). Very powerful!

Doctor, Doctor I feel like a spoon!
Well sit still and don't stir!


So now we have matching we can recreate one of the most well known AI programs of all time, Eliza the psychiatrist. http://en.wikipedia.org/wiki/ELIZA.

From Wikipedia: First implemented in Weizenbaum's own SLIP list-processing language, ELIZA worked by simple parsing and substitution of key words into canned phrases. Depending upon the initial entries by the user the illusion of a human writer could be instantly dispelled, or could continue through several interchanges. It was sometimes so convincing that there are many anecdotes about people becoming very emotionally caught up in dealing with DOCTOR for several minutes until the machine's true lack of understanding became apparent

Code: Select all
(def evalstring (s)  (eval (LSharp.Runtime.ReadFromString s)))

(def doctor ()
  "Demo"
  (with (item 1)
     (while (not (is item 0))
         (do
            (while (is (= inp (Console.ReadLine)) "" ) (pr ": ")))
            (= inpt (evalstring (+ "'" (.ToUpper inp))))
       (if
              (iso inpt 'BYE)
                  (do (prn "GOODBYE" )(err "Done"))

              (match '(I AM WORRIED ABOUT +L) inpt)
                  (prn "WHY ARE YOU WORRIED ABOUT " L "?")

              (match '(+ MOTHER +) inpt)
                  (prn "TELL ME MORE ABOUT YOUR FAMILY")

              (prn "TELL ME MORE")
            )
       (pr ": ")
         )
     )
)
;
; sample session
;

> (doctor)
: (HELLO)
TELL ME MORE
: (I AM WORRIED ABOUT FRED)
WHY ARE YOU WORRIED ABOUT FRED?
: (ITS HIS MOTHER REALLY)
TELL ME MORE ABOUT YOUR FAMILY
: BYE
GOODBYE



The code uses the match function to search pre-canned phrases and uses the assignment mechanism to the work some of the question back into the response - which is where the program gets is ability mimic understanding. The user input is converted into a list using evalstring. So this takes an input string such as "(this is input)" into '(THIS IS INPUT). To make this more powerful and believable requires far more match rules. If you go the this link http://norvig.com/paip/eliza.lisp you will see a much more developed version written in 1991 by Peter Novig in LISP.

Doctor, Doctor Can I have second opinion?
Of course, come back tomorrow!


Tomorrow: Useful features and functions
Day21 - 4 Days to Go !
================
Doctor, Doctor I think I'm suffering from Deja Vu!
Didn't I see you yesterday?


A useful feature is to compare two lists, or to search one list for matching entries in a second list. The modern way to do this would be to use regular expression searching but its easy to add a function match to do this in LISP (LSharp.NET) with lists and it also shows how this type of matching works.

The following match function takes two list and if they identical returns true, but if they're different returns null. It also will treat ">" as a wild card that will match with any ONE character. This makes the matching far more powerful. Here's the code with some examples:

Code: Select all
(def match ( p d)
  (if (and (empty? p) (empty? d)) t
      (or  (empty? p) (empty? d)) nil
      (or  (is (car p) '>)  (is (car p) (car d)))
         (match (cdr p) (cdr d))
))

;;examples

(match '( a b c)  '(a b c))
True
> (match '( a b c)  '(a b d)) 
null
> (match '( a > c)  '(a b c)) 
True


Doctor, Doctor I keep thinking there is two of me
One at a time please


So a simple match is fine, but what if we want to match multiple items. This requires a recursive search. When the atom "+" is identified it then continues to match the remaining items. Here is the code:

Code: Select all
(def match ( p d)
  (if (and (empty? p) (empty? d)) t
      (or  (empty? p) (empty? d)) nil
      (or  (is (car p) '>)  (is (car p) (car d)))
         (match (cdr p) (cdr d))
      (is  (car p) '+)
        (if (match (cdr p) (cdr d)) true (match p (cdr d)))
   )
)

;; more examples

> (match '( a > c)  '(a b d c))
null
> (match '( a + c)  '(a b d c))
True
>


So in this example the "+" matched with the b and d, and the function can then return true as the whole list is then matched. This is useful, but it would be even better if it not only did it find the match but also what items in the list matched - and this leads to the final version of match

Code: Select all
;utilities
(def atomcar (z) (str (car (str z))))
(def atomcdr (z) (str (cdr (str z))))
(def setstr  (x y) (LSharp.Runtime.VarSet (LSharp.Symbol.FromName x) y environment))
(def evalstr (x) (eval (LSharp.Symbol.FromName x)))

;match function
(def match ( p d)
  (if (and (empty? p) (empty? d)) t
      (or  (empty? p) (empty? d)) nil
      (or  (is (car p) '>)  (is (car p) (car d)))
         (match (cdr p) (cdr d))
      (and (is (atomcar (car p)) ">") (match (cdr p) (cdr d)))
         (setstr (atomcdr (car p)) (car d))
      (is  (car p) '+)
        (if (match (cdr p) (cdr d)) true (match p (cdr d)))
      (is (atomcar (car p)) "+")
         (if (match (cdr p) (cdr d)) (setstr (atomcdr (car p)) (list (car d)))
            (match p (cdr d))        (setstr (atomcdr (car p)) (cons (car d) (evalstr (atomcdr (car p))))
         )
     )
  )
)

;;even more examples

> (match '( a >L c) '(a b c)) 
b
> (match '( a +G c) '(a b d c))
(b d)


This has introduced a couple of extra functions, atomcar which returns the first character of an atom. So (atomcar 'HELLO) returns "H", whilst (atomcdr 'HELLO) return "ELLO". setstr is the string equivalent of (= H 0) i.e. (setstr "H" 0) will assign the atom H the value 0. The new match will now allow a symbol to be appended to the search character ">L", which if the match succeeds is assigned the values that are matched. In the above examples the L is assigned b and the G assigned (b d). Very powerful!

Doctor, Doctor I feel like a spoon!
Well sit still and don't stir!


So now we have matching we can recreate one of the most well known AI programs of all time, Eliza the psychiatrist. http://en.wikipedia.org/wiki/ELIZA.

From Wikipedia: First implemented in Weizenbaum's own SLIP list-processing language, ELIZA worked by simple parsing and substitution of key words into canned phrases. Depending upon the initial entries by the user the illusion of a human writer could be instantly dispelled, or could continue through several interchanges. It was sometimes so convincing that there are many anecdotes about people becoming very emotionally caught up in dealing with DOCTOR for several minutes until the machine's true lack of understanding became apparent

Code: Select all
(def evalstring (s)  (eval (LSharp.Runtime.ReadFromString s)))

(def doctor ()
  "Demo"
  (with (item 1)
     (while (not (is item 0))
         (do
            (while (is (= inp (Console.ReadLine)) "" ) (pr ": ")))
            (= inpt (evalstring (+ "'" (.ToUpper inp))))
       (if
              (iso inpt 'BYE)
                  (do (prn "GOODBYE" )(err "Done"))

              (match '(I AM WORRIED ABOUT +L) inpt)
                  (prn "WHY ARE YOU WORRIED ABOUT " L "?")

              (match '(+ MOTHER +) inpt)
                  (prn "TELL ME MORE ABOUT YOUR FAMILY")

              (prn "TELL ME MORE")
            )
       (pr ": ")
         )
     )
)
;
; sample session
;

> (doctor)
: (HELLO)
TELL ME MORE
: (I AM WORRIED ABOUT FRED)
WHY ARE YOU WORRIED ABOUT FRED?
: (ITS HIS MOTHER REALLY)
TELL ME MORE ABOUT YOUR FAMILY
: BYE
GOODBYE



The code uses the match function to search pre-canned phrases and uses the assignment mechanism to the work some of the question back into the response - which is where the program gets is ability mimic understanding. The user input is converted into a list using evalstring. So this takes an input string such as "(this is input)" into '(THIS IS INPUT). To make this more powerful and believable requires far more match rules. If you go the this link http://norvig.com/paip/eliza.lisp you will see a much more developed version written in 1991 by Peter Novig in LISP.

Doctor, Doctor Can I have second opinion?
Of course, come back tomorrow!


Tomorrow: Useful features and functions
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Post by PedroR » Mon Dec 21, 2009 12:34 pm

Post by PedroR
Mon Dec 21, 2009 12:34 pm

hi l3v3rz

I was amused by your cartoon on the previous post! lol

I myself did work with LISP back in college but that endless amount of parenthesis are not for the faint of heart ! :shock:

Great work you're doing on your posts :)

Are you running this on your roboard already?

Regards
Pedro.
hi l3v3rz

I was amused by your cartoon on the previous post! lol

I myself did work with LISP back in college but that endless amount of parenthesis are not for the faint of heart ! :shock:

Great work you're doing on your posts :)

Are you running this on your roboard already?

Regards
Pedro.
PedroR
Savvy Roboteer
Savvy Roboteer
Posts: 1199
Joined: Mon Jun 16, 2008 11:07 pm

Day22 Useful routines

Post by l3v3rz » Mon Dec 21, 2009 11:31 pm

Post by l3v3rz
Mon Dec 21, 2009 11:31 pm

Day22 - 3 Days to Go!
================

Todays post covers a number of useful functions

variable number of arguments

So far all user created function have had a fixed number of arguments, such as (addone 5) but some of the inbuilt functions like + take a variable number of arguments (+ 1 2 3). User functions can do the same using "&" This will wrap all the following arguments up in a single list. If there are mandatory arguments they must precede the & i.e. (a b & c) means there must be at least two argument a and b, and all remaining args are assigned to c. here a a couple of examples:

Code: Select all
> (def test (& x)   (map prn x))

> (test '(a b c d))
abcd
((a b c d))
> (test 'a 'b 'c 'd)
a
b
c
d
(a b c d)
> (test)
null


In the final version of run_robobuilder optional arguments can be passed so that the COM port is preselected and menus are by-passed.

macros
Theres are like variable argument commands, but with a major difference. The arguments aren't evaluated - instead the macro is expanded and then the functions are evaluated. This enables macros to generate code that a function couldn't. Here is an example of a macro used to create a repeat function:

Code: Select all
(mac repeat (n & body) `(for x 1 ,n ,@body))
LSharp.Macro
> (repeat 5 (prn "hello world"))
hello world
hello world
hello world
hello world
hello world
"hello world"


Exceptions
err is a base function that throws an exception. Exceptions are built into .NET code, but err is actually a very old LISP command - and was defined back in LISP 1.6 days. The principle is the same - an exception causes the code to stop what its doing and return to where it was called immediately. Ideally you would be able to "catch" the error but V2 of Lsharp/L# doesn't seem to have implemented this (or errset in old style LISP).

pause / getch
Its often useful to read a character from the terminal. if an entire line is required - up to the CRLF then use Console.Readline, but for single character there is Console.ReadKey. Depending on what is required, there is ReadKey which waits for a key press (and optionally echoes it). The value of the key pressed can then be read using either "keychar" or "key". "keychar" gives the ASCII values whilst "key" gives the ConsoleKey value.

The pause function uses getch to loop until a 'p' is pressed - echoing a '.' for any other key typed. It uses the "keychar" method to read the ASCII value. There is also KeyAvailable method which is set true when a key has been pressed. This enables a non-blocking read - in the example of the exit? function it uses this to see if a user want to break out of a loop - so if a key is available its read - and if its a Q, an exception is thrown using err command explained above. Notice the use of ConsoleKey.Q so it doesn't matter if it upper or lowercase (or even ctrl Q!)

Code: Select all
(def getch () (.keychar (Console.ReadKey true)))

(def pause ()
  (prn "Paused - press 'p' to continue")
  ( while (not (is (getch) #\p)) (pr "."))
)

(def exit? ()
   (if (Console.keyavailable)
     (if (is (.key (Console.ReadKey true)) (ConsoleKey.Q)) (err "Quit Pressed"))))


time
time - times any function given to it. Very useful for seeing the performance of the link between the PC and robobuilder for instance.

Code: Select all
(time (pr "hello"))
> (time (prn "hello"))
hello
time: 2 msec
"hello"
> (time (prn "hello"))
hello
time: 1 msec
"hello"
> (time (prn "hello"))
hello
time: 2 msec
"hello"


map
map is a function that applies its first argument (which must be a function) to each element in the list supplied as its second argument. (In L# this seems to only work with the first list supplied - in LISP it should apply to every list supplied. A couple of examples, firstly using a predefined function such as prn and second using a user defined function addone.

Code: Select all
(map prn '(a b c))
> (map prn '(a b c))
a
b
c
(a b c)

(def addone (x) (+ x 1))
> (addone 6)
7
> (map addone '(2 4 8))
(3 5 9)
>


I use map in Blockworld code in a previous post where I want to display the properties of all the elements used: (map prprop '(B1 B2 B3 B4 TABLE HAND))

Vectors
One might think of vector maths as being for a different type of programming language but given vectors are just lists Lisp is actually very good at them. To do the vector product of two vectors (a b c) (d e f) gives the result (a*d+b*e+c*f). This can be implemented as a simple recursive function. And if we have the dot product we can normalise a vector by taking the square root of the dot product of a vector with itself. Vector routines enable the output of the accelerometer to be processed easily.

Code: Select all
(def dot-product (a b)
  "calculate dot product of two vectors."
  (if (or (empty? a) (empty? b))
      0
      (+ (* (first a) (first b))
      (dot-product (rest a) (rest b)))
  )
)

(def norm (a)
  "normalise a vector i.e. sqrt(a.a)"
  (sqrt (dot-product a a))
)

> (dot-product '(5 4 2) '(1 0 0 ))
5
> (dot-product '(5 4 2) '(0 1 0 ))
4
> (dot-product '(5 4 2) '(1 1 0 ))
9
> (norm '(5 4 2))
6.70820393249937


Tomorrow: penultimate post
Day22 - 3 Days to Go!
================

Todays post covers a number of useful functions

variable number of arguments

So far all user created function have had a fixed number of arguments, such as (addone 5) but some of the inbuilt functions like + take a variable number of arguments (+ 1 2 3). User functions can do the same using "&" This will wrap all the following arguments up in a single list. If there are mandatory arguments they must precede the & i.e. (a b & c) means there must be at least two argument a and b, and all remaining args are assigned to c. here a a couple of examples:

Code: Select all
> (def test (& x)   (map prn x))

> (test '(a b c d))
abcd
((a b c d))
> (test 'a 'b 'c 'd)
a
b
c
d
(a b c d)
> (test)
null


In the final version of run_robobuilder optional arguments can be passed so that the COM port is preselected and menus are by-passed.

macros
Theres are like variable argument commands, but with a major difference. The arguments aren't evaluated - instead the macro is expanded and then the functions are evaluated. This enables macros to generate code that a function couldn't. Here is an example of a macro used to create a repeat function:

Code: Select all
(mac repeat (n & body) `(for x 1 ,n ,@body))
LSharp.Macro
> (repeat 5 (prn "hello world"))
hello world
hello world
hello world
hello world
hello world
"hello world"


Exceptions
err is a base function that throws an exception. Exceptions are built into .NET code, but err is actually a very old LISP command - and was defined back in LISP 1.6 days. The principle is the same - an exception causes the code to stop what its doing and return to where it was called immediately. Ideally you would be able to "catch" the error but V2 of Lsharp/L# doesn't seem to have implemented this (or errset in old style LISP).

pause / getch
Its often useful to read a character from the terminal. if an entire line is required - up to the CRLF then use Console.Readline, but for single character there is Console.ReadKey. Depending on what is required, there is ReadKey which waits for a key press (and optionally echoes it). The value of the key pressed can then be read using either "keychar" or "key". "keychar" gives the ASCII values whilst "key" gives the ConsoleKey value.

The pause function uses getch to loop until a 'p' is pressed - echoing a '.' for any other key typed. It uses the "keychar" method to read the ASCII value. There is also KeyAvailable method which is set true when a key has been pressed. This enables a non-blocking read - in the example of the exit? function it uses this to see if a user want to break out of a loop - so if a key is available its read - and if its a Q, an exception is thrown using err command explained above. Notice the use of ConsoleKey.Q so it doesn't matter if it upper or lowercase (or even ctrl Q!)

Code: Select all
(def getch () (.keychar (Console.ReadKey true)))

(def pause ()
  (prn "Paused - press 'p' to continue")
  ( while (not (is (getch) #\p)) (pr "."))
)

(def exit? ()
   (if (Console.keyavailable)
     (if (is (.key (Console.ReadKey true)) (ConsoleKey.Q)) (err "Quit Pressed"))))


time
time - times any function given to it. Very useful for seeing the performance of the link between the PC and robobuilder for instance.

Code: Select all
(time (pr "hello"))
> (time (prn "hello"))
hello
time: 2 msec
"hello"
> (time (prn "hello"))
hello
time: 1 msec
"hello"
> (time (prn "hello"))
hello
time: 2 msec
"hello"


map
map is a function that applies its first argument (which must be a function) to each element in the list supplied as its second argument. (In L# this seems to only work with the first list supplied - in LISP it should apply to every list supplied. A couple of examples, firstly using a predefined function such as prn and second using a user defined function addone.

Code: Select all
(map prn '(a b c))
> (map prn '(a b c))
a
b
c
(a b c)

(def addone (x) (+ x 1))
> (addone 6)
7
> (map addone '(2 4 8))
(3 5 9)
>


I use map in Blockworld code in a previous post where I want to display the properties of all the elements used: (map prprop '(B1 B2 B3 B4 TABLE HAND))

Vectors
One might think of vector maths as being for a different type of programming language but given vectors are just lists Lisp is actually very good at them. To do the vector product of two vectors (a b c) (d e f) gives the result (a*d+b*e+c*f). This can be implemented as a simple recursive function. And if we have the dot product we can normalise a vector by taking the square root of the dot product of a vector with itself. Vector routines enable the output of the accelerometer to be processed easily.

Code: Select all
(def dot-product (a b)
  "calculate dot product of two vectors."
  (if (or (empty? a) (empty? b))
      0
      (+ (* (first a) (first b))
      (dot-product (rest a) (rest b)))
  )
)

(def norm (a)
  "normalise a vector i.e. sqrt(a.a)"
  (sqrt (dot-product a a))
)

> (dot-product '(5 4 2) '(1 0 0 ))
5
> (dot-product '(5 4 2) '(0 1 0 ))
4
> (dot-product '(5 4 2) '(1 1 0 ))
9
> (norm '(5 4 2))
6.70820393249937


Tomorrow: penultimate post
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Day23 Using a joystick with L#

Post by l3v3rz » Wed Dec 23, 2009 12:16 am

Post by l3v3rz
Wed Dec 23, 2009 12:16 am

Day23 - 2 days to go !
================

As mentioned in a previous post it's possible to use DirectX libraries and L# to read a joystick. The joystick is an input device that can have many different features, for instance analogue or digital style, varying numbers of buttons and even double or triple axis, vibration, tilt. All these parameters are controllable via the device handle. So the first piece of code gets the user to select the device to be used. askjoy is similar to the serialPort selected in that its lists the devices returned and then ask the user to input a number corresponding to device required. Here's the code with an example of it running. You need ask function from Day7.lisp.

Code: Select all
(reference "Microsoft.DirectX.DirectInput")
(using "Microsoft.DirectX.DirectInput")


(def askjoy ()
  "Pick joystick from List"
  (with ( n 1 menu '(0 Exit))
    (= gl (Manager.GetDevices (DeviceClass.GameControl) (EnumDevicesFlags.AttachedOnly) ))
 
    (each l gl (
         do
         (= menu (cons n (cons  (.InstanceName l) menu)))
         (= n (+ n 1)))
    )       
    (= n (ask "Select Joystick Device" "No such option" menu))
   
    ;This picks the item from the list
    (if (is n 0) null (nth gl (- n 1)))
  )
)

> (askjoy)
Select Joystick Device
1 - ?
0 - Exit
: 1
Usage: 5
UsagePage: 1
FFDriverGuid: 00000000-0000-0000-0000-000000000000
ProductName: ?
InstanceName: ?
DeviceSubType: 2
DeviceType: Gamepad
ProductGuid: 990207b5-0000-0000-0000-504944564944
InstanceGuid: bcb9fe70-eb3a-11de-8001-444553540000


The joystick I have is a very simple 2 axis, 8 button digital XY input (i.e. off or on!) controller. Its device name is "?" - not very useful ! Once the handle is selected the device (like a serialport) needs to be acquired so that this application (and no other) has exclusive use. The setjoy function does this and then displays the capabilities of the device. In the example you can see it outputs that its a 2 axis, 8 button controller. It also sets a global handle jd.

Code: Select all
(def setjoy (l)
  "Aquire joystick for use by application"
  ;This creates the Device handle
  (= jd (new "Device" (.InstanceGuid l)))

  ;This specifies its a Joystick
  (.SetDataFormat jd (DeviceDataFormat.Joystick))

  ;This grabs it for your application
  (.Acquire jd)

  ;This lets you see the capabilities
  ( = cps (.Caps jd))
  (prn "Aquired - axes=" (.NumberAxes cps) " buttons=" (.NumberButtons cps))
)

> (setjoy j)
Aquired - axes=2 buttons=8
8
>


The joystick is now ready to be used. To read the button state the code must first call .poll method. The current state can then be read which is an object containing all the current values of the controller. The button states are available via the .getbuttons method. This returns a list, each element corresponding to a different button, first being button1 etc. If the button is pressed its value is 128, else its 0 ( I assume some buttons can now be analogue as well and so this value may also vary). The function readjoy looks to see if a button or axis has been set. The getButton function waits until a button is pressed and then waits until its let go (this is know as de-bouncing). It then returns the value of the button pressed using a list rconmap to translate to a meaningful value:

Code: Select all
(= rconmap '((0 Button1) (1 Button2) (2 Button3) ))
(= rconx   '((0 Left) (65535 Right)))
(= rcony   '((0 Up)   (65535 Down )))

(def readjoy(t l)
   (.poll jd)
   (= cs (.CurrentJoystickstate jd))
   
   (= but (tolist (.getbuttons (.CurrentJoystickstate jd))))
   
   (= r null) 
   (if (is t "BUTTON")
         (each b l
            (if (> (nth but (car b)) 0) (= r (cadr b)))
         )
       (is t "X")
         (each b l
            (if (is (.X cs) (car b) )   (= r (cadr b)))
         )
       (is t "Y")         
         (each b l
            (if (is (.Y cs) (car b) )   (= r (cadr b)))
         )
   )
   r 
)

(def getButton ()
  (with (r () a ())
     (while (not a) (= a (readjoy "BUTTON" rconmap)))
     (= r a)
     (while (or a) (= a (readjoy "BUTTON" rconmap)))
     r 
))

)
LSharp.Function
> (getButton)
Button2
>


In the example above when getButton is called it will wait until Button2 is pressed (and let go) and then displayed "Button2". Similar code can be written to handle the XY axis or all 3 simultaneously. There also a lookup table (but not the code) to do the translation. With the axis button the value varies from 0 min (left or up), 32767 (center) and 65535 max - (right or down). Again these can be translated into meaningful strings. The code can then use an if statement to wait for a button press and then play the appropriate motion in the same way the code work in Day7 except it uses keyboard input.

Tomorrow: Final Post !
Day23 - 2 days to go !
================

As mentioned in a previous post it's possible to use DirectX libraries and L# to read a joystick. The joystick is an input device that can have many different features, for instance analogue or digital style, varying numbers of buttons and even double or triple axis, vibration, tilt. All these parameters are controllable via the device handle. So the first piece of code gets the user to select the device to be used. askjoy is similar to the serialPort selected in that its lists the devices returned and then ask the user to input a number corresponding to device required. Here's the code with an example of it running. You need ask function from Day7.lisp.

Code: Select all
(reference "Microsoft.DirectX.DirectInput")
(using "Microsoft.DirectX.DirectInput")


(def askjoy ()
  "Pick joystick from List"
  (with ( n 1 menu '(0 Exit))
    (= gl (Manager.GetDevices (DeviceClass.GameControl) (EnumDevicesFlags.AttachedOnly) ))
 
    (each l gl (
         do
         (= menu (cons n (cons  (.InstanceName l) menu)))
         (= n (+ n 1)))
    )       
    (= n (ask "Select Joystick Device" "No such option" menu))
   
    ;This picks the item from the list
    (if (is n 0) null (nth gl (- n 1)))
  )
)

> (askjoy)
Select Joystick Device
1 - ?
0 - Exit
: 1
Usage: 5
UsagePage: 1
FFDriverGuid: 00000000-0000-0000-0000-000000000000
ProductName: ?
InstanceName: ?
DeviceSubType: 2
DeviceType: Gamepad
ProductGuid: 990207b5-0000-0000-0000-504944564944
InstanceGuid: bcb9fe70-eb3a-11de-8001-444553540000


The joystick I have is a very simple 2 axis, 8 button digital XY input (i.e. off or on!) controller. Its device name is "?" - not very useful ! Once the handle is selected the device (like a serialport) needs to be acquired so that this application (and no other) has exclusive use. The setjoy function does this and then displays the capabilities of the device. In the example you can see it outputs that its a 2 axis, 8 button controller. It also sets a global handle jd.

Code: Select all
(def setjoy (l)
  "Aquire joystick for use by application"
  ;This creates the Device handle
  (= jd (new "Device" (.InstanceGuid l)))

  ;This specifies its a Joystick
  (.SetDataFormat jd (DeviceDataFormat.Joystick))

  ;This grabs it for your application
  (.Acquire jd)

  ;This lets you see the capabilities
  ( = cps (.Caps jd))
  (prn "Aquired - axes=" (.NumberAxes cps) " buttons=" (.NumberButtons cps))
)

> (setjoy j)
Aquired - axes=2 buttons=8
8
>


The joystick is now ready to be used. To read the button state the code must first call .poll method. The current state can then be read which is an object containing all the current values of the controller. The button states are available via the .getbuttons method. This returns a list, each element corresponding to a different button, first being button1 etc. If the button is pressed its value is 128, else its 0 ( I assume some buttons can now be analogue as well and so this value may also vary). The function readjoy looks to see if a button or axis has been set. The getButton function waits until a button is pressed and then waits until its let go (this is know as de-bouncing). It then returns the value of the button pressed using a list rconmap to translate to a meaningful value:

Code: Select all
(= rconmap '((0 Button1) (1 Button2) (2 Button3) ))
(= rconx   '((0 Left) (65535 Right)))
(= rcony   '((0 Up)   (65535 Down )))

(def readjoy(t l)
   (.poll jd)
   (= cs (.CurrentJoystickstate jd))
   
   (= but (tolist (.getbuttons (.CurrentJoystickstate jd))))
   
   (= r null) 
   (if (is t "BUTTON")
         (each b l
            (if (> (nth but (car b)) 0) (= r (cadr b)))
         )
       (is t "X")
         (each b l
            (if (is (.X cs) (car b) )   (= r (cadr b)))
         )
       (is t "Y")         
         (each b l
            (if (is (.Y cs) (car b) )   (= r (cadr b)))
         )
   )
   r 
)

(def getButton ()
  (with (r () a ())
     (while (not a) (= a (readjoy "BUTTON" rconmap)))
     (= r a)
     (while (or a) (= a (readjoy "BUTTON" rconmap)))
     r 
))

)
LSharp.Function
> (getButton)
Button2
>


In the example above when getButton is called it will wait until Button2 is pressed (and let go) and then displayed "Button2". Similar code can be written to handle the XY axis or all 3 simultaneously. There also a lookup table (but not the code) to do the translation. With the axis button the value varies from 0 min (left or up), 32767 (center) and 65535 max - (right or down). Again these can be translated into meaningful strings. The code can then use an if statement to wait for a button press and then play the appropriate motion in the same way the code work in Day7 except it uses keyboard input.

Tomorrow: Final Post !
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Day24 Final post -

Post by l3v3rz » Wed Dec 23, 2009 9:42 pm

Post by l3v3rz
Wed Dec 23, 2009 9:42 pm

Day24 -1 day left!
=================

Christmas tomorrow!! This Advent series of post have come to and end - I hope you have found it an interesting series.

A compilation of the entire post is available as a single document.

[Edit: latest version can be found at :
http://robobuildervc.googlecode.com/files/PDFOnline.pdf ]

If you have bought (or receive) a new Robobuilder for Xmas I hope you will try out some of the demos. If you already have one and its gathering dust - then I hope you are inspired to get it out of its box - plug it in and have a play. The latest versions of the functions described over the last 24 days are available from my (open source) project site (http://code.google.com/p/robobuildervc/). I've also generated a single document with all the posts combined plus some additional material.

All the files are available from this zip file. http://robobuildervc.googlecode.com/files/Lisp.zip
Unzip the file in same directory as Lsharpconsole and then type (load "Lisp\\xxxx.lisp") to load and run. Here is a list of the files:

Code: Select all
final.lisp         -   (run_robobuilder)
          You will need a robobuilder and a serial connection for this to work

wckutils18.lisp         - (demo)
         This will do a punch left - assuming run_robobuilder first

utilities.lisp             -  (plotaccel 5) ; 5Hz 
         This as lots of utilities but it include the windows display routines :
         This redefine readAcc to use dummy ransom data
               - need to reset for actual use - see code.

joy.lisp                   -  (demojoy)
          This will wait for joystick input and display what was pressed

km.lisp                   -  (doctor)
          The  match function and example program doctor.

asdfg.lisp                -   auto runs
          This is my simple blockworld demo                

Data files:
test.csv              (punch left as a CSV file)
WALK.csv           (walk converted to CSV file)


You can get the latest Lsharp executable with RobobuilderLib.dll (and Lsharp.dll) here:
http://robobuildervc.googlecode.com/files/LSharp%20with%20RBLib.zip

A final function you might find helpful. You can add documentation to a function as an optional string when its defined. This can then be accessed by typing (help function) and it will return what information there is and what parameter or arguments it expects. Its pretty terse! here how to add to your own functions:

Code: Select all
(def test (x y) "This is test" ())

> (help test)
(x y) : This is test
LSharp.Function
>


Summary

So in summary, why would you use L# to program your robot? If you want more than an electronic automaton the robot needs "smarts". You either need to carry those around with you by using add on processing boards such as roboard, or you can use your PC remotely, offloading the complex stuff. The main advantage of the remote approach is it's cheap - if you already have a PC it cost nothing! Although you might want to buy the bluetooth module so you can be untethered.

How does L# compare with alternatives?

The good: Its very small (80K!!) , its open source, its free (I like this), it works, and it works well with .NET. Essentially its a very powerful scripting language. Plus its lisp features make it good for List processing (surprise!). Is it only for remote PCs ? LSharp.net is based on arc syntax and you can run arc on small linux embedded processors. Here's a link to ShevaPlug running Arc on Linux. Of course you loose .NET libraries - and would need to work out the Linux equivalents. http://arcfn.com/2009/08/worlds-smallest-arc-server.html

So are there problems with L#? Yes - there are a few :)
    It doesn't support Return from functions, which makes coding somethings harder.
    It also is missing exception handling - you can throw but not catch.
    I've also noticed that infinite loops seems to crash it which is annoying and needs to be handled.
    There is Very little documentation its syntax is not always clear.
    Its complete lack of trace and debug means getting stuff to work involves putting prn statements in every where

What are the alternatives LISP? I've not looked at these in any detail. DotLisp looks a lot more fully realised than LSharp. IronScheme uses the new DLR libraries from Microsoft and is huge. GnuCommonLisp is probably very good at Lisp - but not so easy to onterface to .NET libraries (I suspect). Here are links if you interested in finding out more:

DotLisp - http://dotlisp.sourceforge.net/dotlisp.htm
GnuCommonLisp http://www.cs.utexas.edu/~novak/gclwin.html
IronScheme http://www.codeplex.com/IronScheme

Which text editors to use is a vital question for Lisp programmers as you eed parentheses checking built in ! Here are the links to the text editors Ive looked at. My favorite is Notepad++ at the moment. Its Lisp syntax highlighting and regular expressions make it easy to use.

Here are download links:

Acknowledgements

    RobobuilderLib uses RBC serial protocol which Robosavvy forum members (I-bot and Raymond) identified.
    Blockworld and Doctor programs were inspired by reading LISP (by PH Winston and BKP Horn):
    http://people.csail.mit.edu/phw/Books/


It would be great to hear from you, if you have liked there series and if you have any questions I'll be happy to try and answer.

A Merry Christmas !!!

Image
Day24 -1 day left!
=================

Christmas tomorrow!! This Advent series of post have come to and end - I hope you have found it an interesting series.

A compilation of the entire post is available as a single document.

[Edit: latest version can be found at :
http://robobuildervc.googlecode.com/files/PDFOnline.pdf ]

If you have bought (or receive) a new Robobuilder for Xmas I hope you will try out some of the demos. If you already have one and its gathering dust - then I hope you are inspired to get it out of its box - plug it in and have a play. The latest versions of the functions described over the last 24 days are available from my (open source) project site (http://code.google.com/p/robobuildervc/). I've also generated a single document with all the posts combined plus some additional material.

All the files are available from this zip file. http://robobuildervc.googlecode.com/files/Lisp.zip
Unzip the file in same directory as Lsharpconsole and then type (load "Lisp\\xxxx.lisp") to load and run. Here is a list of the files:

Code: Select all
final.lisp         -   (run_robobuilder)
          You will need a robobuilder and a serial connection for this to work

wckutils18.lisp         - (demo)
         This will do a punch left - assuming run_robobuilder first

utilities.lisp             -  (plotaccel 5) ; 5Hz 
         This as lots of utilities but it include the windows display routines :
         This redefine readAcc to use dummy ransom data
               - need to reset for actual use - see code.

joy.lisp                   -  (demojoy)
          This will wait for joystick input and display what was pressed

km.lisp                   -  (doctor)
          The  match function and example program doctor.

asdfg.lisp                -   auto runs
          This is my simple blockworld demo                

Data files:
test.csv              (punch left as a CSV file)
WALK.csv           (walk converted to CSV file)


You can get the latest Lsharp executable with RobobuilderLib.dll (and Lsharp.dll) here:
http://robobuildervc.googlecode.com/files/LSharp%20with%20RBLib.zip

A final function you might find helpful. You can add documentation to a function as an optional string when its defined. This can then be accessed by typing (help function) and it will return what information there is and what parameter or arguments it expects. Its pretty terse! here how to add to your own functions:

Code: Select all
(def test (x y) "This is test" ())

> (help test)
(x y) : This is test
LSharp.Function
>


Summary

So in summary, why would you use L# to program your robot? If you want more than an electronic automaton the robot needs "smarts". You either need to carry those around with you by using add on processing boards such as roboard, or you can use your PC remotely, offloading the complex stuff. The main advantage of the remote approach is it's cheap - if you already have a PC it cost nothing! Although you might want to buy the bluetooth module so you can be untethered.

How does L# compare with alternatives?

The good: Its very small (80K!!) , its open source, its free (I like this), it works, and it works well with .NET. Essentially its a very powerful scripting language. Plus its lisp features make it good for List processing (surprise!). Is it only for remote PCs ? LSharp.net is based on arc syntax and you can run arc on small linux embedded processors. Here's a link to ShevaPlug running Arc on Linux. Of course you loose .NET libraries - and would need to work out the Linux equivalents. http://arcfn.com/2009/08/worlds-smallest-arc-server.html

So are there problems with L#? Yes - there are a few :)
    It doesn't support Return from functions, which makes coding somethings harder.
    It also is missing exception handling - you can throw but not catch.
    I've also noticed that infinite loops seems to crash it which is annoying and needs to be handled.
    There is Very little documentation its syntax is not always clear.
    Its complete lack of trace and debug means getting stuff to work involves putting prn statements in every where

What are the alternatives LISP? I've not looked at these in any detail. DotLisp looks a lot more fully realised than LSharp. IronScheme uses the new DLR libraries from Microsoft and is huge. GnuCommonLisp is probably very good at Lisp - but not so easy to onterface to .NET libraries (I suspect). Here are links if you interested in finding out more:

DotLisp - http://dotlisp.sourceforge.net/dotlisp.htm
GnuCommonLisp http://www.cs.utexas.edu/~novak/gclwin.html
IronScheme http://www.codeplex.com/IronScheme

Which text editors to use is a vital question for Lisp programmers as you eed parentheses checking built in ! Here are the links to the text editors Ive looked at. My favorite is Notepad++ at the moment. Its Lisp syntax highlighting and regular expressions make it easy to use.

Here are download links:

Acknowledgements

    RobobuilderLib uses RBC serial protocol which Robosavvy forum members (I-bot and Raymond) identified.
    Blockworld and Doctor programs were inspired by reading LISP (by PH Winston and BKP Horn):
    http://people.csail.mit.edu/phw/Books/


It would be great to hear from you, if you have liked there series and if you have any questions I'll be happy to try and answer.

A Merry Christmas !!!

Image
Last edited by l3v3rz on Sat Jul 03, 2010 11:16 pm, edited 2 times in total.
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Post by pepep » Wed Mar 31, 2010 7:15 pm

Post by pepep
Wed Mar 31, 2010 7:15 pm

GREAT JOB!!! I'm wonder about tour work, congratulations!!

I think that there is only one thing to add: is possible to control the leds (blue, red) in every servo?

Thanks again!
GREAT JOB!!! I'm wonder about tour work, congratulations!!

I think that there is only one thing to add: is possible to control the leds (blue, red) in every servo?

Thanks again!
pepep
Savvy Roboteer
Savvy Roboteer
Posts: 38
Joined: Sun Mar 28, 2010 7:14 pm

Post by l3v3rz » Wed Mar 31, 2010 8:14 pm

Post by l3v3rz
Wed Mar 31, 2010 8:14 pm

Hi,

Thanks for your comments.

In answer to your question, [b]yes [/b ]in theory. I only have a 5710K (rather than 5720K with the LEDs) so I've not tried this, but the RobobuilderLib.dll does support writing to LEDs using the method wckWriteIO. So assuming you've downloaded final.lisp from http://robobuildervc.googlecode.com/files/Lisp.zip. The code is (something like) this :

Code: Select all
(load "final.lisp")                        ; load PC remote routines
(load "wckutils18.lisp")                ; load wck mode routines

(def wckWriteIO (n c0 c1)
   "turn IO on/off"
   (if (not (bound 'wck)) (dcmodeOn))
   (.wckWriteIO wck n c0 c1)
)

(run_robobuilder)                       ; initialise connection to robot
(dcmodeOn)                              ; switch into DC mode
(wckWriteIO 10 false true)            ; set red LED on servo 10
(sleep 1)
(wckWriteIO 10 true false)            ; set blue LED on servo 10
((dcmodeOff)                              ; exit DC mode


I've been working on speech synthesis and recognition routines and a dynamic control routine for my gripper(s) so that it can close automatically to the size of the object being grasped. The lisp code is all up loaded on the web site.

cheers

[Edit - I've updated to cover missing function wckWrite - thanks pepep]
Hi,

Thanks for your comments.

In answer to your question, [b]yes [/b ]in theory. I only have a 5710K (rather than 5720K with the LEDs) so I've not tried this, but the RobobuilderLib.dll does support writing to LEDs using the method wckWriteIO. So assuming you've downloaded final.lisp from http://robobuildervc.googlecode.com/files/Lisp.zip. The code is (something like) this :

Code: Select all
(load "final.lisp")                        ; load PC remote routines
(load "wckutils18.lisp")                ; load wck mode routines

(def wckWriteIO (n c0 c1)
   "turn IO on/off"
   (if (not (bound 'wck)) (dcmodeOn))
   (.wckWriteIO wck n c0 c1)
)

(run_robobuilder)                       ; initialise connection to robot
(dcmodeOn)                              ; switch into DC mode
(wckWriteIO 10 false true)            ; set red LED on servo 10
(sleep 1)
(wckWriteIO 10 true false)            ; set blue LED on servo 10
((dcmodeOff)                              ; exit DC mode


I've been working on speech synthesis and recognition routines and a dynamic control routine for my gripper(s) so that it can close automatically to the size of the object being grasped. The lisp code is all up loaded on the web site.

cheers

[Edit - I've updated to cover missing function wckWrite - thanks pepep]
Last edited by l3v3rz on Thu Apr 01, 2010 4:59 pm, edited 1 time in total.
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

Post by pepep » Thu Apr 01, 2010 6:48 am

Post by pepep
Thu Apr 01, 2010 6:48 am

Ok, thanks, but the lisp function wckWriteIO don't exists in wckutils ...
I found it in the source of RobobuilderLib.dll, and add to simple functions in Lisp, perhaps do you want to add to your lisp library:
Code: Select all
(def setLeds (n blue red)
   "turn on/off servo leds"
   (.wckWriteIO wck n blue red)
)

(def setallLeds (n blue red)
   "turn on/off all servo leds"
   (for i 0 n
         (.wckWriteIO wck i blue red)
      )
)


but ... I think that wckWriteIO method have an error: there is no combination for turn on the red led only:
true false --> blue on, red off
true true --> both on
false true --> both on **** Error??
false false --> both off

Thanks again!
Ok, thanks, but the lisp function wckWriteIO don't exists in wckutils ...
I found it in the source of RobobuilderLib.dll, and add to simple functions in Lisp, perhaps do you want to add to your lisp library:
Code: Select all
(def setLeds (n blue red)
   "turn on/off servo leds"
   (.wckWriteIO wck n blue red)
)

(def setallLeds (n blue red)
   "turn on/off all servo leds"
   (for i 0 n
         (.wckWriteIO wck i blue red)
      )
)


but ... I think that wckWriteIO method have an error: there is no combination for turn on the red led only:
true false --> blue on, red off
true true --> both on
false true --> both on **** Error??
false false --> both off

Thanks again!
pepep
Savvy Roboteer
Savvy Roboteer
Posts: 38
Joined: Sun Mar 28, 2010 7:14 pm

Post by l3v3rz » Thu Apr 01, 2010 9:11 am

Post by l3v3rz
Thu Apr 01, 2010 9:11 am

Yes you're right - I should have added

Code: Select all
(def wckWriteIO (n c0 c1)
   "turn IO on/off"
   (if (not (bound 'wck)) (dcmodeOn))
   (.wckWriteIO wck n c0 c1)
)


The problem with the IO is a bug a RobobuilderLib/wckWriteIO - I've made a change and uploaded a new version - http://robobuildervc.googlecode.com/fil ... derLib.dll

Hope this works - unfortunately I have no way of testing as I don't have any of the colourful servos.

cheers
Yes you're right - I should have added

Code: Select all
(def wckWriteIO (n c0 c1)
   "turn IO on/off"
   (if (not (bound 'wck)) (dcmodeOn))
   (.wckWriteIO wck n c0 c1)
)


The problem with the IO is a bug a RobobuilderLib/wckWriteIO - I've made a change and uploaded a new version - http://robobuildervc.googlecode.com/fil ... derLib.dll

Hope this works - unfortunately I have no way of testing as I don't have any of the colourful servos.

cheers
Last edited by l3v3rz on Thu Apr 01, 2010 5:00 pm, edited 1 time in total.
l3v3rz
Savvy Roboteer
Savvy Roboteer
Posts: 473
Joined: Fri Jul 18, 2008 2:34 pm

PreviousNext
93 postsPage 2 of 71, 2, 3, 4, 5 ... 7
93 postsPage 2 of 71, 2, 3, 4, 5 ... 7