The LLAMA Tutorial

LLAMA is a Multi-tasking Control System composed of 6 modules, INTERPRT, EXDVR, SPKR, SYSMGR, DLEX, and RADIO, the first three running concurrently, that performs the duties of a high-level language, robot controller, sensor reader and general system controller. LLAMA is written in C for the OS-9 operating system. LLAMA interfaces through OS-9 to the various expert modules that make the robot a distributed computer system. Figure M-5 illustrates the system connectivity. A listing of each file and their corresponding block in the figure is given below:

                INTERPRT - System Interpreter, started by SYSMGR as “interprt -i,” or “interprt -w” and Spawned Interpreters, started as “interprt -s” by the System Interpreter.

                EXDVR - SDLC Expert Module Command Queue Controller and Sensor Processor. This module is an example of a simple non-preemptive, task-sharing multi-tasking system.

                SPKR - Speech and Status Computer.

                SYSMGR - System Manager, starts system. Use “sysmgr -i” for interactive mode or “sysmgr” for work, or no-echo, mode.

                DLEX - not shown, downloads software to expert modules. Must be executed every time robot is powered-up.

                RADIO - not shown, initializes TNC at system bootup. Synchronizes G2 to LLAMA but is not needed for general radio communication.

How LLAMA Initializes Itself

           When the system manager starts up the interpreter, it selects the system interpreter. This interpreter initializes a linked list, called the dictionary, of all available primitives. It then performs a two pass execution of LLAMA commands.

           Typically, LLAMA constants and variables are initialized in the first pass and inserted into the dictionary and any sign-on messages can be displayed. Next, macros are compiled into the dictionary, possibly incorporating the previously defined variables and constants. The second execution pass may clear the emergency stop, or perform any needed housekeeping. This is where an “autonomous robot” macro would be executed, however the interpretive environment would not be entered. Assuming the initialization was performed properly, LLAMA will be ready to accept commands.

The Interactive Environment

           LLAMA is a dual-purpose operating environment. It allows both the definition of macros and executions of functions in the same environment. This follows the principle of “Iterative Development” which says that breaking and testing your problem into smaller components can speed the completion of your project. LLAMA is well suited to this approach since a typical macro compiles to Threaded Code in less than a second and the macro is immediately available for execution (and testing).

           The interactive environment allows the user to test ideas as she types them. For example, say that a macro that moves the robot in a square three feet on a side has just been defined and is to be executed. The problem is that the robot is next to a wall and there is a good chance that the robot would hit the wall if the square is executed. To prevent this, the operator would enter the appropriate command to move the robot away from the wall and then execute the square program.

           LLAMA recognizes only two kinds of inputs, commonly referred to as tokens: numbers and instructions. Every token must be separated by white-space, such as a space, tab, newline, form feed, etc. LLAMA departs from the FORTH approach of returning an “ok” after successful execution. LLAMA only gives a report if a message is requested or if an error occurred.

           Defining a macro in LLAMA is the same as in FORTH: a colon is used to start the definition, a name is given to the macro and the body of the definition follows. New-lines can be performed during the defining process with no ill effects. Definition ceases when a semicolon is encountered. Assuming there are no errors, the new macro definition is added to the dictionary.

 

 

Figure M-5. LLAMA system interconnection diagram.

 

The Stack

           The stack is fundamental to the successful operation of LLAMA. LLAMA is loosely based on FORTH and both operate using RPN (Reverse Polish Notation) or post-fix stack operations, in effect, an advanced Hewlett Packard calculator. The stack size is about 256 numbers, where the numbers are 32 bit signed integers.

           IMPORTANT: Most LLAMA functions place and remove numbers from the stack. The users manual notes which functions affect the stack. The FORTH convention for determining what a function removes and puts onto the stack is given as an example:

/                       ( n1 n2 — n )

Divide takes two numbers, n1 and n2, from the stack and returns one number, the quotient. Top of stack on entry is the right-most number to the left of the dash, in this case n2. Top of stack on exit is similar, also the right most entry on the right side of the dash, although only one number is returned in this example.

FORTH likes a clean stack following execution of functions. What this means is that some macros or primitives may leave residual values on the stack. These cases should be handled with functions like “drop.”

Variables, Constants and Conditionals

           A non-trivial LLAMA application typically uses conditionals to control program flow. Numeric constants are used to establish system defaults while variables allow altering macro behavior. All constants and variables are global, which means that all spawned interpreters have access to them.

Defining a constant:

10 constant minutes

In this example, the constant minutes is given the value 10. If minutes is used in a macro or on the command line, it places its value on the stack, thus:

minutes minutes *

would leave 100 on the stack. Variables are slightly different. They are defined the same way but they are accessed differently.

Defining a variable:

200 variable default_speed

The variable in this example is given the initial value 200. To place the variable’s value on the stack, two operations must take place:

default_speed @

The variable first places it’s “address” on the stack, this is because LLAMA and FORTH are written to interact with low-level functions to increase efficiency. The “@” symbol “fetches” the contents of the address on the stack and places the contents on the stack. Thus the above operation leaves 200 on the stack. To replace 200 with 100, the following is done:

100 default_speed !

The exclamation mark takes the variable’s address and stores the next stack item at that address.

Conditionals are used to control the flow of program execution. The key point here is that conditionals are part of macros (programs) and cannot be used in the immediate execution environment. This makes sense since the interactive environment should only be used to test complete ideas, otherwise the user would be trying to mix testing and development into one!

Multi-tasking Basics

           Standard FORTH definitions do not support multi-tasking and so LLAMA’s multi-tasking functions are unique to itself. LLAMA allows the concurrent execution of up to 32 processes although more than four or five will slow the system appreciably (in these cases, close attention must be paid to the setting of process priorities to restore system efficiency). These concurrent processes are simply macros executing in the background. Note: if a process that either sleeps or loops for a significant period of time is executed in the system interpreter it will prevent user input until done, in fact, any input will be buffered and executed once the task is complete.

           The key is that the system interpreter should be used for development and preferably not for execution of complex tasks (unless they are well debugged and/or no operator intervention is desired); these processes should be executed in the background. An additional advantage is that these processes can have switches (global variables) that are activated by the user in the system interpreter. Thus controlled execution of background tasks is possible; an example will be given later.

           One good rule of thumb is not to print messages or data from background processes from a continuous loop more than once every five seconds since it will clog the screen and make interaction difficult (but not impossible).

           Let’s say we wish to execute the macro “go_wall” as a background task. We would type the following:

{ go_wall }

This will either start a spawned (concurrent) interpreter or use an idle spawned interpreter to execute the macro. The brackets may enclose complex expressions and conditionals, in fact, the spawned interpreter automatically compiles the tokens, gives the resulting macro a name of its choosing, adds it to the dictionary and calls the Parser to evaluate and execute it.

Printing to the Screen

           Since LLAMA operates in a multi-tasking environment, the traditional FORTH method to display numbers and text must be modified and here’s why: Typically, outputs are defined to be sent to the screen as bursts of characters, with processing performed in the interim, thus the output:

motor temperature: 300 degrees

would consist of an output for the header, “motor temperature,” a conversion of a number on the stack to the text representation and then the closing information, “degrees.” The problem is that if two processes are printing information in this manner, their messages may blur together as such:

track motor temperature: 30 feet 300 left degrees

which certainly would confuse the observer. We would rather see:

track 30 feet left

motor temperature: 300 degrees

To prevent this, a line of output must be sent to the screen in one write. A marker is set up in LLAMA using the “make” primitive that initializes text output. Any text following is buffered and sent only when the “out” primitive is given. So in effect, if we wish to “make - out” we would like to do it without interruptions. The example given below shows how to display the motor temperature. We will define a macro called “mottemp” and assume that the temperature exists on the stack when we call the macro.

: mottemp

      make

         ." motor temperature: "

         space

         D.R

         space

         ." degrees "

         cr

      out

;

The macro “space” inserts a blank between the numbers and the macro “cr” generates a newline. “D.R” generates the text equivalent for the number on the stack with no spaces before or after, this gives the user maximum flexibility in defining outputs.

Executing “mottemp” will do the following:

40 mottemp<cr>

motor temperature: 40 degrees

           (enter next text here)

Performance Orders

The major elements of creating macros within LLAMA have been covered in previous sections. The object now is to apply a format that agrees with constructs, called performance orders, developed under G2 for LLAMA. G2 creates complex structures that are composed of a sequence of primitives and macros. These define the desired action to be executed by the robot. Since we do not have a high speed interface between the robot and G2, we must create an artificial construct that reduces the dependence on a high-speed link. This construct is essentially a macro designed to place more processing burden on the robot. The macro would typically contain conditionals for on-board decision-making. There is really no logical difference between what is desired here and what a Mars rover would do; the difference is only in the communication delay. An example might be to execute a circle when an object (a person or other robot) comes within two feet of the robot. Another might be to start a lecture at 9:30.

Performance orders can be defined explicitly as macros, numbers, and primitives that are able to be executed under prescribed conditions as a spawned process in LLAMA. Performance orders are composed of a performance or performances and execution in time/state information. A performance is composed of skills and conditionals. Conditionals are primitives that regulate program flow. Skills are made up of numbers, macros and primitives.

By spawning a process that contains a performance order, the system is freed to accept another performance order while executing the first. The advantage can be realized in the following example.

Suppose that the robot has just been instructed to execute a complex performance order that calls for it to circumnavigate a room and report if it “sees” a doorway. It will use the performance order macro to tell it how to avoid obstacles, etc. Next, suppose that G2 has concluded that the robot will or will not find a doorway and if it finds a doorway then the robot is to execute a performance order that will take it through the door, otherwise the task will be to move to the center of the room. Although the three performance orders could be made into one, together they may be too long to send in a reasonable amount of time. Only the first is downloaded and executed.

Now that the requirements for the performance orders have been established it is important to identify what is called a generic performance in relation to a performance order. An example of the former may be a door traversal routine with various parameters, such as “door size.” A generic performance differs from a performance in that a generic performance may be general enough and useful enough to be available as a macro on the robot. To make a generic performance into a macro, the performance must be preceded by a colon, given a name which will be used later to call it from within a performance order, expressed as a sequence of tokens and finally terminated with a semi-colon. This would be sent to the robot and prior to being called upon by a performance order. Executing a generic performance is accomplished by placing it within a performance order, whereas defining a generic performance is accomplished by sending the generic performance within a macro definition structure.

As the robot is searching for a doorway, which is executing as a background task, the system interpreter is available for use. G2 downloads the two remaining performance orders. This is very important, the system interpreter begins to assemble the incoming performance orders while a spawned interpreter is executing a different performance order in the background. This is an example of multi-tasking used effectively.

The beginning of these performance orders will look as follows:

{ 0 txrel pause <performance> }

Where <performance> is a sequence of tokens defining the performance desired, such as traversing a doorway. The “txrel” is a standard initialization which tells the spawned interpreter to execute the performance immediately. Its use will be explained in the next sections. The command “pause” tells the spawned interpreter to halt execution until it gets a signal. This suspends the process and removes it from OS-9’s active process list, which means that it will not be using processor time. This is desirable since we don’t want an idle process “stealing” processor time from the room exploration performance order.

When the performance order has been downloaded, a message returns a handle with a unique number to that process, such as:

handle 09

This allows G2 to identify the sleeping process within LLAMA. It will get one message for each of the two performance orders. This message is sent whenever a process is spawned. Whenever the performance order completes, G2 receives a message like:

nohandle 08

Using these messages, G2 is able to keep track of the currently executing (or sleeping) processes within LLAMA.

Assuming that the first performance order has completed and returned a message indicating it has found a door, G2 can now execute the appropriate performance order. Once again, assuming that the door traversal performance order has a handle of “9” and the center-finding performance order has a handle of “10,” G2 will send the following:

9 wake 10 exit

This will restart the door traversal performance order and delete the center-finding performance order. The LLAMA system gives G2 power and flexibility in how it interfaces with the robot and should be used to its fullest advantage. This concludes the three-performance order example.

 

Typically the two conditions that Performance Orders execute under are time and state. To execute in time, a “txrel” or “txabs” primitive is used to suspend the desired commands until a certain time has been reached. To execute in state, conditionals would be used.

Let’s take a look at these two structures in detail. Each structure is examined and a real example is given.

Execution in Time

Time can be used as a means to conditionally control the execution of primitives and macros on the robot. This can be useful if, say, a sunspot cycle is going to disrupt radio communication from 11:50 am to 12:10 pm and we wish to demonstrate the capabilities of the robot to some senior members of an engineering society at noon.

The robot has a demonstration program called “demo” which we wish to execute. This program demonstrates the features of the robot and it is covered in more detail in the next section and in the Appendix. We will use this demo.

Two scheduling commands would be useful for this example. They are “txrel” and “txabs.” We find out that “txrel” is a time execute relative command and would not serve our purposes. However, “txabs” will execute at a given absolute time.

Assuming the sunspots are not causing any problems currently, lets set the robot’s internal clock to the current time of day and ignore the one or two second delay for data transmission of the time set function. Assume the time is 10:08 am, Monday November 4, 1991. We would send the command:

1991 11 4 10 8 0 6 settime

The robot’s clock will now be synchronized to G2’s clock.

To execute “demo” at noon today, we would construct the following performance order:

{ 1991 11 4 12 0 0 6 txabs demo }

What this will do is execute the spawn definition right now, but once the command txabs is received, that process will suspend execution and wait until 12:00 pm today. Once noon rolls around, the next command in the string will execute, in this case “demo.” Once demo is complete, our performance order is complete. Note that we could have sent a performance order right after the previous with the intent of having the robot say “Hello, my name is Miss Marple” every minute for the next ten minutes

{ 10 0 do 0 2 tm 1 0 2 txrel loop }

and would not interfere with our noon appointment, in fact, the first performance order would execute at noon, as desired even though it was entered and sent first.

Hopefully our visitors were interested and amused.

Execution in State

Executing a performance order at a certain time has many uses, but it might be desirable to execute a performance when a certain condition occurs. Let us say that our visitors, described in the previous section, were troubled by the fact that the robot started executing a little after the noon hour even though their watches said noon. The wait was somewhat annoying to them. Let us also say that our visitors wish to interact with the robot: we have just introduced the robot to some school children and we wish the robot to execute the demo program when one of them approaches the robot.

To do this we must decide what condition will bring about the execution of the demo program. We can define our needs as follows:

Start “demo” when someone approaches to within two feet of the robot.

Let’s create a macro that will place a “one” on the stack once an ultrasonic sensor reads less than 20, or two feet. We will assume that there is no error condition and that all 24 sonars are enabled.

: near

      0 ult !

      ulsave

      if

         24 0 do

            20 <

            if

               1 ult !

            endif

         loop

         ult @

         if

            1

         else

            0

         endif

      else

         1

      endif

;

Our performance order should start the ultrasonics and stop them when done. Lets write the order:

{ ulon begin near until uloff demo }

The spawn definition will execute and loop within the begin - until conditional until “near” places a one on the stack. Notice that “near” must place a zero on the stack otherwise, or to phrase it another way, “near” must leave one and only one number on the stack under any conditions. Once the condition is met (object < 2 feet) the conditional is exited, the ultrasonics turned off, and demo is executed.

The “DEMO” Macro, A Detailed Explanation

This explanation covers a particular sub-demo of the main demo. The sub demo will read the bumper switches and announce verbally the number of the pressed switch. Remember to enable message speech using 20 tc.

FORTH code tends to be simple yet potentially hard to write well because many operations use the stack implicitly and must be broken down into simpler functions to work well. This can be helped if the desired code is written down before it is entered for execution. It is desirable to show how the stack is behaving at every command to help verify proper operation of the macro. This is what is done in this example.

FORTH is considered to be a very efficient high level language due to its threaded nature. What slows down execution of FORTH commands is how well the threading routines are written for a particular application. These routines manage the jumps into and out of nested macros. This can slow down certain kinds of constructs. This implementation of FORTH does not rate well in this regard due to the constraints of the multiprocessing environment and since it is written in C; therefore, when writing LLAMA code, do not nest the macros too deeply if efficiency is required.

The stack graphic format is as follows: Top of Stack is given as the left-most item and succeeding items are shown towards the right. The parentheses contain the stack for display purposes. For example, lets place 4 and then 23 on the stack and add the numbers together:

4     ( 4 )

23    ( 23 4 )

+     ( 27 )

The following example is taken from “DEMO-7,” the bumper switch reading demonstration. The macro as given in the Appendix is given here for reference:

: demo7

      16 2 tm           ("Press a bumper switch")

      3 sl

      17 2 tm           ("Press switch 4 to stop")

      0 ult !           (put zero into "ult")

      bpr bpme          (reset bumpers and disable emergency stop feature - use bumpers in tactile mode)

      begin          (loop until bumper 4 is pressed)

         bpgs           (put number of bumpers pressed on stack)

         dup            (duplicate "quantity" onto stack)

         if          (do if no zero, or a switch pressed)

            0 do        (loop number of switches pressed)

               dup      (check for switch 4 pressed)

               4 =

               if    (if 4 pressed)

                  1 ult !  (store 1 into variable)

               endif

               4 tm     (say switch number out loud)

            loop

            64 sl2      (delay for switch bounce)

         else

            drop        (clean up stack)

         endif

         ult @          (get "ult" value computed)

      until          (exit if "ult" is one)

      bpr               (restore emergency stop feature)

      3 sl

;

When writing a non-trivial macro, it is a good idea to write it out on a piece of paper or on a white/blackboard with stack contents displayed to clarify the function’s behavior. Variables should generally be avoided since they tend to be slower than stack manipulations, however, they can be used to simplify functions with complex stack operations. Variables are used in this example.

0 ult !

Since ult has been defined as a variable, the following sequence takes place:

           zero is placed on the stack

0     ( 0 )

           ult places its memory address on the stack

ult   ( 12004 0 )

           !, or “store,” gets the memory address and stores zero into it.

!     ( - )

The following resets the bumpers into a known state and then disables the emergency stop mode of the bumpers. It should be pointed out that in emergency stop mode, a pressed switch is latched whereas in the tactile mode it is a momentary action, which implies that a loop check must be fast enough to catch the switch closure. This can be a problem in some applications.

bpr   ( - )

bpme  ( - )

Start the infinite loop here. A begin-until combination is needed to identify the conditional statement. We will loop until the number four bumper switch is pressed. There should be no values on the stack at this point.

begin ( - )

We use the following function to put the current switch closures onto the stack. If no switches are pressed, a zero is put on the stack, otherwise the number of switches pressed and the switch numbers are placed on the stack. The quantity of switches pressed is an important value to have on the stack, it is always on the top-of-stack, because it allows the code to extract the exact number of items placed onto the stack. It simplifies the writing of FORTH code. So, if bumper switch #5 is pressed, the following appears on the stack:

bpgs  ( 1 5 )

We need to see if a switch was pressed for our “if” statement. Since we may need the count=1 later lets duplicate it.

dup   ( 1 1 5 )

The body of an if statement will execute if the top-of-stack is non-zero, which in this case it is. The top-of-stack is discarded.

if    ( 1 5 )

We now want to extract the switch presses from the stack. The top-of-stack tells us the number if switches pressed, so implement a loop simply by putting a zero on the stack and doing a do loop. The do loop will remove the top two numbers and use them as loop indices (we will do this loop only once).

0     ( 0 1 5 )

do    ( 5 )

dup   ( 5 5 )

4     ( 4 5 5 )

Now check if the top two stack items are equal. If they are, put a “1” on the stack or if they are not equal, put a “0” on the stack. Since 4 and 5 are not equal, put a zero on the stack.

=     ( 0 5 )

if    ( 5 )

Since top-of-stack was zero, we skip to the endif:

endif ( 5 )

and speak the number that represents the bumper switch press number (the 4 is a code for speaking the numbers from 0 - 31).

4     ( 4 5 )

tm    ( - )

We loop until we are finished speaking all bumper switches.

loop  ( - )

A delay of one-quarter second has been added to slow down the reading time. The switches would be read too fast otherwise and multiple numbers would be spoken.

64    ( 64 )

sl2   ( - )

If no switches were pressed, we have to remove the extra zero that appears on the stack. Note, this was not the case at the start of this example.

else  ( 0 )

drop  ( - )

endif ( - )

Now check if the variable “ult” was set due to the switch four being pressed.

ult   ( 12004 )

@     ( 0 )

until ( - )

Restore bumper switch to the emergency stop mode.

bpr   ( - )

Sleep for three seconds to give a delay between demos.

3     ( 3 )

sl    ( - )

End of macro.

The “SAFETY” Macro

           LLAMA contains a macro definition that utilizes the robot’s 24 ultrasonic sensors to stop the robot if any sensor return is less than a certain amount. Although not a perfect algorithm, it works and is a good illustration of multi-tasking, printing, and things to avoid. In the traditional sense, an algorithm is a complex series of actions in one module. Here the term, “algorithm,” refers to a complex combination of macros and primitives which may lack visual connectivity.

           Visual connectivity implies that the components of the algorithm are presented as a whole (and they will be here), but in LLAMA some of the macros may be shared be other algorithms, thus when describing other algorithms, multiple references may be given. Reusing macros saves memory and is a feature of LLAMA and FORTH. Although this reusability is similar to function calls in C, LLAMA and FORTH do not need the descriptive overhead and definitions inherent in C; this is at the expense of potentially confusing stack operations.

Initialize the variables:

0 variable count

0 variable distance

20 variable set

0 variable off

256 variable waittime

Define macros:

: safety                (executes only once)

      distance !        (get safe distance as arg)

      ulsave            (get 24 readings to stack, assume ulon)

      0<

      if             (check if error occurred)

         drop           (delete count)

         3 0 tm         (say "alarm")

         s           (stop motors)

      else              (no error)

         if          (if count not equal to zero)

            23 count !  (assume 24 readings)

            24 0        (get 24 readings in do loop)

            do

               distance @  (get safe distance)

               <     (less than ultra reading?)

               if       (do if yes)

                  3 0 tm   (say "alarm")

                  s     (stop motor)

                  clrs     (remove rest of readings)

                  leave (exit loop)

               endif

               -1 count +! (decrement counter)

            loop

         else              (done if no readings or ultras off)

            3 0 tm         (say "alarm")

            s           (stop motors)

         endif

      endif

;

: stopit                   (must be background task!)

      ulon                 (start ultrasonics)

      begin

         set @ safety         (get set distance, do SAFETY)

         waittime @ sl2    (wait 1 second)

         off @          (continue ?)

      until

      uloff

;

Execute:

{ stopit }

The macro “stopit” will execute as a background task while drives and steers are being executed by other processes. If it encounters a near condition, it will send an “s” (all stop) to the motors and sound an alarm. The variable “count” contains the ultrasonic number that triggered the stop. It will wait for 1 second and check again. This is useful if a complex trajectory is sending a sequence of timed drives and steers that are constantly attempting to approach the nearby object—they will never do so. However, this algorithm has at least two drawbacks, first, once stopped, the robot is probably still near to the object, so a message to that effect will appear every second, which could be an annoyance. Second: a stop command is being issued which hampers the ability to move away from the object. Correction: Stop all other motion requests in progress and reduce the set distance. Alternatively, significantly increase the delay time between checks.

The “SQUARE” Macro

           Robot motion should be smooth and stops and starts should be minimized if time is a factor. This routine illustrates how the robot may execute a square trajectory which happens to have rounded corners. The robot returns to its original position and orientation once the maneuver is complete. Since the robot’s positioning is never perfect upon the completion of a turn, it may be desirable to correct its orientation from internal sensors in a closed-loop fashion. The virtual line-arc, or “until,” functions have built-in correction that is usually more accurate than Denning’s vector command “DSV.”

These are the specifications of our square:

           drive at 0.3 feet per second during turns.

           drive at 0.4 feet per second in straightaways.

           drive in straightaways for 4 seconds.

           steer 90 degrees at 30 degrees per second.

           drive acceleration is about 10 feet/sec/sec.

           steer acceleration is about 100 degrees/sec/sec.

           steer correction rate is 20 degrees per second.

Time to complete trajectory (approximate):

           4 * ((90 / 30) + 4 ) = 28 seconds

Distance travelled:

           4 * ((90 / 30) * 0.3 + 4 * 0.4) = 10 feet

Angle rotated through:

(this one is important, the robot after executing four 90 degree turns typically will be off by 10 degrees, if no correction is used, from its desired angle of rotation of 360 degrees due to the inaccuracies of the motor controller and the latency of the software)

           360 degrees

Define variables:

200 variable CRCTANG    (correction overshoot angle)

Define Open Loop SQUARE:

: sq                 (SQUARE)

      runtil            (reset "until" motion)

      4 0               (do 90 degrees/ straight 4 times)

      do

         3 900 300 STUA (drspd=.3, tang=90, tspd=30)

         16 4 drud      (drspd=.4)

      loop

      drs               (needed, since drud does not stop)

;

Define Closed Loop SQUARE:

: sqc                (SQUARE with correction)

      runtil            (reset "until" motion)

      4 0               (do 90 degrees/ straight 4 times)

      do

         3 900 300 STUAC   (drspd=.3, tang=90, tspd=30)

         16 4 drud      (drspd=.4)

      loop

      drs               (needed, since drud does not stop)

;

Execute:

           1. First home the robot and note the angle:

home

           2. Execute Square (Open or Closed Loop)

{ sq } or { sqc }

           3. Set position to zero:

0 0 0 spos

           3. Home Robot:

home

           5. Get angle error and print:

xyton GANG . xytoff

Results:

           OPEN LOOP:

           Angle turned through: 376.5 degrees

           Error: +16.5 degrees

           CLOSED LOOP:

           Angle turned through: 362.0 degrees

           Error: +2.0 degrees

It is evident that closed loop control can be very beneficial in increasing the accuracy of a trajectory. The results seem to show this, however this is only a small subset of the type of trajectories encountered and more testing needs to be done.