\documentstyle[12pt,final,twoside,sf,sfheads]{rccldoc}
\begin{document}
\title{Real-Time Trajectory Generation in Multi-RCCL}
\author{John Lloyd and Vincent Hayward}

\date{April 1992}
\maketitle
\parskip = 2ptplus3pt

\begin{abstract}
This paper describes the design of the trajectory generator for
a robot programming system called Multi-RCCL, which is a package
of ``C'' routines for doing real-time manipulator control
in a UNIX environment. RCCL has been used successfully in developing 
robot control applications in numerous research and industry
facilities over the last several years. One of its strongest
features is the ability to integrate real-time sensor control
into the manipulator task specification.
RCCL primitives supply the trajectory
generator with target points for motions in joint or Cartesian coordinates.
Other primitives allow the code developer to specify on-line
functions that can modify the target points, or possibly cancel
motion requests, in response to various sensor or 
control inputs. The design requirements of the 
trajectory generator are that it be able to integrate these
on-line modifications into the overall robot motion, and provide a
smooth path between adjacent motions even when sensor
inputs make the future trajectory uncertain.

\end{abstract}
\newpage

\chapter{Introduction}

Multi-RCCL is a library of ``C'' routines and data structures
for writing robot control programs. It is an extension
to the original RCCL (Robot Control C Library), which was 
written at Purdue University by Vincent Hayward \cite{Hayward86}.
Since then, various enhancements have been made to the system, including
the ability to control multiple robots and distribute the trajectory control
over several CPUs. Most of this work has been done at McGill University
and at the Jet Propulsion Laboratory \cite{Lloyd88,Lloyd89,Hayati89}. 

RCCL's two fundamental features are:

\begin{list}{}{}

\item[$\diamondsuit$]
A comprehensive set of ``C'' language primitives for specifying
robot motions and actions in a UNIX workstation environment.

\item[$\diamondsuit$]
The ability to write application code
which processes sensory information and adjusts the robot
trajectory in real-time.

\end{list}

These two features have made RCCL a particularly 
useful tool for developing applications where manipulator actions
are controlled by various sensor and control inputs. 
Since the system provides direct on-line control of the manipulator,
special techniques have been used to obtain the required real-time
performance from conventional UNIX workstations \cite{Lloyd90}.

Principal RCCL sites include the Jet Propulsion
Laboratory, the General Electric Advanced Technology Laboratory
(New Jersey), Columbia University, the University of Pennsylvania,
the University of Southern California, NASA Goddard Space flight center,
the University of Illinois, Universit\"at Bielefeld (Germany), 
and several others. Specific applications for which it has been
used include manipulator force control and telerobotics 
\cite{Lee86,Kan90}, 
multi-arm force control \cite{Hayati90}, visual servoing and tracking
\cite{Allen91,Castano92}, active visual exploration
\cite{Whaite91}, and the development of robot programming
interfaces \cite{Fagg91}.

The system's trajectory generator, and its ability to integrate sensor
inputs with preplanned actions, has thus been thoroughly
demonstrated over the last couple of years. 
The purpose of this paper is to
present the design of the trajectory generator in detail. 

It should be noted that responding to dynamic events requires that 
virtually all of the trajectory generation be done directly on-line,
in a manner that precludes some of the more optimal solutions
that are possible if the trajectory is planned in advance
\cite{Bobrow85,ShinMcKay86}.

The reader is assumed to be familiar with the original book
by Richard Paul \cite{Paul81} and all
the notational conventions used there. 
For purposes of clarity, we have omitted 
describing certain details and special cases.

\chapter{Overview}

RCCL generates the trajectories necessary to move a manipulator
through sequences of {\it target positions\/}, 
which are submitted to a trajectory generator for
asynchronous execution.
Target positions can be specified in either Cartesian or joint coordinates.
This paper will focus only on the former; joint
target specifications can be handled in the same way.
A Cartesian target is described by a {\it position equation\/},
which is a closed kinematic loop of 4x4 homogeneous transforms
containing the manipulator's
${\bf T6}$ transform. 
The component transforms
of such target equations may be time-varying, and motions 
to such targets will {\it track\/} the variations. 
For example, suppose we wish 
to have a manipulator pick up an object on a moving platform.
The application program could define the following target equation,
\begin{equation}
{\bf Z} \; {\bf T6}(t) \; {\bf E} \;=\; {\bf W} \; {\bf P}(t)   \label{loop}
\end{equation}
and motions to this target will cause the manipulator to travel 
to a position where {\bf T6} satisfies the equation (see Figure \ref{diag}).
In this case ${\bf P}(t)$ is time-varying, and 
${\bf T6}$ will track the changes.
A principal means by which the application program may
vary a component transform is to attach it
to a function that is evaluated once every control cycle
by the trajectory generator; such a function may modify the transform
based on either some preprogrammed plan or sensor inputs. 
``Stopping'' at a time varying target implies that the manipulator
will reach the target but continue to track its motions.

\begin{figure}[tbh]
\boxedsizedfig{4.5truein}{3.5truein}%
{\psfig{figure=figs/tgmr1.idr}}%
{Kinematic loop for a manipulator target attached
to a moving platform.}%
\label{diag}%
\end{figure}
%

The fact that the target specification is dynamic makes the system 
quite versatile but means that virtually
all of the trajectory computation must be done on-line. This
is, in fact, the greatest constraint imposed on everything
that we will discuss.

Internally, the trajectory generator divides the target position equation
into a {\bf TOOL} term and a {\bf COORD} term: {\bf TOOL} is
the set of transforms between {\bf T6} and whatever frame the application
has designated as the {\it tool frame\/}, and {\bf COORD} 
is ``other half'' of the equation. In general, these terms
will be time-varying if their components are. Assuming in the above
example that the tool frame is designated by 
{\bf E}, then RCCL would represent
(\ref{loop}) internally as
\[
{\bf T6}(t) \;\; {\bf TOOL} \;=\; {\bf COORD}(t)
\]
where
\begin{eqnarray}
{\bf TOOL} & \;=\; & {\bf E} \\
{\bf COORD}(t) & \;=\; & {\bf Z}^{-1} \; {\bf W} \; {\bf P}(t)
\end{eqnarray}
This partioning is used for performing the {\it drive\/} computation
(discussed later).

The job of the trajectory generator is to compute paths between
successive (possible time-varying) target points.
The application can specify whether this path is to be
computed in Cartesian or joint coordinates, and imposes velocity and
acceleration constraints in the selected coordinate system.
The velocity constraint determines the overall time of the path segment
($\sigma$), while the acceleration constraint determines the length
of time ($\tau$) spent doing a smooth {\it transition\/} 
between adjacent path segments.

\begin{figure}[tbh]
\boxedsizedfig{6truein}{3truein}%
{\psfig{figure=figs/tgmr2.eps}}%
{Path segments between target positions.}%
\label{path}%
\end{figure}

Figure \ref{path} shows (in one dimension) path segments connecting
an initial point $A$, a via point $B$, and a stopping 
point $C$. The $\sigma$ and $\tau$ values are shown for
the path segment $\overline{BC}$. Notice that $\tau$ is actually one half the
total transition time, which is
symmetrical about the path segment endpoints. The action of
``stopping'' at $C$ is also computed as a path segment (in effect another
motion request to the same target) whose $\sigma$ 
is specified explicitly. This technique is particularly useful
since the target position we ``stop'' at might actually be 
in motion.
Finally, notice that unless we 
actually do stop at a target point, the transitions are computed
to ``undershoot'' it. 

The trajectory computations are done
in real-time at a fixed sample rate. The target position
({\it i.e.}, the {\bf COORD} and {\bf TOOL} terms) is re-evaluated
once every control cycle; this action will include the evaluation
of any functions bound to the component transforms.

For convenience, this paper will use a time scale where the
start time of the path segment under discussion is given
by $t_b$ and its end time is $t_c$ (such that $\sigma = t_c - t_b$). 
Moreover, the time corresponding
to one control cycle will be defined as unity, and the time scale
will be offset so that $t_b - \tau = 1$. Under these coordinates,
$\tau$ and $\sigma$ are equal to their respective number of
control cycles.

As stated above, the path interpolation can be done in either
Cartesian or joint coordinates.
The latter is accomplished
using a {\it drive\/} transform constructed in
the manipulator tool frame.
Individual path points
are then computed from the equations
\begin{eqnarray*}
{\bf T6}(t) & \;=\; & {\bf COORD}(t) \; {\bf DRIVE}(t) \; 
                    {\bf TOOL}(t)^{-1}\\
\noalign{\medskip}
{\bf j6}(t) & \;=\; & \Lambda^{-1}({\bf T6}(t)) 
\end{eqnarray*}
where $\Lambda^{-1}()$ indicates an inverse robot kinematics
operation.
At time $t_b$, the drive transform 
accommodates the initial difference between the current position 
and the target. Over the course of the path segment, {\bf DRIVE}(t) is
computed so as to trace a ``straight line'' from its initial value to the
identity {\bf I}.
To do this, a function {\it trsfToDrive()\/} is used
to convert {\bf DRIVE} into a
set of {\it drive parameters\/} ({\bf dp}) which can be linearly scaled.
Given a function {\it driveToTrsf()\/}
that converts drive parameters back into transforms,
and a normalized {\it motion scale\/} coordinate $s$, defined as
\[
s = {(t - t_b) \over \sigma},
\]
the subsequent values of ${\bf DRIVE}(t)$ can be computed from
\[
{\bf DRIVE}(t) \;=\; {\it driveToTrsf\/}\,((1-s) \, {\bf dp})
\]
The mechanics of the drive parameters are described
in \cite{Paul81}, chapter 5.

Computing the path in joint coordinates is similar except that
in this case the drive interpolation
is done in joint space. The corresponding computation is
\begin{eqnarray*}
{\bf TC}(t) & \;=\; & {\bf COORD}(t) \; {\bf TOOL}(t)^{-1}\\
\noalign{\medskip}
{\bf j6}(t) & \;=\; & \Lambda^{-1}({\bf TC}(t)) + {\bf jdrive}(t)
\end{eqnarray*}
where {\bf TC} indicates the value of the motion target in the T6 frame.
Instead of using a Cartesian {\bf DRIVE} transform, we use
a vector of joint coordinates {\bf jdrive}. 
The drive parameters {\bf jdp} are simply the initial values of {\bf jdrive},
which means that
\[
{\bf jdrive}(t) \;=\; (1-s) \, {\bf jdp}
\]
Notice that
the path will still track time-variations in the Cartesian
target position.

\bigskip
\bigskip

Following from the above discussion, 
the rest of this paper will describe, in general, how Multi-RCCL 
(1) computes transitions between path segments, (2) determines the 
drive parameters, and (3) computes $\sigma$ and $\tau$, under conditions
where the target position may be changing. A detailed description of
the specific Joint and Cartesian path generation algorithms is also
included.

\chapter{Transitions Between Path Segments}\label{blend}

When we abut adjacent path
segments, we must generally allow a transition time $\tau$ during
which one path segment can be smoothly blended into another.
The magnitude of $\tau$ reflects the maximum amount of acceleration
that can be tolerated by the manipulator; a longer value of
$\tau$ is necessary for path changes involving larger velocity
changes. The $\tau$ for any motion is computed dynamically
(section \ref{tau}).

The path segment blending algorithm used by RCCL is quite
independent of the other parts of the trajectory calculation, and so
we discuss it first. For illustration, we will consider
the blending of two one dimensional path segments $S_0(t)$ and $S_1(t)$.
Each path segment is the combined result of a {\it drive\/} toward a
target plus variations in the target itself.

\begin{figure}[tbh]
\boxedsizedfig{6truein}{4truein}%
{\psfig{figure=figs/tgmr3.eps}}%
{Blending two path segments together.}%
\label{blend.fig}%
\end{figure}
%

To smoothly connect the path segments, a blend function $\beta$
(see Figure \ref{blend.fig})
is applied to $S_1(t)$ over the 
interval $1 \le t \le t_b + \tau$ (recall that $t_b - \tau = 1$).
The implication of this is that the computation
for a path segment actually starts 
at time $t_b - \tau$ {\it before\/} the time $t_b$ when
it officially starts.

All the parameters needed to compute the blend function can be
determined from the initial position, velocity, and acceleration
of $S_0$ and $S_1$ at $t = 1$. Assume for the moment that
the blend function can be applied simply by adding it
to $S_1$ (although this is not true for rotations;
see section \ref{rotblend}).
It must then satisfy the following constraints:
\begin{eqnarray}
S_1(1) + \beta(1) & = & S_0(1) \label{boundary} \\
S_1(1)' + \beta(1)' & = & S_0(1)' \nonumber \\
S_1(1)'' + \beta(1)'' & = & S_0(1)'' \nonumber \\
\beta(t_b + \tau) & = & 0 \nonumber \\
\beta(t_b + \tau)' & = & 0 \nonumber \\
\beta(t_b + \tau)'' & = & 0 \nonumber
\end{eqnarray}
These can satisfied by computing the blend function with a
fifth order Hermite polynominal
\begin{equation}
\beta(h) = \alpha_5 h^5 + \alpha_4 h^4 + \alpha_3 h^3 +
           \alpha_2 h^2 + \alpha_1 h + \alpha_0 \label{Hermite}
\end{equation}
where $h$ is a time parameter normalized to span the interval $[0,1]$
during the transition:
\[
h = {t - t_b + \tau \over 2 \tau }
\]

Defining the difference between $S_1$ and $S_2$ to be 
$\delta(t) \equiv S_1(t) - S_2(t)$,
the coefficients of (\ref{Hermite}) that satisfy 
(\ref{boundary}) are:
\begin{eqnarray*}
\alpha_0 & = & \delta(0) \\
\alpha_1 & = & \delta'(0) \\
\alpha_2 & = & 0.5 \, \delta''(0) \\
\alpha_3 & = & -10 \,\delta(0) - 6 \,\delta'(0) - 1.5 \,\delta''(0) \\
\alpha_4 & = & 15 \,\delta(0) + 8 \,\delta'(0) + 1.5 \,\delta''(0) \\
\alpha_5 & = & -6 \,\delta(0) - 3 \,\delta'(0) - 0.5 \,\delta''(0) 
\end{eqnarray*}

Joint space paths, and the translational components of Cartesian paths,
can be blended by the direct extension of this formulation to
several dimensions. The blending of rotational trajectories, which is
more complicated because rotations do not commute and therefore cannot be
treated as vector quantities, is discussed in section \ref{rotblend}.

The blend function is actually just the ``{\bf DRIVE} transform''
revisited, only here
discontinuities are being removed
in the derivative terms. We could dispose of the drive computation
all together, and
simply make the transition interval cover the entire path segment.
We don't do this, however, since applications often desire constant
velocity straight line trajectories.

Notice that in figure \ref{blend.fig},
$S_0$ and $S_1$ intersect at $t_b$. Generally,
the trajectory generator sequences events so this 
is in fact the case, yielding an ``undershoot'' transition
(of the sort described in \cite{Paul81}) that goes through the inside
of the intersection point. However, the paths do not have to intersect at
$t_b$; different blending behaviors will result depending on where
(or even if) they intersect. 
Variations on the blend function described here have been suggested that
allow the application to ``tune'' the extent of the via-point undershoot,
or specify an overshoot \cite{Hayward88}.

The principal advantage of the blend technique is that it is robust:
we do not need to know anything about the path $S_0$ after
time $t = 1$, and we do not need to know anything about path
$S_1$ in advance. $S_0$ and $S_1$ do not even have to be straight lines,
as illustrated in the figure. This is important since
the target may be time-varying and sensor-driven and hence not
completely predictable.

\section{Initializing the blend parameters}\label{blendinit}

To initialize the blend function it is necessary 
to know the values of $S_0$ and $S_1$ and their derivatives at
time $t = 1$.

For computational reasons,
RCCL does not presently worry about any initial difference
in acceleration between the path segments: for purposes of computing
the blend function, it is assumed that $S_0''(1) - S_1''(1) = 0$.
We therefore need only $S_0(1)$, $S_0'(1)$, $S_1(1)$, and $S_1'(1)$.
In general, however, we don't have $S_1'(1)$ since we have only started
computing $S_1(t)$ during the first control cycle.
To accommodate this, the trajectory generator waits
for one more control cycle and then computes
$S_1(2)$, from which $S_1'(1)$ may be estimated as
$S_1'(1) = S_1(2) - S_1(1)$.
The output setpoints for the first control cycle are simply
set equal to $S_0(1)$.

Two interface functions are defined
which compute the required derivatives and
blend parameters from the values of
$S_0$ and $S_1$ at times 1 and 2:
\[
        {\it setBlend0\/}(S_0(0), S_1(0))
\]
\[
        {\it setBlend1\/}(S_0(1), S_1(1), 2 \tau)
\]
The third argument to {\it setBlend1()\/} gives the total length of
the transition time, which is needed to compute $h$.

Since the blend function cannot always be applied by simple addition,
its application to $S_1$ will be indicated generally as
$\beta(S_1(t))$.

The computation of $S_0(1)$ and $S_0(2)$ is currently done by
extrapolating the initial path:
\[
S_0(1) = S_0(0) + S_0'(0)
\]
\[
S_0(2) = S_0(0) + 2 S_0'(0)
\]
There are two reasons doing this, instead of
computing $S_0(t)$ directly.
The first is simply to save computation.
The second
is that we do not wish (under the present implementation)
to compute $S_0$ at the same time we are computing $S_1$,
since some of the transforms which define the first target
may also be used in the definition of the second target. 
If any of these transforms
are bound to an application defined function, then that function would
have to be called twice during the same control cycle in two different
motion contexts. Resolving this properly would
require adjustments to
the present application interface. 
The small errors introduced by extrapolating
$S_1$ have not caused any difficulties to date.

\section{Blending Rotations}\label{rotblend}

Analogous to the previous discussion, we can consider the problem
of blending rotations as computing a smooth transition between two
rotational paths ${\bf R0}(h)$ and ${\bf R1}(h)$ over
a normalized time interval $[0,1]$. Unlikely the treatment above,
however, we cannot do this using a simple Hermite polynominal
since there is no vector-like representation for rotations that
defines a proper metric for them \footnote{Although 
vector blending can work if the rotational distance between 
${\rm R0}$ and ${\rm R1}$ is not very large; for instance,
interpolation of
quaternions works quite nicely for displacements less than $45^\circ$.}.

One obvious solution to this problem is to find a space that
{\bf does} form a metric for rotations and do the blending there. For
instance,
the unit sphere in $\Re^4$ formed by the set of unit quaternions forms
a metric for rotations, and is possible to construct curves on the surface of
this sphere to smoothly connect different rotation states \cite{Shoemake85}.
Unfortunately, this technique is a little too 
expensive (computationally) to be of use for one-line calculation.

Instead, we use a method that
has some similarities to the work described in \cite{Taylor79}.
To begin with, the computation has been simplified by
ignoring initial rotational accelerations.
This has not caused
any noticeable problems, and acceleration compensation could
be added, if necessary, as a simple extension of the following method.

The blend function $\beta$ takes the form of a
blend rotation ${\bf RB}(h)$ that is applied to ${\bf R1}(h)$ 
over the transition
interval, producing the output rotation ${\bf R}(h)$:
\[
{\bf R}(h) \;=\; {\bf R1}(h) \; {\bf RB}(h)
\]
Let the rotational velocities of {\bf R0}, {\bf R1}, and {\bf RB},
be 
given by $\Omega_0$, $\Omega_1$, and $\Omega_B$. We then have the following
boundary conditions:
\begin{eqnarray*}
{\bf RB}(0) & \;=\; & {\bf R1}(0)^{-1} \; {\bf R0}(0) \\
{\bf RB}(1) & \;=\; & {\bf I} \\
{}^{R0(0)}\Omega_B(0) & \;=\; & {}^{R0(0)}\Omega_0(0) - {}^{R0(0)}\Omega_1(0)\\
\Omega_B(1) & \;=\; & 0 
\end{eqnarray*}
The velocities in the third equation are described with respect to the
${\bf R0}(0)$ coordinate frame.

Looking at the boundary conditions, we see that what we essentially
want to do is remove the initial displacement and 
velocity of ${\bf RB}$. The initial displacement ${\bf RB}(0)$ 
can be expressed as a single rotation about some axis. If this axis
is parallel to $\Omega_B(0)$, then the whole problem reduces to
a one dimensional one in terms of the rotation parameter about that axis. 
While this is not
generally true, it suggests resolving {\bf RB} into separate rotations that
compensate for velocity and displacement. Let the notation 
${\it rot\/}(\theta, {\bf u})$ denote a rotation of $\theta$ about the
axis ${\bf u}$. Now let $\omega_0 = \|\Omega_B(0)\|$, let
${\bf u}_v$ be a unit vector parallel to $\Omega_B(0)$, and
define ${\bf RB}$ as the following product:
\[
{\bf RB}(h) \;=\; 
{\bf R1}(0)^{-1} \; {\bf R0}(0) \; {\bf RC}(h) \; {\bf RV}(h)
\]

The first two (constant) terms compensate for the initial displacement.

The ${\bf RV}(h)$ term, which is initially equal to {\bf I}, acts to
bring $\Omega_B(0)$ to 0:
\[
{\bf RV}(h) \;=\; {\it rot\/}(\omega_0 g_v(h)/2, \; {\bf u}_v)
\]
where $g_v$ is the polynomial
\[
g_v = (h^3 - 2 h^2 + 2) h
\]
defined so that
{\bf RV} satisfies the boundary conditions
which require it to have an initial velocity of $\omega_0$ and a final
velocity of 0, and 0 acceleration at both ends.

Finally, the ${\bf RC}(h)$ term brings all displacements to 0, including
those incurred by {\bf RV}. Since ${\bf RB(1)} = {\bf I}$, we have that
\[
{\bf RC}(1) \;=\; {\bf R0}(0)^{-1}\;{\bf R1}(0) \; {\bf RV}(1)^{-1}
\]
Letting ${\bf u}_c$ be the axis of rotation for ${\bf RC}(1)$ and
$\theta_c$ be the angle of rotation, ${\bf RC}(h)$ is computed from
\[
{\bf RC}(h) \;=\; {\it rot\/}(\theta_c g_c(h), \; {\bf u}_c)
\]
where $g_c$ is the polynomial 
\[
g_c = (6 h^2 - 15 h + 10) h^3
\]
which gives ${\bf RC}$ a velocity and acceleration of 0 at the
transition endpoints.

\chapter{Computing the drive parameters}\label{drive}

In this section, we will continue to use the one dimensional paths
$S_0$ and $S_1$ to
illustrate how drive parameters are computed, first for fixed targets
and then for time varying targets.

\begin{figure}[tbh]
\boxedsizedfig{6truein}{4truein}%
{\psfig{figure=figs/tgmr4.eps}}%
{Computing the drive parameter for fixed targets.}%
\label{blend2}%
\end{figure}


\section{Fixed Targets}

Refer to figure \ref{blend2}. 
When the computation begins for path $S_1$ at $t = 1$,
the manipulator
is following path
$S_0$ is on its way to some point $B$.
If $C_0$ is the official target point for path $S_0$, and
is constant, then 
$B = C_0$. If the $C_0(t)$ is not constant,
then $B = C_0(t_b)$, which creates a minor problem 
because $C_0(t_b)$ is generally not known ahead of time.
In this case $B$ can be estimated by extrapolation:
\begin{equation}
\hat{B} = S_0(0) + (\tau + 1) S_0'(0)\label{hatb}
\end{equation}
Another instance where it becomes necessary to do this is when the
motion along $S_0$ is interrupted prematurely, before it has had time to
approach the official target.

Assume that the (constant) target for the next path segment
is $C_1$.   
The drive parameter $D$ for this motion is then simply
\begin{equation}
D = \hat B - C_1        \label{d1}
\end{equation}
and $S_1$ can be computed by
\begin{equation}
S_1(t) = C_1 + (1-s) D \label{s2}
\end{equation}

\section{Time-varying Targets}

If $C_1(t)$ is time-varying, then there is another minor
problem deciding how to
compute the drive parameter. During the first cycle,
the drive offset $O(1)$ can be computed from the initial
target value as
\[
O(1) = \hat{B} - C_1(1)
\]
But repeating the computation during the second cycle yields
\[
O(2) = \hat{B} - C_1(2)
\]
which may be different. 
What we really want is the drive offset for time $t_b$
(See figure \ref{blend3}):
\[
O(t_b) = \hat{B} - C_1(t_b)
\]
This yields an equation for
$S_1$ that looks like
\begin{equation}
S_1(t) = C_1(t) + (1-s) \; (\hat{B} - C_1(t_b)) \label{s2t}
\end{equation}
Substituting in for time $t_b$, we notice that 
\[
S_1(t_b) = \hat{B}
\]
which is correct.

\begin{figure}[tbh]
\boxedsizedfig{6truein}{4truein}%
{\psfig{figure=figs/tgmr5.eps}}%
{Computing the drive parameter for moving targets.}%
\label{blend3}%
\end{figure}


While $O(t_b)$ cannot be known with certainty ahead of time,
if it is assumed that $C_1(t)$ is roughly linear in the transition region,
then $O(t_b)$ can be estimated by extrapolation:
\[
D \equiv \hat{O}(t_b) =  O(1) + \tau (O(2) - O(1))
\]
This requires postponing the computation of $D$ until the second
control cycle, but we already have to ``coast''
for the first cycle anyway, for purposes of calculating the blend parameters.

Once $D$ has been computed, it is necessary to compute $S_1(2)$ and
``back compute'' $S_1(1)$ in order to initialize the blend parameters.
This can be done using (\ref{s2t}), which yields, with the
$1-s$ term expanded,
\begin{eqnarray*}
S_1(1) & = & C_1(1) + \left({t_c - 1 \over \sigma}\right) D \\
S_1(2) & = & C_1(2) + \left({t_c - 2 \over \sigma}\right) D 
\end{eqnarray*}

\section{Stopping}\label{stop}

The path generation methods given above 
are quite versatile and can handle a variety of cases.
For instance, if we switch path interpolation
modes from the previous cycle, then we only have to transform the 
initial velocity estimate associated with $S_0'(0)$. Since 
path points are computed in both Cartesian and joint coordinates anyway,
this is easy. We can also handle motion interruption easily: if 
a motion is canceled before it has reached its anticipated target,
its ``virtual target'' $\hat{B}$ can
be computed using (\ref{hatb}), and the rest of the computation
is the same.

There is one particular case that is treated separately, and
that is stopping. Recall that this is implemented as a duplicate
motion to the same target point.
This implies some simplification in computing the
the trajectory paths. For one thing, there is no need to
do a {\it drive\/} computation: by definition, the manipulator
has already reached its target.
It might be necessary to maintain a fixed
{\it drive offset\/}, however: in cases where the preceding motion
was interrupted before completion, the
most natural option is to
stop at the point where the motion was interrupted, rather than
at a point that was never reached in the first place.

This is done in the following way:
During the first computation cycle of the stop segment,
the final scale
factor $s_f$ associated with the previous motion is determined.
If the motion went to completion, then
$s_f = 1$; otherwise, $s_f < 1$.
The constant term $(1-s_f) D$ is then used in 
(\ref{s2t}) in place of the time varying term $(1-s) D$.

\chapter{Computing the path segment and transition times}\label{tausigma}

This section outlines briefly the calculation of
$\tau$ and $\sigma$. It should be mentioned in passing that
although these times are normally computed
automatically, as described here, RCCL does permit the
application to specify them explicitly. 
All operations described here are assumed to be normalized to
the appropriate units.

\section{Transition times}\label{tau}

RCCL computes its path segment transition times ``on the fly'',
taking into consideration the current velocity, the distance
to the next target, and the specified velocity and acceleration
limits.
While the method used is only a heuristic, it works
reasonably well and is computationally tractable.
The computation for Cartesian motions will be described here.
The equivalent computation for joint motions is analogous.

Velocity and acceleration constraints 
for Cartesian motions are specified in terms of
a maximum translational ``cruising'' speed $\bar v_t$,
a maximum rotational speed $\bar v_r$, a maximum translational
acceleration $\bar{a}_t$, and a maximum rotational acceleration 
$\bar{a}_r$. To determine $\tau$, the system estimates
the peak translational and rotational velocity 
($\widehat{\bf v}_t$ and $\widehat{\bf v}_r$) for the upcoming 
path segment,
subtracts these from the current velocities ${\bf v}_{t0}$ and 
${\bf v}_{r0}$, and divides by the appropriate acceleration limit.

The path segment velocities are estimated as follows.
Assume that the manipulator is currently heading toward a
target point ${\bf C1}(t)$, and the target point for the next
path segment is ${\bf C2}(t)$. The
displacement ${\bf D}$ between these two targets can be computed as
\[
{\bf D} \;=\; {\bf C1}(t)^{-1} \; {\bf C2}(t)
\]
${\bf D}$ is used to compute ${\bf u}_t$ (a unit vector parallel to
the translation vector), $d_t$ (the magnitude of the translation vector),
${\bf u}_r$ (a unit vector describing the equivalent axis of
rotation), and $d_r$ (the angle of rotation about ${\bf u}_r$).

The path segment is assumed to consist of an acceleration
phase, a cruise phase, and a deceleration phase. Considering
first the translational
component, the system constructs estimates
of the speed ($\hat v_t$) and path segment
time ($\hat \sigma_t$).
Let $\delta_a = \bar{v}_t^2/\bar{a}_t$ be the nominal translational
distance covered during the acceleration/deceleration phase. 
If $\delta_a \le d_t$, then $\hat v_t$ and $\hat \sigma_t$
are given by
\begin{eqnarray*}
\hat \sigma_t & \,=\, & 2 \bar v_t / \bar{a}_t + (d_t - \delta_a)/\bar v_t \\
\hat v_t & \,=\, & \bar{v}_t 
\end{eqnarray*}
Otherwise, if $\delta_a > d_t$, then the desired cruising velocity
$\bar v_t$ will not be reached, and $\hat v_t$ and $\hat \sigma_t$ are 
given by
\begin{eqnarray*}
\hat \sigma_t & = & 2 \sqrt{d_t/\bar{a}_t} \\
\hat v_t & = & \hat \sigma_t \bar{a}_t / 2 
\end{eqnarray*}
The same computation may be repeated for the rotational component
to obtain $\hat \sigma_r$ and $\hat v_r$. 
Since $\hat \sigma_t$ and
$\hat \sigma_r$ will generally not be equal, the actual estimate
of the path segment time $\hat \sigma$ is set
to the maximum of the two:
\[
\hat\sigma = {\rm max}(\sigma_t, \sigma_r)
\]
$\widehat{\bf v}_t$ and
$\widehat{\bf v}_r$ can now be computed as
\begin{eqnarray*}
\widehat{\bf v}_t & = & \left({\sigma_t \hat{v}_t \over \hat\sigma}\right)
{\bf u}_t \\
\widehat{\bf v}_r & = & \left({\sigma_r \hat{v}_r \over \hat\sigma}\right)
{\bf u}_r 
\end{eqnarray*}

Finally, $\tau$ is determined from
\[
\tau = {1 \over 2}{\rm max}\left({
{\|\widehat{\bf v}_t - {\bf v}_{0t}\| \over \bar{a}_t }, 
{\|\widehat{\bf v}_r - {\bf v}_{0r}\| \over \bar{a}_r }
}\right)
\]
If the next path segment is a stop request,
then $\widehat{\bf v}_t$ and 
$\widehat{\bf v}_r$ are 0 and
may be eliminated from the computation
altogether.

\section{The Motion Segment Time}\label{sigma}

The motion segment time $\sigma$ was estimated during the computation
of $\tau$. However, this considered only the anticipated
displacement for the path segment, and was not updated to be
consistent with the final computed value of $\tau$.

During the first computation cycle of the path segment,
$\sigma$ is re-computed using the distance to the target
and the known value of $\tau$. This calculation is quite simple,
and is illustrated again using the translational component.
This time, let $\delta_c = d_t - 2 \tau \bar{v}_t$ represent the total distance
covered
during the cruise phase of the path segment.
If $\delta_c > 0$, then $\sigma_t$ is computed from
\[
\sigma_t = 2 \tau + d_t/\bar{v}_t
\]
Otherwise, the path segment does not contain a cruise phase, and
\[
\sigma_t = 2 \tau
\]
After repeating the these computations for the
rotational component to obtain $\sigma_r$,
the final value of $\sigma$ is computed from
\[
\sigma = {\rm max}(\sigma_t, \sigma_r)
\] 

\section{General Remarks}

For joint interpolated motions, the computations are similar to those
above, with each joint value being treated as an
independent constraint component. 

It is emphasized again that the computation of $\tau$ is only 
approximate, with computation time being the chief
limiting factor. In particular, a precise determination of
$\widehat{\bf v}_t$ and $\widehat{\bf v}_r$ would have to
also consider the initial velocities ${\bf v}_{t0}$ and ${\bf v}_{r0}$. 

These calculations require that ${\bf C2}(t)$ be computed
simultaneously with the current motion target ${\bf C1}(t)$.
It was mentioned briefly in section \ref{blendinit} that RCCL avoids
computing two paths simultaneously so as to not have to evaluate
a functionally bound transform twice in two different motion
contexts. This rule is still adhered to; ${\bf C2}(t)$ is determined
by multiplying the transforms of the associated position equation
but {\bf not} calling any functions that may attached to them. This
can cause inaccuracies if a transform's function introduces discontinuities
to its value at the beginning of the motion, although such cases are rare.

Finally, it should be mentioned that $\bar{a}_t$ and $\bar{a}_r$ 
could be computed dynamically using the manipulator Jacobian and
the joint acceleration limits, although this
is not done at the present time.

\chapter{Cartesian Motions}

This section presents the specific computations used by the
trajectory generator to do Cartesian interpolated motions. 
Some things have been omitted, including calculations
related to sections \ref{stop} and \ref{tausigma}.

\section{Special operations used}

A few of the operations used in the general discussion need to
modified for Cartesian coordinates. 
In particular, it is not possible to simply extrapolate
a transform matrix ${\bf M}$ directly by computing something like
\begin{equation}
{\bf M}(k) \;=\; {\bf M}(0) + k {\bf M}'(0) \label{impossible}
\end{equation}
An equivalent computation is possible, however. Suppose
that a displacement is represented, not by a transform,
but by a 7-tuple consisting of a position vector, an axis of rotation, 
and an angle of rotation about that axis\footnote
{This forms a computationally convenient representation for a finite screw.}%
RCCL maintains such objects and calls them DSPLs (for 
{\it displacements}). The functions
{\it trsfToDspl()\/} and {\it dsplToTrsf()\/} are used to convert between
them and transforms. A DSPL representation can be easily
scaled by simply multiplying the position vector and the rotation angle.
The combined operation of scaling a DSPL and converting 
it back to a transform is performed by the function 
{\it extrapTrsfByDspl()\/} (notice that this function returns
a transform). Since a DSPL can represent
a velocity as well as finite displacement, the equivalent to
(\ref{impossible}) can be constructed as
\[
{\bf M}(k) \;=\; {\bf M}(0) {\it extrapTrsfByDspl\/}({\bf v}, \; k)
\]
where {\bf v} is a DSPL representing a one-cycle displacement.
This can be easily computed from two successive transform
values:
\[
{\bf v} \;=\; {\it trsfToDspl\/} ({\bf M}0^{-1} \; {\bf M}1)
\]
Note that, like drive parameters, DSPLs can be easily interpolated.
DSPLs could be used in place of drive parameters, although the
two-angle interpolation used for drive parameters offers some advantages
in robot task specification.

The blending of Cartesian path segments is done in the 
manipulator T6 frame, which means that
$S_0$ and $S_1$ correspond to values of ${\bf T6}$ which
will be designated as {\bf S0} and {\bf S1}. 

We are now ready to present the path computation algorithm.

\section{First path segment computation cycle}

It is assumed that
${\bf T6}(0)$ and ${\bf v}_0$ (a DSPL representing the unit velocity in the
T6 frame) are initially available.
We first compute the value of ${\bf T6}$ towards
which we are heading, either by setting it equal to the previous motion's
T6 target (if it is constant and the previous motion
was not interrupted), or by estimating it from
\[
\widehat{\bf TB} \;=\; {\bf T6}(0) \; 
                       {\it extrapTrsfByDspl\/}({\bf v}_0, \; \tau+1)
\]
Next, an initial value drive offset is computed:
\[
{\bf O}(1) \;=\; {\bf COORD}(1)^{-1}\;\widehat{\bf TB}\;{\bf TOOL}(1)
\]
${\bf S0}(1)$ and ${\bf S0}(2)$ are then computed by extrapolation,
\[
{\bf S0}(1) \;=\; {\bf T6}(0) \; {\it extrapTrsfByDspl\/}({\bf v}_0, \; 1)
\]
\[
{\bf S0}(2) \;=\; {\bf T6}(0) \; {\it extrapTrsfByDspl\/}({\bf v}_0, \; 2)
\]
and ${\bf S0}(1)$ is used as the output setpoint:
\[
{\bf j6}(1) \;=\; \Lambda^{-1}( {\bf S0}(1) )
\]

\section{Second path segment computation cycle}

This cycle is the busiest. 
A second drive offset is computed, and then
the two offsets are extrapolated to obtain the real drive parameters
{\bf dp}:
\[
{\bf O}(2) \;=\; {\bf COORD}(2)^{-1}\;\widehat{\bf TB}\;{\bf TOOL}(2)
\]
\[
{\bf ov} \;=\; {\it trsfToDspl\/}({\bf O}(1)^{-1} {\bf O}(2))
\]
\[
{\bf dp} \;=\; {\it trsfToDrive\/}( {\bf O}(1) \;
{\it extrapTrsfByDspl\/} ({\bf ov}, \tau) \,)
\]
The values of ${\bf DRIVE}(t)$ corresponding 
to cycles 1 and 2 are then computed:
\[
{\bf DRIVE}(1) = {\it extrapTrsfByDrive\/}({\bf dp}, (t_c-1)/\sigma)
\]
\[
{\bf DRIVE}(2) = {\it extrapTrsfByDrive\/}({\bf dp}, (t_c-2)/\sigma)
\]
These are used to compute ${\bf S1}(1)$ and ${\bf S1}(2)$
and set the blend parameters:
\[
{\bf S1}(1) \;=\; {\bf COORD}(1) \; {\bf DRIVE}(1) \; {\bf TOOL}(1)^{-1}
\]
\[
{\bf S1}(2) \;=\; {\bf COORD}(2) \; {\bf DRIVE}(2) \; {\bf TOOL}(2)^{-1}
\]
\[
{\it setBlend1\/}({\bf S0}(1), {\bf S1}(1))
\]
\[
{\it setBlend2\/}({\bf S0}(2), {\bf S1}(2), 2 \tau)
\]
Lastly, the output setpoint is determined by applying the blend
function to ${\bf S1}(2)$:
\[
{\bf j6}(2) \;=\; \Lambda^{-1}(\beta({\bf S1}(2)))
\]

\section{Remaining computation cycles}

The value of ${\bf DRIVE}(t)$ is computed,
\[
{\bf DRIVE}(t) \;=\; {\it extrapTrsfByDrive\/}({\bf dp}, \, 1-s)
\]
and used to compute the output setpoint directly. The
application of the blend function $\beta$ is omitted if $t \ge t_b + \tau$:
\[
{\bf j6}(t) \;=\; \Lambda^{-1}(\beta(
{\bf COORD}(t) \; {\bf DRIVE}(t) \; {\bf TOOL}(t)^{-1}) )
\]

\chapter{Joint Coordinates}

This section presents the specific computations used by the
trajectory generator to do joint interpolated motions. 
Because joint coordinates can be treated as vector quantities,
their associated path computations follow directly from the
introductory discussion given earlier. 
Since the path segment blending is done in
joint coordinates, the values of
$S_0$ and $S_1$ correspond to values of ${\bf j6}$ which will be
designated as {\bf js0} and {\bf js1}. 

\section{First path segment computation cycle}

Assume that the initial joint
angles ${\bf j6}(0)$ and velocities ${\bf jv}(0)$ are available.
The value of ${\bf j6}$ towards
which we are heading is computed, either by setting it 
equal to the previous motion's
j6 target (if it is constant and the motion was not
interrupted), or by estimating it from
\[
\widehat{\bf jb} \;=\; {\bf j6}(0) + (\tau+1) {\bf jv}(0) 
\]
Next, the value of the motion target 
is computed in joint coordinates ({\bf jc})
and used to find an initial value for the drive offset ${\bf jo}$:
\begin{eqnarray*}
{\bf jc}(1) &\;=\;& \Lambda^{-1}({\bf COORD}(1) \; 
                             {\bf TOOL}(1)^{-1})\\
{\bf jo}(1) &\;=\;& \widehat{\bf jb} - {\bf jc}(1)
\end{eqnarray*}
${\bf js0}(1)$ and ${\bf js1}(2)$ are then computed by 
extrapolation,
\begin{eqnarray*}
{\bf js0}(1) &\;=\;& {\bf j6}(0) + {\bf jv}(0) \\
{\bf js0}(2) &\;=\;& {\bf j6}(0) + 2 {\bf jv}(0) 
\end{eqnarray*}
and ${\bf j6}(1)$ is set equal to ${\bf js0}(1)$.

\section{Second path segment computation cycle}

A second value for the drive offset is computed,
and used with the first value to
determine the drive parameters
{\bf jdp}:
\begin{eqnarray*}
{\bf jo}(2) &\;=\;& \widehat{\bf jb} - \Lambda^{-1}({\bf COORD}(2) \; 
                             {\bf TOOL}(2)^{-1})\\
{\bf jdp} &\;=\;& {\bf jo}(1) + \tau ({\bf jo}(2) - {\bf jo}(1)) 
\end{eqnarray*}
${\bf js1}(1)$ and ${\bf js1}(2)$
are then computed and used to set the blend parameters:
\[
{\bf js1}(1) \;=\; {\bf jc}(1) + \left({t_c - 1 \over \sigma}\right)
\,{\bf jd} (\tau)
\]
\[
{\bf js1}(2) \;=\; {\bf jc}(2) + \left({t_c - 2 \over \sigma}\right)
\,{\bf jd} (\tau)
\]
\[
{\it setBlend1\/}({\bf js0}(1), {\bf js1}(1))
\]
\[
{\it setBlend2\/}({\bf js0}(2), {\bf js1}(2), 2 \tau)
\]
Lastly, the output setpoint is computed by applying the blend
function $\beta$ to ${\bf js1}(2)$:
\[
{\bf j6}(2) \;=\; \beta({\bf js1}(2))
\]

\section{Remaining computation cycles}

The output setpoint is just computed directly, with the blend function
$\beta$ being omitted if $t \ge t_b + \tau$:
\[
{\bf j6}(t) \;=\; \beta(
\Lambda^{-1}({\bf COORD}(t) \; {\bf TOOL}(t)^{-1}) ) + (1-s) {\bf jdp} )
\]

\chapter{Computation Times}

How much CPU time do the trajectory computations described
here require? 
On a microVAX II system (now somewhat obsolete), 
the trajectory generator can control one PUMA robot 
quite comfortably at a 30 msec. sample rate. The trajectory
computation itself, involving closed form inverse kinematics,
several transform multiples, the application of a blend function,
and precomputation of the next motion request, 
typically requires no more than 10 msec. An additional 5 msec.
or so may be required for the extra computations during the first two 
cycles of a motion.

On a Sun4 Sparc system, one robot can be controlled easily 
at 5 msec., with the trajectory computations taking up about 1.5 msec. of this
time. Newer systems such as Silicon graphics workstations running the MIPS
R3000 CPU can do all of the trajectory computations in as little
as .5 msec.

To help achieve these speeds, most computations are done using
single precision, and the trigonometric and square root functions are computed
using linearly interpolated lookup tables with a worst case error
of about $5^{-7}$. 

\chapter{Conclusion}

The robot programming system RCCL has been demonstrated
over the last several years to be an useful platform
for developing a wide variety of robot control applications.
The trajectory generation technique described here, which
uses first order velocity extrapolation combined with path blending,
has been shown to be effective in handling various situations where
the robot's path is subject to on-line modification from
a wide range of stimuli. Examples of these stimuli include operator
control inputs from joysticks or hand-controllers, readings
from force/torque sensors used to implement impedance control,
or various types of visual and range-finding information used
to implement tracking.

\newpage

\begin{thebibliography}{}
%\chapter*{References}\bigskip
%
%\parskip = 4ptplus2pt
%\baselineskip = 14pt
%\def\ref #1{\par
%\noindent {\bf #1}\quad\hangindent=2\parindent\hangafter=1\relax}

\bibitem{Allen91}
Peter K. Allen, Billibon Yoshimi, and Aleksander Timcenko,
``Real-time visual servoing''.
{\it IEEE International Conference on Robotics and Automation},
Sacramento, California, April 9-11, 1991, pp. 851 -- 856.

\bibitem{Bobrow85}
J. E. Bobrow, S. Dubowsky and J. S. Gibson,
``Time Optimal Control of Robotic Manipulators along a Specified Path''.
{\it International Journal of Robotics Research},
Fall 1985, pp. 3 -- 17.
(Vol. 4, No. 3)

\bibitem{Hayati89}
Samad Hayati, Tom Lee, Ed Kahn, and John Lloyd,
``The JPL Telerobot Manipulator Control and Mechanization Sub-system''.
{\it NASA Conference on Space Telerobotics}, Pasadena, Ca., 
January 31 - February 2, 1989.

\bibitem{Hayati90}
Samad Hayati, Tom Lee, Kam Tso, Paul Backes, and John Lloyd,
``A Testbed for a Unified Teleoperated-Autonomous Dual-Arm Robotic System''.
{\it IEEE International Conference on Robotics and Automation},
Cincinnati, Ohio, May 13-18, 1990.

\bibitem{Hayward86}
Vincent Hayward and Richard Paul,
``Robot Manipulator Control Under UNIX: RCCL, a Robot Control C Library''.
{\it International Journal of Robotics Research},
Winter 1986, pp. 94 -- 111.
(Vol. 5, No. 4)

\bibitem{Hayward88}
Vincent Hayward, Laeeque Daneshmend, Ajit Nilakantan,
``Model Based Trajectory Planning Using Preview''.
Report TR-CIM-88-9,
Research Center for Intelligent Machines,
McGill University, Montreal, March 1988.

\bibitem{Castano92}
A. Castano A. and S. A. Hutchinson,
``Hybrid Vision/Position Servo Control of a Robotic Manipulator''.
To be presented at the
{\it IEEE International Conference on Robotics and Automation},
Nice, France, May 1992.

\bibitem{Fagg91}
Andrew H. Fagg, M. Anthony Lewis, Thea Iberall, and George A. Bekey,
``$R^2AD$: Rapid Robotics Application Development Environment''.
{\it IEEE International Conference on Robotics and Automation},
Sacramento, California, April 9-11, 1991, pp. 1420 -- 1426.

\bibitem{Kan90}
Edwin P. Kan, Jin Lee, and Michel Junod,
``An integrated FRHC-PUMA-EE Teleautonomous System''.
{\it 3rd International Symposium on Robotics and
Manufacturing},
Vancouver, B.C., Canada, July 18-20, 1990, pp. 397 -- 404.

\bibitem{Lee86}
Jin S. Lee, Samad Hayati, Vincent Hayward, and John Lloyd,
``Implementation of RCCL, a Robot Control C Library, on a 
microVAX II''. {\it Advances in Intelligent Robotics Systems},
SPIE's Cambridge Symposium on Optical
and Optoelectronic Engineering, Cambridge, Mass.,
26-31 October, 1986.

\bibitem{Lloyd90}
John Lloyd, Mike Parker, and Gaylord Holder,
``Real Time Control Under UNIX for RCCL''.
{\it 3rd International Symposium on Robotics and
Manufacturing},
Vancouver, B.C., Canada, July 18-20, 1990, pp. 237 -- 242.

\bibitem{Lloyd89}
John Lloyd and Vincent Hayward,
``Multi-RCCL User's Guide''.
Research Center for Intelligent Machines,
McGill University, Montreal, November 1989.

\bibitem{Lloyd88}
John Lloyd, Mike Parker, and Rick McClain,
``Extending the RCCL Programming Environment to Multiple
Robots and Processors''. 
{\it IEEE Conference on Robotics and Automation}, 
Philadelphia, Pa., April 24-29, 1988, pp. 465 -- 469.

\bibitem{Paul81}
Richard P. Paul,
{\it Robot Manipulators: Mathematics, Programming, and Control}. 
MIT Press, Cambridge, Mass., 
1981

\bibitem{ShinMcKay86}
Kang G. Shin and Neil D. Mckay,
``A Dynamic Programming Approach to Trajectory Planning of Robotic
Manipulators''.
{\it IEEE Transactions on Automatic Control}, 
June 1986, pp. 491 -- 500.
(Vol. AC-31, No. 6)

\bibitem{Shoemake85}
Ken Shoemake, 
``Animating Rotation with Quaternion Curves''. 
{\it Computer Graphics} (SIGGRAPH '85 Conference Proceedings), 
San Fransisco, California, July 22 -- 26, 1985, pp. 245 -- 254.
(Vol. 19, No. 3)

\bibitem{Taylor79}
R.H. Taylor, 
``Planning and Execution of Straight Line Manipulator Trajectories''. 
{\it IBM Journal of Research and Development},
July 1979, pp. 253 -- 264. 
(Vol. 23, No. 4)

\bibitem{Whaite91}
Whaite, P. and Ferrie, F.P.,
``From Uncertainty to Visual Exploration''.
{\it IEEE Transactions on Pattern Recognition and 
Machine Intelligence},
October, 1991, pp. 1038 -- 1050. (Vol. PAMI-13, No. 10)

\end{thebibliography}
\end{document}
\bye
