.ND
.FS
This work was partially supported by a Grant from the CNRS
project ARA (Automatique et Robotique Avanc\o"e\'"e), France, and
by the Ransburg Chair of Robotics.
This material is also based on work supported by the National
Science Foundation under the Grant No. MEA-8119884.
Any opinions, findings, conclusions, or recommendations
expressed in this publication are those of the authors
and do not necessarily reflect the views of the National
Science Foundation.
Facilities to perform this research are provided by the Purdue University
CIDMAC
project.
.FE
.ce 1
Robot Real Time Control User's Manual
.sp 7
.ce 1
Vincent Hayward
.sp 5
.ce 3
School of Electrical Engineering
Purdue University
West Lafayette, Indiana, 47907
.sp 5
.ce 1
TR-EE 83-42
.sp 5
.ce 1
October 1983
.bp
.EQ
delim $$
.EN
.NH
Overview
.PP
This manual describes how to implement real time programs
to control the robots situated in room B20 at Potter Building.
The Stanford and the Puma 600 arms can be equally used.
The system consist of :
.IP *
an executable module, called the superviser, which is to be
loaded down to the robot controller's lsi11 processor;
.IP *
a UNIX library containing several entry points:
.RS
.IP -
a function
.B control()
which permits to open the robot channel
and makes the vax sensitive to controller's interrupts;
.IP -
a function
.B release()
which shuts off the arm and closes the channel;
.IP -
a function
.B adcopen()
for specifying analog to digital conversions.
.IP -
a "C" structure
.B how
describing the robot state
updated at sample time intervals;
.IP -
a "C" structure
.B chg
describing the different commands that
are likely to be sent to the arm controller;
.IP -
a flag
.B terminate
to indicate when the real-time interaction
should terminate;
.IP -
a pointer to a string
.B mess
that will be printed at the termination
of the real-time interaction;
.IP -
a function
.B stepmode()
that permits to manually move each joint
from the terminal;
.IP -
mapping functions:
.B
enctoang(), rngtoang(),
.R
etc... (see appendix).
.RE
.IP *
an include file
.B rtc.h
describing the structures
.B how
and
.B chg
;
.NH
Real Time Considerations
.PP
Real time programming offers several peculiar aspects which
have to be mentioned here.
A real time program consists of two parts.
The main function, executed in time sharing, allows
the program to communicate with the rest of the system.
It can contain system calls, as disk or terminal
input-output, etc ...
The interrupt routines, activated upon controller interrupt,
are executed in kernel mode for efficiency.
Their execution time must be limited and therefore, they
.B cannot
perform
.B any
type of system call (read(2), write, fork(2),
signal(2), exec(2), brk(2) and so on).
.PP
Any machine error as floating point exception or bad memory reference
occurring during the execution of
the interrupt functions will produce a system crash.
The program cannot grow it's allocated region therefore any
calls like fork(2), open(2), malloc(2) must not be invoked when
the channel is open.
However, this type of calls can be performed before opening
the channel ,which is a minor burden.
If you need to use dynamic memory allocation, the package provides
alternative entry points (see Appendix) to perform similar functions
(alloc/free) but within a pre-allocated pool of memory.
Since the capacity is limited, caution must be taken
to insure that the allocations are successful.
.PP
The interrupt functions are called from the robot interface
package without any argument and are executed at very high priority.
Therefore, they can only communicated with the rest of the program
via global variables, which makes the programming more difficult.
A systematic use of structured data types and of the 'lint' program
is strongly recommanded.
.PP
The package makes use of SIGINT (signal(2)) to terminate the
real time interaction and to shut off the arm. The real time
driver makes use of SIGHUP (signal(2)) to signal a hardware
error to the package. Therefore, SIGINT and SIGHUP must
.B not
be caught by the user.
Interaction can be terminated either by setting the global
variable
.B terminate
to 'YES' from the main program or from the interrupt
functions.
The same effect is achieved by typing a 'break' or often a 'rub' on the
terminal (see stty(2)).
The main function must remain active throughout the execution
of the real time program.
You will have to code something like :
.DS L

        while(!terminate) {
                /* do time sharing */
        }
        release("finished");
.DE
.PP
The termination will be properly produced either by a 'break'
at the terminal or by setting
.B terminate
to YES anywhere
in the program.
.NH
The 'How' Structure
.PP
The
.B how
structure describes the current state of the arm and
is updated at interrupt time.
.DS L
struct how {
        unsigned short
                exio,                   /* state of the extern io register */
                pos[NJOINTS];           /* current positions */
        short
                adcr[ADC];              /* adc readings */
};
.DE
.PP
The content of the external i/o register reflects the state
of the switches on the front panel.
Normal usage of the interface does not require to pay any attention to
the register's content.
Positions are specified in
.B encoder
values.
See appendix for mapping the encoders values
onto the trigonometric circle, or on a physical linear scale.
The
.B how
structure can be read either synchronously
by the interrupt code or asynchronously by the main program.
The first 'NJOINTS' elements of the 'adcr' array are by default
the adc readings reflecting the motor currents of the manipulator's joints.
The 'adcr' values are in adc readings and have to be scaled by mean
of some calibration program.
.NH
The 'Chg' Structure
.PP
The
.B chg
structure describes the commands that the user wants
to be executed by the robot controller.
It is made of sub-structures which correspond to the four following
cases:
.IP -
A command is to be sent individually to a particular joint processor.
Their name is of the form 'i_name'. For each concerned command,
the structure 'chg' contains an array of these structures, one per
joint.
Each of the 'i_name' structure contains one value field.

.IP -
The same command is to be send to all the joint processors.
Their name is of the form 'a_name'.
For each concerned command, the structure 'chg' contains an instance
of a 'a_name' structure, but the value field is an array.
Using one 'a_command' is equivalent to use 'NJOINTS' 'i_commands', but
is more concise, and more efficient as far as the transmission time
and the command execution time are concerned.

.IP -
The same command is to be sent to all the joint processors, but
the same value is to be set in each joint processor.
They are named 'g_name' commands.
A 'g_command' contains one value field.

.IP -
The last case is a command which does not need any transmitted value.
.PP
The following shows the structure
.B chg
and the various combinations
of commands:
.DS L
struct i {
        char set;               /* flag to be set if i_command requested */
        unsigned short vali;    /* corresponding value */
};

struct a {
        char set ;              /* flag to be set if a_command requested */
        unsigned short vala[NJOINTS];   /* corresponding values */
};

struct g {
        char set;               /* flag to be set if g_command requested */
        unsigned short valg;    /* corresponding value */
};


struct chg {
        struct i i_motion[NJOINTS];      /* case POS - CUR - STOP */
        struct a a_motion;

        struct g g_hand;                /* command hand */
        struct g g_rate;                /* rate in 2 ** value * 7 ms */

        char end;                       /* termination */
        char stop;                      /* stop on the spot */
};
.DE
.PP
The actual chg structure in the rtc.h file may exhibit other instances
of 'a', 'i', or 'g' structures.
However, the following briefly describes the commands you are concerned with.
.IP "motion : " 11
Specify a position servo if the flag is raised with 'POS' value.
Specify a motor current if the flag is raised with 'CUR' value.
.FS
The values of POS, CUR, STOP, STOPCAL, NJOINTS, ADC, YES, NO,
are '#defined' and must not be redefined.
.FE
.IP "rate : "   11
Modify the sampling rate, or in other terms, the interrupt interval
time, it is expressed in powers of two times 7 milli-second.
.IP  "stop : "  11
Let the joint processor servo on the last position value.
.IP "hand : "   11
Control the hand.
Meaning dependent on particular experiments.
.NH
Sequence of operations
.NH 2
Opening the Channel
.PP
The package provides the
.B control
function to open the robot's
device and initialize the communication. This function takes
two arguments which are pointers to the functions that you
want to be executed on interrupt.
The purpose of these two functions is explained in the next
paragraph.
Before you tell the controller to start interrupting the vax,
you must make sure that the device is opened and your program
is waiting for interrupts.
Once the channel is opened, the user must not catch the
SIGINT and SIGHUP signals (see signal(2)).
The minimum code will look like:
.DS L
#include "rtc.h"                /* include communication declarations */
                                /* global variables */
 ...

main()
{
        int fn1(), fn2();       /* declare real time functions */

        control(fn1, fn2);      /* open channel */
        while (!terminate)      /* wait for the end */
                nap(10);
        release("all done");
}

fn1()
{
}

fn2()
{
        extern struct how how;
        extern struct chg chg;

        ...                     /* read arm state in how */
        ...                     /* compute next point, put commands in chg */
}

.DE
.PP
While the program is 'pausing' or 'napping' you will start the
superviser program by depressing the 'ARM POWER' push button and
the interaction will take place.
A 'break' at the terminal will cause the
.B release
function to be automatically called and
the message "** Interrupted" printed on the terminal.
However, it can be a good idea to systematically terminate your
programs to a call to
.B release.
Failing to issue this call by one mean or another will leave the
device opened and therefore unable to be re-opened.
So, make sure that 'exit's in your programs are preceded by
a call to
.B release
as shown in paragraph 2.
The function
.B release
expects a character
string argument as message to be printed when the channel is
explicitly closed.
.PP
Note that a valid organization can be:
.DS L
#include "rtc.h"                /* include communication declarations */

 ...                            /* global variables */

main()
{
        int fn1(), fn2();       /* declare real time functions */

        control(fn1, fn2);      /* open channel */
        pause();                /* wait for the program to be broken */
}

fn1()
{
}

fn2()
{
        extern struct how how;
        extern struct chg chg;

        ...                     /* read arm state in how */
        ...                     /* compute next point, put commands in chg */
}

.DE
.PP
If analog to digital conversions are needed, the
.B adcopen
function allows
to select the physical adc channels.
The function
.B adcopen
must be called from the time sharing code after
a call to
.B control.
This function transmits to the lsi a request as to send back to your
program the readings of the selected adc channel.
The function
.B adcopen
returns an integer that is the index in
the array 'adcr' of the corresponding channel.
The first 6 channels are used to measure the motor currents,
channels 0 through 5 are
therefore opened by default and cannot be reopened.
Motor currents can be
read in positions 0 to 5 of the array 'adcr'.
The function
.B adcopen
returns -1 if the
channel is  already opened, or the argument invalid, or
.B control
is not
active.  Up to 10 channels can be used (6 through 15).
.PP
Example :
A sensor is wired to channel 7, in order to use it the code may look like:
.DS L
#define SENSORCHANNEL   7

int sensor = -1;

main()
{
        blabla;
        ...
        control(f1, f2);
        sensor = adcopen(SENSORCHANNEL);
        while (!terminate) {
                nap(10);
        }
        release("happy");
}

f1()
{
}

f2()
{
        if (sensor < 0) {       /* not ready yet */
                return;
        }
        pos = how.adcr[sensor] * factor;
        nextpoint = f(pos);
}
.DE
.NH 2
What Happens on Interrupt
.PP
In order to provide for parallel processing in the vax and
in the lsi11, the following scheduling occurs:
.TS
allbox, tab(@);
c c c.
                        @LSI                    @VAX
Interrupt Time -->      @gather arm state       @may still be computing
                        @interrupt vax          @acknowledge
                        @send buffer            @get buffer
                        @wait for vax           @call user's fn1
                        @                       @set up command buffer
                        @get commands           @send command buffer
                        @execute commands       @call user's fn2
.TE
.PP
Three schemes are possible.
The first one spreads the computations among fn1 and fn2.
It is used to provide the fastest response to sensing feedback,
while providing the maximum computing time.
The second case only uses fn2 and provides the maximum computing time.
The third case provides for best control if the computing time is
short by using fn1 only.
At the first interaction, fn1 is not called.
.NH
Examples

The Marsh-mallow Program :
.PP
This program merely reads the actual positions of the joints at time $t$
and drive them at time $t+1$ with the read values.
If a torque is exerted on a joint when the positions are measured,
the actual position is:
.EQ
P sub a ~=~ P sub d ~+~ E
.EN
.PP
Where $P sub a$ is the measured position, $P sub d$ is the desired
position, and $E$ the position servo error.
One can assume that the error is proportional to the exerted torque.
.EQ
E~=~k~T
.EN
Where $k$ is a spring constant depending on the gain of the servo and
the sample rate.
$T$ is the exerted torque.
Let us compute the velocity by subtracting the differential position
between two successive sample times when the actual position at
time $t$ is specified as desired position at time $t+1$.
.EQ
P sub a sub {t+1} ~=~ P sub a sub {t}~+~ k~T sub {t+1}
.EN
.EQ
DELTA P sub a ~=~ P sub a sub {t+1} - P sub a sub {t} ~=~ k~T sub {t}
.EN
.PP
The velocity is proportional to the torque which make the manipulator
act like a highly damped free system.
.DS L
#include "rtc.h"

int     enough = NO;

main()
{
        extern int terminate;
        int dummy(), soft();

        control(dummy, soft);
        printf("press return to end ");
        getchar();
        enough = YES;
        while (!terminate)
                sleep(1);
        release("done");
}


dummy()
{
}


soft()
{
        extern struct chg chg;
        extern struct how how;
        register int i;

        if (enough) {
                terminate = YES;
                return;
        }
        chg.a_motion.set = POS;
        for (i = 0; i < NJOINTS; ++i) {
                chg.a_motion.vala[i] = how.pos[i];
        }
}
.DE
The Free Program :
.PP
This second example makes use of the torques to motor currents mapping
to overcome the gravity loadings of the manipulator joints.
.DS L
#include "rtc.h"

extern struct how how;
extern struct chg chg;

main()
{
        extern int terminate;
        int dummy(), freej();

        control(dummy, freej);
        for (; ; ) {
                pause();
        }
}

dummy(){}

static freej()
{
        double sin(), cos();

        static int first = YES;
        static unsigned short old[NJOINTS];
        short cur[NJOINTS];
        double jpos[NJOINTS], gtor[NJOINTS];
        double c2, c23, s23, c4, s4, c5, s5;
        register int i;

        if (first) {
                first = NO;
                for (i = 0; i < NJOINTS; ++i) {
                        old[i] = how.pos[i];
                }
                return;
        }
        enctoang(jpos, how.pos);
        c2 = cos(jpos[1]);
        c23 = cos(jpos[1] + jpos[2]);
        s23 = sin(jpos[1] + jpos[2]);
        c4 = cos(jpos[3]);
        s4 = sin(jpos[3]);
        c5 = cos(jpos[4]);
        s5 = sin(jpos[4]);
        gravload(gtor, c2, c23, s23, c4, s4, c5, s5);
        tortodac(cur, gtor, how.pos, old);
        for (i = 0; i < NJOINTS; ++i) {
                chg.a_motion.vala[i] = cur[i];
                old[i] = how.pos[i];
        }
        chg.a_motion.set = CUR;
}


gravload(l, c2, c23, s23, c4, s4, c5, s5)
double *l;
double c2, c23, s23, c4, s4, c5, s5;
{
        l[0] = 0.;
        l[2] = CP32 * s23 + CP31 * c23;
        l[1] = l[2] + CP21 * c2;
        l[3] = -(CP50 * s23 * s4 * s5);
        l[4] = CP50 * (s23 * c4 * c5 + c23 * s5);
        l[5] = 0.;
}

.DE
.NH
Calibration, stepping mode, and automatic home return
.PP
It is time to insist on the fact that when the controller is
powered up, the encoders counters contain random values that have
no relation with the physical configuration of the arm.
At least once after powering up the controller, the arm
.B has
to be calibrated.
This is achieved by calling
.B stepmode()
before doing anything.
When you exit from this mode, you are prompted for calibration.
The arm must then it a position close to the home position.
The calibration moves each joint if the arm, reset the counters
and move each joint again to the home position.
You must do it at least once, and in whichever configuration
you could leave the arm,
it is not necessary to do it before the next power up.
If your program terminates leaving the arm away from its
home position, you can either bring it back using the stepping
mode and calibrate it again, or merely break any program at any point
which cause the package to prompt you if you want
an automatic home return.
The return will be always successful, unless you seriously damaged
the arm.
.PP
The ARM POWER switch is the only physical communication that the
controller has with the rest of the universe.
The sequence of operation is always the same :

.IP 1
Power is off and the controller keeps monitoring the ARM POWER button;
.IP 2
You run you program, it can then open files, do system calls etc.
It can either :
.RS
.IP 2.1
exit.
.IP 2.2
call
.B stepmode;
.IP 2.3
call
.B control;
.RE
.IP 3
The channel is opened, and the vax is waiting for interrupts.
You press ARM POWER, and the controller starts interrupting the vax.
.IP 4
The vax receives interrupts and executes one of the three programs :
.RS
.IP 4.1
If you are in stepping mode, you can move individually each joint,
when done, the channel is closed;
.IP 4.2
If you asked for home return after a break, the arm will return and the whole
programs exits.
.IP 4.3
Otherwise, your program issued a call to
.B control
and your
interrupt functions are executed.
When your program is done, it must call
.B release;
.RE
.IP 5
The channel is closed, goto step 1.

.PP
You can code an unlimited number of 'control-release' pairs, if you need
either to use different interrupt functions or perform system calls
at a given moment.
.PP
The interface constantly monitors if the arm power is on.
If for any reason, the arm power is turned off, the controller
will still continue to run, but the vax will ignore interrupts
and the arm will remains in the state it was at the moment
power was turned off (and brakes set).
Turning on the arm power again, will resume the execution.
This may cause some jerky motions if the arm was
stopped in the middle of a trajectory.
.NH
Error handling
.PP
The problem is to cleanly terminate a program when something
goes wrong.
If the error is detected in the main program, a proper exit at any place
in the code can be :
.DS L
        if (panic) {
                terminate = YES;
                release("why ...");
                exit(1);
        }
.DE
If the error is detected in the interrupt functions,
.B release
and
.B exit
cannot be called but
.B terminate
can be set and
the interrupt function must return (in this case from interrupt).
The package will cease to execute your interrupt functions.
The main program remains active, arm power is turned off and
everything seems frozen.
The only resource is then to break the program
and perform an automatic home position return
if you wish.
The string to which the
.B mess
variable is pointing to will
be printed before the final exit. This is the only debugging
tool in the real-time environment.
Attention !, if the error is detected inside a function called
from the main interrupt function, caution must be taken to
cause a cascade of returns.
The following shows a few possibilities.
.DS L
fn()    /* interrupt function */
{
/* do whatever */
        if (panic) {
                mess = "why ...";
                terminate = YES;
                return;
        }
/* do whatever */
        if (fn1())
                return;
/* do whatever */
        fn2();
        if (terminate)
                return;
}

fn1()
{
/* do whatever */
        if (panic) {
                terminate = YES;
                mess = "why ...";
                return(YES);
        }
/* do whatever */
        fn2();
        if (terminate)
                return(YES);
/* do whatever */
        return(NO);
}

fn2()
{
/* do whatever */
        if (panic) {
                terminate = YES;
                mess = "why ...";
                return;
        }
/* do whatever */
}
.DE
.PP
Any message issued by the package is preceded by two stars, ex:
"** Channel opened, turn on ARM POWER", so you can distinguish them from your
own messages.
.PP
The package also performs checks on the validity of the
data send to and received from the controller.
Following is a list of all the messages:
.IP 1)
Error messages :
.RS
.IP
"** too many commands", this will occur there are more commands
specified than the output buffer can contain.
.IP
"** too large position increment", this means that ,in position mode,
one joint has been specified a value too far from the last read position.
.IP
"** out of range", means that a specified value would drive a joint
out of range.
.IP
"** too much current", means that you specified too a large value in
current mode.
.IP
"** too large observed current", a driving current is too high, probably
because the arm ran into an obstacle in position mode.
.IP
"** too large observed velocity", the velocity resulting from incorrect
current or position specifications is too high.
.IP
"** joint(s) out of range", the position resulting from incorrect
current or position specifications drives the arm out of range.
.IP
"** channel not released", the channel has not been released before
a reopen via 'control()'.
.IP
"** bad checksum", result of a hardware failure.
.IP
"** interrupt occurred before end of user function",
result of too lengthy computations. Most of the time you will get :
.IP
"**** hardware failure", the channel cannot initialize.
.IP
"**** driver error...exch = %d\n", something wrong, number of exchanges.
.IP
"** can't gtty", if the tty could'nt be put in cbreak mode during
the stepping mode.
.RE
.IP 2)
Informative messages.
.RS
.IP
"** Interrupted", when you break the program.
.IP
"** exch = %d", total number of times fn2 has been called.
.IP
"** end of step mode", self explanatory.
.IP
"** calibrating", while U wait for the arm to be calibrated.
.IP
"** attempting a home return", self explanatory.
.IP
"** back home, calibrated", ....
.IP
"** channel opened, turn on ARM POWER", informs that the vax is ready
to receive interrupts.
.RE
.IP 3)
Prompts
.RS
.IP
"** attempt an automatic home return ?", what you get when the
program is broken, answer `y' or `n'.
.IP
"** exit ? (y/n) _  calibrate ? (y/n) _  sure ? (y/n) _ ",
exit of the stepping mode.
.RE
.NH
Appendix
.PP
Additional entry points are provided :

.IP -
.B
enctoang(a, e)
.R
is a function whose first argument is an array of 'NJOINTS'
.B doubles
and whose second argument is an array of 'NJOINTS'
.B shorts.
The function converts the encoders values contained in the second
argument to angles expressed in
.B radians
within the range [-$pi$ ,+$pi$] relative to
the solution coordinate frames described in [Paul2 81] for the Puma
arm and in [Paul3 81] for the Stanford arm.

.IP -
.B
angtoenc(e, a)
.R
is a function which performs the inverse transformation.
The first argument is an array of 'NJOINTS'
.B shorts
and the second
argument is an array of 'NJOINTS'
.B doubles.
This function returns a non zero integer code when one or more
angle is out of range.
The bits are referenced by their octal value.
Bit 01 is set if joint 1 is out of range, bit 02 for joint 2, bit 04
for joint 3, and so on.

.IP -
.B
enctorng(r, e) and rngtoenc(e, r)
.R
perform the same functions
as above but angles are expressed in the [0 - range] interval.
In that case, an angle is zero when the corresponding joints
is at its limit position turning counterclockwise.

.IP -
.B
angtorng(r, a) and rngtoang(a, r)
.R
perform the mappings of
angles to ranges and vice-versa. Both arguments are arrays
of doubles.

.IP -
.B
tortodac(d, t, v, o)
.R
performs torques to dac values mapping.
Where 'd' is an array of 'NJOINTS'
.B shorts,
the resulting dac values, computed from the array 'd' of 'NJOINTS'
.B doubles,
the desired torques in
.B N-mm.
The arrays 'v' and 'o' are sets of 'NJOINTS'
encoder values, 'v'
is the current value of the joint positions, and 'o' the previous.
This is needed to decide the current velocity of each joint [Zhang 83].

.IP -
.B
adctotor(t, a, v, o)
.R
performs the inverse operation, where 't' are
the torques stored in an array of
.B doubles
computed from 'a' the measured adc values, an array of 'NJOINTS'
.B shorts.
The arrays 'v' and 'o' serve the same purpose as before.

.PP
Memory allocation substitutes, work as described in Chap 2 of
the UNIX manual, use :
.TS
tab(@);
c c c.
malloc_l @for @malloc
free_l @for @free
realloc_l @for @realloc
calloc_l @for @calloc
cfree_l @for @cfree
.TE
.EQ
delim ^^
.EN
.NH
Utility programs
.PP
A program called
.B calib
is made available, it contains :
.DS L
main()
{
        stepmode();
}
.DE
It allows to calibrate the arm after power up, such as calling stepmode
in the development programs is not mandatory.
Use :
.DS L
calib
.DE
.PP
A program called
.B play
reads a file of encoder values and ship them
to the controller to be executed.
Each setpoint is a record of seven 16 bits words.
The first six words are the encoder values for the joints
one through six.
The seventh 16 bits word specifies the hand position.
For historical reasons, it is expressed on the 12
high order bits.
With the pneumatic gripper, the character 'o' placed in those 12 bits,
opens it, 'c' closes it.
Play uses the file as a circular buffer and will play
the sequence of points over and over.
Both ends of the sequence must match.
Use :
.DS L
play file
.DE
The programs promts : "more ? (y/n)", if 'n' is answered, the arm will
stop at the end of the current cycle.
If 'y' is answered, it becomes possible to change the rate at which
the points are send by factor of two.
.PP
The programs
.B mkenc
reads a file of points in formated form and
makes a file of 'playable' encoders values created as '@@@.out' in
the current directory.
The points must be expressed in
.B radians
relative to the solution coordinate
frames (it uses 'angtoenc').
An error message is printed on the standard error file each time
a value is specified closer than five degrees off the joint limits.
Use :
.DS L
mkenc file    or     mkenc < file
.DE
where "file" contains lines like :
.DS L
0.00 -1.57 3.14 0 1.57 0
0.00 -1.57 3.14 0 1.57 0
0.00 -1.57 3.14 0 1.57 0
0.00 -1.57 3.14 0 1.57 0
.DE
.PP
The shell file
.B dl
performs the downloading of the superviser
down to the controller, use :
.DS L
dl moper
.DE
for the Puma.
.PP
One more detail, a file called 'llib-rtc'
in the style of 'llib-lc' describes the
form of the calls and external variables.
This can be used for 'linting' your programs and checking the proper
list of arguments.
Use :
.DS L
lint myprog.c -v llib-rtc
.DE
where 'llib-rtc' may have to be preceded
by some leading path (see below).
.PP
Finally, use the math library '-lnm' for efficiency.
.NH
Files
.PP
Currently are set up for the Puma manipulator :
.DS L
/b/rccl/l/rtc.a         the library
/b/rccl/h/rtc.h         the include file
/b/rccl/l/llib-rtc      lint description of the library
/b/rccl/s/calib         calibration program (merely a call to stepmode)
/b/rccl/s/play          the play program
/b/rccl/s/mkenc         make encoder values
/b/rccl/s/dl            down load the controller
/b/rccl/s/moper         the superviser
.DE
.PP
In order to save typing errors there are some usefull short hands
to specify the leading paths.
As an example we list here the commands necessary to compile, link
and execute a real time program.
We will first set the PATH shell variable such as the list
of search directories also include '/b/rccl/s' (this can be done in
the '.profile' or '.cshrc' startup files) :
.DS L
PATH=$PATH:/b/rccl/s            (for sh users)
export PATH

set path=($path /b/rccl/s)      (for csh users)
.DE
We can know use
.B calib,
.B play,
and
.B dl
as regular commands.
.PP
Then we may like to create shell variables to specify the libraries (
can be done in '.profile' or '.cshrc').
.DS L
rtc=/b/rccl/l/rtc.a             (for sh users)
rtclint="-v /b/rccl/l/llib-rtc"

set rtc=/b/rccl/l/rtc.a         (for csh users)
set rtclint=(-v /b/rccl/l/llib-rtc)
.DE
.PP
The last detail is to specify the include files :
.DS L
rtc="-I/b/rccl/h /b/rccl/l/rtc.a"       (for sh users)
rtclint="-I/b/rccl/h -v /b/rccl/l/llib-rtc"

set rtc=(-I/b/rccl/h /b/rccl/l/rtc.a)        (for csh users)
set rtclint=(-I/b/rccl/h -v /b/rccl/l/llib-rtc)
.DE
Now the compile and link can be done as :
.DS L
cc myprog.c $rtc -lnm
.DE
and the lint :
.DS L
lint myprog.c $rtclint
.DE
If the controller has been downloaded and the arm calibrated, just type :
.DS L
a.out
.DE
When you get the message "** channel opened, turn on ARM POWER",
turn on ARM POWER and good luck.
.NH
References
.IP
Paul , R., P., Shimano, B. , Mayer, E., G.,
"Kinematic Control Equations for Simple Manipulators",
IEEE Transactions on Systems, Man, and Cybernetics, Vol. SMC-11,
No 6, June 1981.
.IP
Paul, R. P., "Robot Manipulators: Mathematics, Programming,
and Control", MIT Press 1981.
.IP
Zhang, H., Paul, R. P.,
"Determination of Simplified Dynamics of Puma Manipulator", Purdue University.
