This web page is a collection of email messages
from Bill Gribble
and others concerning the machines in the UTCS Robot Lab and
use of the wheelchair and other robots.
Return-Path:
Sender: grib@cs.utexas.edu
To: "Micheal S. Hewett"
Subject: Re: Significant changes to SPLAT in CVS
References: <87d7tbk6g9.fsf@flophouse.localnet> <38306FC9.15361C40@cs.utexas.edu>
From: Bill Gribble
Date: 15 Nov 1999 14:53:06 -0600
You need to tell CVS to use ssh, not rsh (rsh is such a huge security hole
that we uninstalled it):
export CVS_RSH=ssh
cvs update -Pd
b.g.
Return-Path:
Sender: grib@cs.utexas.edu
To: robotics@cs.utexas.edu
Subject: Bugs in knowledge base fixed
From: Bill Gribble
Date: 01 Dec 1999 09:51:19 -0600
[...]
We've upgraded the compiler recently, so you may need to 'make clean'
before building to avoid complaints about missing system header files.
b.g.
Return-Path:
Sender: grib@cs.utexas.edu
To: robotics@cs.utexas.edu
Subject: flat-interface & midline follower for splat in CVS.
From: Bill Gribble
Date: 14 Nov 1999 13:29:37 -0600
Getting the code
----------------
You need to have SSH set up so that you can log into argus without a
password. If you don't know how to do this, send me email.
If you already have an argus source tree checked out:
$ cd ${argus_top_level}
$ cvs update -Pd
If you don't have an argus source tree checked out:
$ export CVSROOT=argus.csres.utexas.edu:/usr/src/CVS
$ cvs checkout argus:all
Building the code
-----------------
You need to build a couple of servers to run SPLAT. It's not too
hard. This should work without a hitch on lab machines, but we
require fairly up-to-date versions of g++ (2.95.2 or better) and we
use threads heavily so I don't know how well it will compile on CS
machines.
1. Edit ${argus_top_level}/Makefile.defs
There's a line near the top that should say
SOURCE_ROOT=/home/grib/devel/argus
Change that to be the top level of your argus source directory
(the directory that Makefile.defs is in).
2. Build the 'kbase', 'dobjects', and 'corelib' modules.
$ cd kbase
$ make all
corelib and dobjects will get built automatically (there's a
module dependency system).
Running the code
----------------
I have provided a file called test-flat.scm in the splat/ directory
which shows off the midline following control law.
1. Run flat and the java display. Mike has good instructions for
this on his web pages. note the port number that Flat is
listening for connections on (it's usually 1069).
2. Run the distributed object name-server/router (I suggest you
open a new xterm for this):
$ cd ${argus_top_level}/dobjects
$ ./switchboard --port 14000
3. Run a knowledge base server (I suggest a new xterm for this also).
$ cd ${argus_top_level}/kbase
$ ./kb-server --swb localhost:14000
If you are running the kb-server on a machine different from
the one you ran switchboard on, substitute the switchboard host name
for 'localhost' in the --swb argument to kb-server.
If you wish to load a default knowledge base, use the argument
--kb to load it.
4. Launch rscheme and load the test-flat file.
$ cd ${argus_top_level}/splat
$ ../corelib/argusrs.devel test-flat.scm
You'll see it loading a bunch of support files, compiling several
SPLATs, and doing some other stuff.
5. Go for it.
top[1]=> (test-flat-midline-following )
for example,
top[1]=> (test-flat-midline-following 0.50
"localhost:14000"
"localhost:1069")
NOTES:
------
*******************************************************************
The wheelchair's coordinate system is different than the one used
natively in Flat!
*******************************************************************
I handle the translations from flat's coordinates automatically in the
class, but when you are writing new control laws you
MUST USE THE NEW COORDINATE SYSTEM, because you're talking to a level
of interface that only knows wheelchair coordinates.
It is:
- positive X axis along the robot's direction of travel
- positive Y axis sticking out the robot's left ear
- positive angles counterclockwise when viewed from above
- displacements in METERS, velocities METERS/SEC, angles RADIANS,
angular velocities RADIANS/SEC.
The midline-following controller is in the file follow-hallway.scm.
It implements a "little red wagon" controller as we discussed in the
meeting last week. Right now, its termination conditions are
rudimentary and there's no safety-expert or other sanity checker.
Nevertheless it works pretty well. I'll be modifying it more in the
next few days.
The flat client interface is in flat-client.scm. and
are subclasses of , and their
interfaces are the same, so if you substituted a
(make-wheelchair-client) for the (make-flat-client) in test-flat.scm
you would be controlling the wheelchair with the same splat.
Enjoy,
Bill Gribble
Return-Path:
Sender: grib@cs.utexas.edu
To: "Micheal S. Hewett"
Subject: Re: argus compile errors
From: Bill Gribble
Don't [compile] on argus. You are bound to be in some pain. You
should never log into argus or janus... they has about as much power
as a pocket calculator and are just being used as CVS server and
firewall.
The problem on shadow is that compile tools aren't synced as they
should be with the rest of the machines. We use automatic dependency
generation (look for files called foo.d), which generates references
to system and local header files. Since your home dir shows up on
multiple machines, if the paths to system header files aren't the same
(they aren't if the compiler version is different) you have problems.
Try doing a 'make clean' to get rid of the .d files, and start over.
Compiling this stuff takes a long time. We have tried to streamline
it as much as possible, but plan for a build from scratch of corelib,
dobjects, and kbase to take 10-15 minutes on the fast machines.
Please try again [...], after a 'make clean', on glare or eclipse
and let me know if it still breaks.
b.g.
Return-Path:
Sender: grib@cs.utexas.edu
Subject: Generating splats from planner output
From: Bill Gribble
Date: 23 Apr 1999 12:11:53 -0500
Emilio,
After our conversation Wednesday I've written a sample of what I think
planner output might look like given the architecture we discussed.
It includes a number of new splat primitives that aren't implemented
yet, but aren't too difficult.
The basic concept is that the LISP process starts the SPLAT scheme
process via whatever the Common LISP system() equivalent is, and keeps
an input-port and an output-port corresponding to the Scheme process'
standard output and standard input, respectively. The Lisp process
prints Scheme forms to the Scheme process, and the Scheme process
prints Lisp forms back to the Lisp process. The Lisp process
needs to periodically read from that port, process the input, and
perhaps write a response.
The lisp->scheme interface is going straight to the Scheme reader,
so you can send any valid Scheme form and do what you want with the
output. There is a simple API for doing the main function (creating
and running plans) which will have predictable output and will be
easier to deal with than if you send arbitrary scheme code.
(make-plan-splat )
Special form which does the following:
- create a single-method splat (the method consisting of )
with no preconditions or fancy stuff, ready to run.
- associates with the splat for future input/output tagging.
can be a symbol, a number, anything that can be an association
list tag.
(start-plan-splat )
starts the specified splat running.
(abort-plan-splat )
causes the splat to exit immediately and return .
When significant things happen, the splat can spit out forms on
its output-port. These will be valid lisp lists, which the lisp
reader should read and process as it wishes. There are two main
kinds of output that the splat side can generate: splat-status-report
and splat-replan-request. splat-status-report forms come periodically
to let the lisp side know what's going on in the plan. They can be
safely ignored, except that you want to keep looking for a
'completed-task report so you know you can go on to the next
thing.
(splat-status-report 'in-progress)
(splat-status-report 'completed-subtask 1)
(splat-status-report 'completed-task #t) ; for success
(splat-status-report 'completed-task #f) ; for failure
When the splat needs a new plan, it can ask for one:
(splat-replan-request )
where is a string that contains whatever information the LISP
planner needs to figure out what plan to generate. Since the planner
generates the code which asks for the replan, it can embed its own
internal representation of the task or goal in the string. In the
following example, (replan-and-resume) is a special form which
generates the splat-replan-request with the string ":go :here
taylor-outside-door" as the . It will expand to the
splat that is generated by the planner.
Here is what should be sent by the planner to the Scheme reader to
instantiate a plan for traveling from the robot lab out of Taylor
Hall. The (reference-shared-object ) forms create data
connections to the shared knowledge base that we discussed. The
path specifiers are arbitrary, but I conceived of it as being
knowledge-base::context::identifier
I discussed with Ben how the Algernon knowledge base could be taught
to use the shared knowledge base for a subset of its knowledge (those
things which are changed or used by the Splat engine); I'll send
another email with a summary of that discussion.
(simple-splat
(let ((elevator
(reference-shared-object "objects::taylor-hall::4th-floor-elevator"))
(door
(reference-shared-object "objects::taylor-hall::robot-lab-door"))
(hallway
(reference-shared-object "paths::taylor-hall::4th-floor-hallway")))
(sequence-actions
(go-thru-doorway
;; door to go through
door
;; meters after door to stop
1.0)
(follow-hallway-until-near-object
;; which hallway
hallway
;; which direction
'positive
;; which object
elevator
;; how near when it's time to stop (meters)
4.0)
(enter-elevator elevator)
(travel-in-elevator elevator 'down 2)
(replan-and-resume
":GO :HERE TAYLOR-OUTSIDE-DOOR"))))
The plan primitives in use are
go-thru-doorway
Visually locate the specified door, trave through it, and stop when
you are meters past the center of the door.
follow-hallway-until-near-object
Return-Path:
Sender: grib@cs.utexas.edu
To: robotics@cs.utexas.edu
Subject: Significant changes to SPLAT in CVS
From: Bill Gribble
Date: 15 Nov 1999 11:31:02 -0600
- Removed a bunch of archaic files from the archive to avoid confusion
when you're poking around the splat code. use 'cvs update -Pd' and
remove any files that CVS warns you are "no longer pertinent".
- Changed follow-hallway control law a bit. Made the "wagon handle"
in the little-red-wagon controller proportionate in length to the
robot's travel speed. This is based on the "highway driving insight":
the faster one drives on the highway, the farther ahead one looks to
adjust steering.
- If you want to write test SPLATs for flat, there are a couple of
working rangefinder-based and open-loop control laws:
(follow-hallway-to-end robot travel-speed)
Get this by adding (depend 'follow-hallway) to your file.
(turn-in-place robot angle speed grab-mutex?)
Get this by adding (depend 'turn-in-place) to the top of your
scheme file. Pass #t to grab-mutex if you have not acquired access
to the robot's controller before calling turn-in-place. You get the
mutex by calling
(semaphore-wait (base-control-mutex robot))
See follow-hallway.scm for an example.
(drive-distance/speed robot distance speed grab-mutex?)
This is an open-loop drive command. No sensors at all (except
odometry) You can get it by (depend 'follow-hallway) also.
- Changed/added SPLAT syntax:
(parallel-actions ...) has been renamed to
(parallel-actions/all ...), and a new syntax form
(parallel-actions/any ...) has been added.
(parallel-actions/all
(wait-a-while 2)
(wait-a-while 4)
(wait-a-while 6))
will take 6 seconds to complete;
(parallel-actions/any
(wait-a-while 2)
(wait-a-while 4)
(wait-a-while 6))
will take 2.
Of course
(sequence-actions
(wait-a-while 2)
(wait-a-while 4)
(wait-a-while 6))
will take 12 seconds.
New syntax form (with-predicate-true ) has been added
at Emilio's request:
(with-predicate-true
(predicate/vars
)
)
Evaluates the splat , terminating if the predicate
defined in ever becomes false. is evaluated
at startup, and any time the value of a fluent in
changes.
Example:
(let ((continue? (make-splat-fluent #t eq?)))
(with-predicate-true
(predicate/vars (continue?)
(get-fluent continue))
(parallel-actions/all
(wait-a-while 10)
(sequence-actions
(wait-a-while 5)
(set-fluent! continue? #f)))))
will run 5 seconds and then be terminated when continue? is set to
#f.
A similar syntax form, (repeat-until-true ), has
been around a while but isn't documented:
(repeat-until-true
(predicate/vars
)
)
Repeatedly evaluates the splat until the predicate
defined in becomes true. is evaluated at startup,
and any time the value of a fluent in changes.
Example:
(let ((loop-count (make-splat-fluent 0 eq?)))
(repeat-until-true
(predicate/vars (loop-count)
(eq? (get-fluent loop-count) 10))
(sequence-actions
(wait-a-while 1)
(set-fluent! loop-count (+ 1 (get-fluent loop-count))))))
will take 10 seconds to run.
- Lots of bug fixes.
b.g.
Return-Path:
Sender: grib@cs.utexas.edu
To: robotics@cs.utexas.edu
Subject: Added get-current-ranges-right/get-current-ranges-left to
From: Bill Gribble
Date: 15 Nov 1999 12:49:48 -0600
At Emilio's request, I added functions to get the current rangefinder
readings. These are available on both and
.
(get-current-ranges-right )
(get-current-ranges-left )
b.g.
From: Mike Hewett
Date: Tue, 28 Dec 1999 15:03:22 -0600
The maximum bounding box, with all the wheels splayed out is
1.3081 x 0.847725 (meters)
We also need the location of the origin of the
wheelchair reference frame in this box. As near
as I can determine, it is located as shown in
the diagram below:
(front)
0.847725
____________________________
| |
| |
| |
| |
| |
| |
| |
| 0.4238625 |
| ........... X |
| . | 1.3081
| . |
| . |
| . |
| . |
| . |
| . |
| . 0.6604 |
| . |
| . |
| . |
| . |
| . |
____________________________
I don't know what it should be called. How about
something like:
Tinman::bounds
width 0.847725
height 1.3081
front 0.6477
back 0.6604
left 0.4238625
right 0.4238625
If the laser rangefinders are moved, their locations and angles
relative to the robot need to be determined and added to the
knowledge base. Below is one way to do it. Note that the
wheelchair's coordinate system has its origin at the central
pivot point of the wheelchair, its X-axis projects forward
and its Y-axis projects to the left.
It is important for these measurements to be very accurate.
We need about two decimals of precision in the angular measurements.
Using tape or chalk, outline a rectangle on the floor to contain the
wheelchair. The rectangle's bounds are
described above. Include a vertical line from the front of
the wheelchair to the center. You need at least a few meters of
space in front of the wheelchair. Make sure you know whether the
actual boundary is the front, center or back of the line.
Position the wheelchair in the rectangle.
Using a string with a weight on it, mark a spot on the floor directly
under the central axis of each rangefinder.
Position a couple of thin objects a couple of meters in front
of the wheelchair. Each one should be on a line from the
wheelchair's origin through one of the rangefinder locations.
Use a long string to position the objects.
Take a scan from each rangefinder to locate the object in front
of it. The rangefinder reading has an (r, theta) value. Convert
the reading into a coordinate system where zero degrees is straight
forward from the rangefinder and angles increase to the left.
Move the wheelchair and, using the marks on the floor, find the
location of each rangefinder, as (x, y) and (r, theta) in the
wheelchair's coordinate system.
The rangefinder's angle relative to the wheelchair's coordinate
system is (theta(rangefinder) - theta(reading)).
Store these values in the kbase (argus/kbase/robot-lab-kb.dump)
in the variable 'calibration::laser-rangefinders':
l-scanner-dx (meters)
l-scanner-dy (meters)
l-scanner-theta (radians)
r-scanner-dx (meters)
r-scanner-dy (meters)
r-scanner-theta (radians)
Make a sanity check on the values:
l-scanner-dx (> 0, about 0.4)
l-scanner-dy (> 0, about 0.2)
l-scanner-theta (about pi/4, or 0.77)
r-scanner-dx (> 0, about 0.4)
r-scanner-dy (< 0, about -0.2)
r-scanner-theta (about pi/4, or 0.77)
Check in the revised kbase.
If these values are incorrect, the robot will not be able to follow
the wall at a specific set-point. It will be off by a few hundred millimeters.
Also, the Flat laser display will have a parallax effect or a slight rotation if
the rangefinder angles in the .flat file do not correspond to the actual angles.
Mike Hewett
13 Aug 2000
Subject: notes on laser segment display
Date: 22 Dec 1999 14:29:00 -0600
From: Bill Gribble
To: robotics@cs.utexas.edu
Since extract-min-max-segments can be called with a variety of arguments,
I had to pick a set of arguments to use when calling it in the dashboard
program. Which is to say that the line segments being extracted may not
be the ones that you are seeing if you are using different arguments than
dashboard does.
Dashboard gets its arguments from the knowledge base so that you can tune
the segment extraction parameters by hand while watching the display
interactively. The frame that holds the arguments used is called
runtime-state::display::segment-params. Below is a printout of
the frame showing what the default values are and what the names of the
slots are.
vulcan> ./kb-tool --swb localhost:14000 print-frame runtime-state::display::segment-params
Frame runtime-state::display::segment-params
k-colinear-range ==> 0.10
k-colinear-theta ==> 0.05
k-discontinuity-thresh ==> 0.10
k-squid-std-dev ==> 0.02
k-squid-winsize ==> 4
Use kb-tool to assign new values to the slots and the new values will
be used the next time the dashboard display is updated.
b.g.
Subject: new rangefinder interface for segments
Date: 21 Dec 1999 14:39:47 -0600
From: Bill Gribble
To: robotics@cs.utexas.edu
I've finished porting the segment-extraction code to run in C++. It
works on test data, but I need to test it on the real rangefinders
before handing it over to you. I just want to let you know what the
interface is so you can start modifying your code.
The wheelchair-client (and flat-client after this afternoon) have a new
method called "extract-min-max-segments". Its arguments are:
(extract-min-max-segments
min-angle ;; start of the window to examine (radians, 0 is ahead)
max-angle ;; end of window (both are in wheelchair frame of reference)
k-discontinuity-thresh ;; threshold range-diff (in meters)
;; for discontinuity betw. adjacent pts
k-squid-window-size ;; window size in samples for squid binning
k-squid-window-std-dev ;; std deviation for qualitative slope
;; (slope mag. < std dev are UNKNOWN)
k-colinear-range-thresh ;; threshold of range difference for two
;; segments to be combined as colinear
k-colinear-theta-thresh ;; threshold of segment angle for colinear
;; segment combination
)
It returns a vector of , where
(class ()
(seg-range) ;; range in meters to intersection of fit line and
;; perpendicular segment from origin
(seg-theta) ;; orientation of segment (in radians) (0 is straight ahead)
(seg-sigma) ;; standard deviation of samples in segment window
(p-min) ;; a representing the minimum-theta end of the
;; segment
(p-max) ;; a representing the maximum-theta end of the
;; segment
)
In case it's not clear, the segments are represented by the r-theta
representation of the line that best fits the segment data (using a
least-squares approximation) and the two endpoints of the segment.
Two colinear segments will have the same r and theta, but different
endpoints, so merging colinear segments (which is done automatically)
is just a matter of comparing the r and theta of the extracted
segments.
I'll send another email when this stuff is checked in to CVS.
b.g.
Return-Path:
Sender: grib@cs.utexas.edu
To: "Micheal S. Hewett"
Subject: Re: Wheelchair stuff
From: Bill Gribble
Date: 30 Nov 1999 07:32:19 -0600
"Micheal S. Hewett" writes:
> Emilio and I couldn't run the dashboard today because
> the system couldn't find libimage and libvideo. Maybe
> he needed to update his argus files...
This is a symptom of Coda getting full. I'll reset it.
[...]
b.g.
> Also, I pushed the Reset button on the Tupperware box
> once in order to stop the wheelchair and after that
> we could never get the wheelchair to move, although all
> the sensor data was coming through. How do we recover
> from pushing Reset?
Never push the reset button. It's a pain to get back to a running
state. I'll show you how to do it at the meeting.
The 100% sure way to stop the wheelchair is to (1) turn the
green button off, then (2) kill the tinman-server process.
Return-Path:
From: Bill Gribble
To: robotics@cs.utexas.edu
Subject: loading software on the tupperware box.
Message-Id:
Date: Tue, 30 Nov 1999 14:31:49 -0600
In case anyone should ever happen to accidentally reset the tupperware
box, or if (as has happened sporadically in the past) the tupperware
should stop updating its odometry after it's been up for several days,
here's how to reload the software.
1. Log in to vulcan.
2. Look in the argus/robots/tinman directory. You need a downloadable
object file called 'tinman-onboard-server'. If it doesn't exist,
build it. You have to put the Tupperware compiler in your path
first:
$ export PATH=${PATH}:/usr/local/arc/linuxbin
$ cd ..
$ make tinman-onboard-server
$ cd tinman
3. Reset the Tupperware to its factory default program. You have done
this if the green LEDs marked "led/tpu0" thru "led/tpu3" are
flashing sequentially in a pattern best described as "Cylon eye"
(cf. Battlestar Galactica). If you don't see this pattern, press
the red Reset button. If that doesn't work, turn the "computer"
and "sensor" switches off (in that order) and back on (in the
reverse order), then press the Reset button.
3. Launch "arc", the tinman interface program. It takes one argument,
the serial port to use (the Tupperware box is connected to
/dev/ttyS1). NOTE: arc screws up the terminal interface something
terrible, so you always want to run it exactly as follows so that
your terminal gets reset automatically:
$ export PATH=${PATH}:/usr/local/arc/linuxbin
$ arc -port /dev/ttyS1 ; stty sane
4. Now you're in a shell talking to the tinman. type "ps" to see a list
of the programs that get run by default (a very dumb program to
avoid obstacles using the sonars). You need to load the new onboard
server that we use:
arc> ramload tinman-onboard-server
5. Run the onboard server. This is the tricky part.
You have to type two commands to "arc":
arc> run tinman-onboard-server
arc> quit
The trick is that the time between hitting "return" on the first
command and hitting "return" on the second command has to be within
a narrow window of opportunity. My estimate is that 2.5 sec should
elapse between hitting "return" on the run command and "return" on
the quit command.
6. Make sure it worked: if you got the timing right, your "arc" session
should be quit and back to the unix shell, with no weird characters
printed to the display. If you look at the LEDs on the front of the
Tupperware box, there should no longer be the "cylon eye" pattern,
"led/tpu0" should be blinking at about 1 Hz, and the "COM 1" LED in
the upper left corner of the Tupp should be flashing green fairly
rapidly.
Good luck! Emilio has seen this process, so you can also ask him if
it's too confusing.
b.g.
Return-Path:
Sender: grib@cs.utexas.edu
To: Emilio Remolina
Subject: medusa & log-stream info
From: Bill Gribble
Date: 01 Dec 1999 22:34:36 -0600
> 1) I want to use your log-server. What do I have to do?
To make log entries:
1. create a object. The first arg is always
"LogStream", the second arg is the name of the log. Multiple
clients can connect to the same log; their entries are sequenced
and timestamped correctly.
2. Call (-make-entry logger "string to log" timestamp) to
make a log entry.
3. Call -rotate-log to create a new "volume"
of log entries. The idea is that if you have multiple data
sets you call rotate-log between sets, and the log server
stores each data set separately.
EXAMPLE:
(depend 'LogSourceClient)
(let ((logger (make- "LogStream" "test log")))
(-make-entry logger "first entry" (time))
(-make-entry logger "second entry" (time))
(-rotate-log logger)
(-make-entry logger "entry in new volume" (time)))
To save log entries to a file:
With the log server still running, use the program mathtools/log-tool.
'log-tool list --swb localhost:14000' will show you a summary of
all the stuff in the log server. 'log-tool' will show you a summary
of the things the log tool can do. 'log-tool dump-all --swb localhost:14000'
will dump every volume of every log to separate files.
If you're worried about something crashing before you get the data saved
to disk, you can call
(-dump-current-volume logger "filename").
The volume number gets appended to the filename so you get a set of files
if you periodically rotate the logs.
Format of files:
timestamp sequence# entry
timestamp is a floating point value representing time in seconds (to a
precision of microseconds) since Jan 1 1970.
sequence# is the number of this entry in this volume of this log
(the first entry in any volume is sequence #0).
The entry runs until the end of the line.
It is best to do each of these in a separate Xterm, i.e.
start 8 Xterms using:
rxvt &
(in the xterm) ssh -X vulcan
cd $ARGUS
and then start a program in each window
dobjects/switchboard --port 14000
kbase/kb-server --swb localhost:14000 --kb kbase/robot-lab-kb.dump
mathtools/log-server --swb localhost:14000
servers/sick-server 10 --swb localhost:14000
wait for the sensors to synchronize. Restart if necessary.
servers/tinman-server --swb localhost:14000
servers/frame-server --rate 10 --swb localhost:14000
servers/dashboard-server --servicedb localhost:14000
servers/ptu-server --swb localhost:14000
If you have problems with backspace/erase on the lab
machines, do 'stty erase '^?'.
Do this exactly in the order shown. To put the
wheelchair back on wall power, do the steps in
the reverse order.
Check the Ethernet cable going into the computer
on the back of the wheelchair. If necessary, remove
the cable going from the wall and replace with the
cable from the wireless Ethernet box.
Look under the front center of the wheelchair.
There is a large power converter near the bottom.
On the left side is a metal switch. Switch it ON (UP).
On top of the power converter is a smaller black box.
On top is a metal switch. Switch it to the LEFT.
(The brown plastic switch in front controls power to
the sensors. Normally you don't change its position.)
The power strip on the wheelchair is plugged into the wall
current. Unplug it and put the cord somewhere on the wheelchair
out of the way.
Unplug the battery charger from the wheelchair. This is
a round plug on the right rear side of the wheelchair.
The plug is partially blocked by the computer, so be careful
when maneuvering it.
Return-Path:
To: Bill Gribble
Cc: eremolin@cs.utexas.edu
Subject: Re: Sockets in Scheme
From: Rob Browning
Date: 14 Nov 1999 14:58:03 -0600
Emilio's request jogged my memory and I went back and looked at that
stuff. So that in the future people don't have to go around reading
raw sgml and hunting in the source, I converted/wrote up some text
docs for the relevant functions.
[function] (inet-server (port )) => (fd )
This procedure establishes a socket on port to listen for connections
from the internet. The listen queue is set to 3.
[function] (make-service (fd )) => (service )
This procedure arranges to listen for connections on fd, which was
presumably created using something like inet-server. Use
get-next-client to dequeue connecting clients.
[function] (get-next-client (service )) => (fd )
This procedure extracts the next client connecting to service. It
returns the socket, fd, and the peer socket name.
[function] (inet-client (host ) (port )) => (fd )
This procedure establishes a TCP/IP (stream) connection to the the
given port on the given host. The host may be specified as either a
or an . If host{/Phrase} is a , it is
resolved using string->hostaddr.
[function] (string->hostaddr (spec )) => (host . If the first
character of the string is a digit, then the string is interpreted as
a dotted IP address (e.g., 204.71.200.72). Otherwise, it is
interpreted as a host name and resolved using gethostbyname.
If the host name cannot be found, or it has no address, or if the
resolver library encounters some other error, a
condition is signalled.
[function] (remote-port-owner (fd )) => (type ) (info )
This procedure contacts the ident server on the machine which is the
peer of the given socket. If a recognizable ident response is
returned, the response type and additional info are returned as two
values.
(define svc (make-service (inet-server 5050)))
(remote-port-owner (get-next-client svc))
"USERID"
"UNIX : dkolbly"{/ComputerOutput}
If the peer host is not running an ident server, this procedure
signals an error (an , "Connection refused").
[function] (open-mbox-input-port (fd )) => (port )
This procedure creates a safe wrapper around the given fd, allowing it
to be used as a standard input-port. This wrapper is thread-aware, so
all IO will be thread-blocking rather than process blocking. AFAIK it
is not thread-safe. If you have multiple threads using the port, you
will need suitable synchornization mechanisms.
[function] (open-queued-output (fd )) => (port )
This procedure creates a safe wrapper around the given fd, allowing it
to be used as a standard output-port. This wrapper is thread-aware, so
all IO will be thread-blocking rather than process blocking. AFAIK it
is not thread-safe. If you have multiple threads using the port, you
will need suitable synchronization mechanisms.
[function] (flush-output-port (port )) =>
Flush the output port. You may need this when using the above
server/client functions (among other times) in order to guarantee
timely responses.
--
Rob Browning PGP=E80E0D04F521A094 532B97F5D64E3930
Return-Path:
Sender: grib@cs.utexas.edu
To: robotics@cs.utexas.edu
Subject: Forgot one change
From: Bill Gribble
Date: 15 Nov 1999 12:11:05 -0600
- Added support for Common LISP interface via TCP. To use it,
add the following to an RScheme file:
(depend 'lisp-interface)
(start-lisp-interface portno)
where "portno" is an integer specifying the TSP port to listen on.
Connecting to this port gives you a simple read-eval-print interface to
the calling RScheme process. You can test it by using
"telnet localhost " from a shell, or making a TCP port
connection in Lisp.
b.g.
Return-Path:
Sender: grib@cs.utexas.edu
To: hewett@cs.utexas.edu
Subject: test-lisp.scm
From: Bill Gribble
Date: 16 Nov 1999 11:49:38 -0600
(depend 'lisp-interface)
(depend 'shared-frame)
(define (start-server swb port)
(service-db-set-server swb)
(start-lisp-interface port))
;; example: (start-server "localhost:14000" 12345)
Return-Path:
To: hewett@cs.utexas.edu, eremolin@cs.utexas.edu
Subject: interface to knowledgebase
From: Bill Gribble
Date: 13 Oct 1999 10:53:35 -0500
I think the easiest thing to do is just use a remote REPL accessible
through a TCP connection, and you pass scheme forms to get information
from the KB. So you would just pass it something like
(define frame-1 (make-shared-frame "lfor::robot-lab"))
(get-slot frame-1 "outgoing-links")
I'm including the Scheme definition of the KB interface. The class is
called . The object-id parameter is just a string;
I've been using a naming convention which uses :: and tries to break
the KB namespace up into logical chunks. The prefixes I'm using now
are
calibration
calibration::video (frame) (video card params like brightness)
calibration::turn/drive (frame) (calibration of low-level control)
calibration::camera (frame) (calibration of imaging geometry)
calibration::colors
calibration::colors::red (frame)
calibration::colors::blue (frame)
etc.
runtime-state
runtime-state::robot
runtime-state::robot::configuration (frame) (position,pan/tilt pos,etc)
lfor
lfor::robot-lab (frame)
objects
I think there's more stuff but that gives you the idea.
This code's a little over-complicated because I'm planning to implement
knowledgebase server push sometime pretty soon.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; shared-frame.scm
;;; represents a frame in the shared knowledge base
;;;
;;; CVS Identifier:
;;; $Id: shared-frame.scm,v 1.4 1999/09/23 22:03:35 grib Exp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(depend 'KBPointerClient)
(depend 'KBStorageClient)
(support 'shared-frame)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; class ... wraps up a KBPointer and lets you
;;; do stuff like waiting for a slot to change, waiting for the
;;; frame to change, etc. Server push not yet implemented so
;;; the second kbpointerclient is useless.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-class ()
(object-id init-value: #f init-keyword: #f)
(kb-rw-pointer init-value: #f init-keyword: #f)
(kb-wait-pointer init-value: #f init-keyword: #f))
(define (make-shared-frame object-id)
(let ((rval (make )))
(set-kb-rw-pointer! rval
(make- "KnowledgeBase" object-id))
(set-kb-wait-pointer! rval
(make- "KnowledgeBase"
object-id))
rval))
(define (get-slot (self ) (slot ))
(with-input-from-string
(-get-slot (kb-rw-pointer self) slot)
(lambda ()
(read))))
(define (set-slot! (self ) (slot ) value)
(-set-slot (kb-rw-pointer self) slot
(with-output-to-string
(lambda ()
(display value)))))
(define (ref-object (self ) (object-id ))
(-ref-object self string))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; class ... wraps up KBStorageClient.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-class ()
(client init-value: #f init-keyword: #f))
(define (make-kb-storage)
(let ((rval (make )))
(set-client! rval
(make- "KBStorage"))
rval))
(define (dump-kb (self ) filename)
(-dump-kb (client self) filename))
(define (restore-kb (self ) filename)
(-restore-kb (client self) filename))
Return-Path:
Sender: grib@cs.utexas.edu
To: "Micheal S. Hewett"
Subject: Re: Your KB connection
From: Bill Gribble
Date: 05 Nov 1999 18:11:22 -0600
This interface will work fine with a couple of name changes.
frame-p ==> shared-frame?
slots-of-frame ==> slots
The argument of make-shared-frame is a string:
(define my-frame (make-shared-frame "my-frame"))
Be aware that shared-frame? probably has different semantics than you
expect: (shared-frame? foo) returns true if the symbol foo points to
an object of type . I am guessing you meant it to be
"does frame exist in the KB?" and there's currently no way to
do that. Right now, make-shared-frame ends up automatically creating
the frame if it doesn't exist. Is it important to have the predicate?
Also a couple of synchronization primitives. set-slot! and get-slot
are guaranteed atomic, but if you need synchronized multiple-slot
access you have to lock and unlock the frame:
(acquire-read )
(release-read )
(acquire-write )
(release-write )
If it's not obvious from the names, synchronization is "multiple
reader, single writer" with FIFO ordering over all readers and
writers. i.e. a writer can't get access until all readers have been
released.
[...]
b.g.
Return-Path:
Sender: grib@cs.utexas.edu
To: "Micheal S. Hewett"
Subject: Re: frames and s
References: <3831A399.5E2A8497@cs.utexas.edu>
From: Bill Gribble
Date: 16 Nov 1999 14:04:32 -0600
"Micheal S. Hewett" writes:
> It seems that I have to send the form:
> (get-slot (make-shared-frame "calibration::turn-drive")
> "drive-default")
> Is that correct? If so, that will create a lot of instances
> in a web browser type of application.
Yes, that's right. Most applications don't use the KB like a web
browser would, so that doesn't bother me.
You can minimize the resource usage by explicitly cleaning up after
yourself:
(let* ((frame-ptr (make-shared-frame "calibration::turn/drive"))
(slot-value (get-slot frame-ptr "drive-default")))
(finalize frame-ptr)
slot-value)
b.g.
Subject: [unfinished] intro to distributed object system
Date: 22 Dec 1999 15:13:38 -0600
From: Bill Gribble
To: robotics@cs.utexas.edu
Just in case it comes in handy. I'll finish it later but here you have
an introduction to the introduction.
b.g.
===================================================================
Let's say you have a new piece of hardware that you have installed on
the robot.. a GPS receiver, for example. This receiver has "read"
semantics (you can ask it where you are) and (though we might not use
it) "write" semantics (you can set waypoints and configure the thing
in various ways).
Since the GPS probably has a serial-port interface, it's easy to write
a class to talk to it. Let's make it a trivially simple class with
just a few methods :
class GPSReceiver {
public:
GPSReceiver(const string & serial_port_name);
~GPSReceiver();
gps_pos_t get_current_position();
void set_waypoint(gps_pos_t waypoint);
private:
SerialPort _port;
};
Obviously, you can only instantiate a GPSReceiver object on the same
machine that the GPS receiver is plugged in to. But you might want
access to the data from anywhere, so you want to use the distributed
object system. I'll go through two different ways of doing this, one
straightforward but not exactly what you want, and one that's a little
more of a hassle up front but does just what you want.
========================
== the straightforward path
========================
The most straightforward way is to make the GPSReceiver class
distributable and provide it as a service on the machine with the
physical thing plugged into it. This means that anyone can ask the
switchboard where the GPSReceiver class can be instantiated, then
cause one to be created there, and keep a "stub" object that can call
methods on it.
First you need to make a 'distribution template' which describes the
class in a parsable abstract form. This goes in the file
classname.do2 , so here we create GPSReceiver.do2 :
---------------------
(define-distributed-type 'gps-pos-t
'(c++ "gps_pos_t" "gps_pos_t" "GPSReceiver.h")
'(scheme "" "gps-pos" "gps-pos"))
(define-distributed-object "GPSReceiver"
'(constructurs
(("serial-port-name" (reference-to string) const)))
'(methods
("get_current_position" () gps-pos-t)
("set_waypoint" (("waypoint_pos" gps-pos-t)) #f)))
---------------------
define-distributed-type
-----------------------
define-distributed-type is used to tell the distributed object
system about objects that may be used as arguments or return values
in the methods of a distributed object. All the standard types
are defined already. We use explicit naming conventions for sizes to
get around compiler assumptions, so don't expect "int" to work...
use "int_s4" (signed, 4-byte) instead.
standard types:
int_u1, int_s1 ("unsigned char", "char")
int_u2, int_s2 ("unsigned short", "short")
int_u4, int_s4 ("unsigned int", "int")
real_8 ("double")
string STL rope class... a great string class.
time struct timeval
bool boolean
pointer typedef void * pointer;
vector STL vector of string
You also get pointers to, references to, and fixed-length C vectors of
whatever type for free.
If you have a new type foo that you need to send as an argument or retval,
you need to :
1. put it in a define-distributed-type form in the .do2 file
2. define functions called DO_get_foo and DO_send_foo according
to instructions below.
The fields in the define-distributed type form are:
(define-distributed-type
'(c++ )
'(scheme ))
The "embeddable" names are ones used when automatically generating
function names that include types in the function names; you might
have to refer to a c++ type as parent_type::child_type, but you
can't make a function with that in its name; you might put the line
'(c++ "parent_type::child_type" "parent_type_child_type" "parent_comm.h")
the header file is the one that contains the prototypes for DO_get_foo
and DO_send_foo.
This is from Ben's brain, not Bill's.
From: "Benjamin J. Kuipers"
Date: Wed, 1 Dec 1999 23:37:52 -0600
Emilio,
This afternoon, we briefly discussed the problem of stopping the robot
smoothly at a desired point, in spite of the weird control at low
speeds. I have a suggestion.
Note that, if velocity is directly controllable, the first-order
control law
dx/dt = -f(x)
brings the system to rest at x=0 for any f in M0+ (that is, any
monotonically increasing f such that f(0)=0.
Since any f will work, we can pick the f that has the performance
properties we want.
The linear controller you have been using is f(x) = kx.
Try it with f(x) = k*sqrt(x).
Solving this analytically, with an initial condition x(0)=1, you get
the predicted behavior
x(t) = (1 - kt/2)^2
which is a parabolic drop from x=1 to x=0.
You can solve for velocity explicitly, too:
v(t) = k^2 t/2 - k
which actually means that you could implement the control signal as
an open loop change in velocity: select the right value for k, then
change velocity linearly to zero over the selected time interval.
Then you are no longer constrained by sensing. If you estimated the
situation right to start with, you just stop. (The open loop approach
also avoids the singularity as f(x) -> 0.)
Caveat: I have assumed that velocity is directly, and instaneously,
controllable, which is only an approximation. It only bites you if
the initial velocity is quite far from -f(x) for the initial value of x.
The controller will probably have to have another component to handle
cases like that.
This should be very easy to implement and test, using the forward
range sensor to test how close to the desired point the wheelchair
actually stopped. (Aim for 1.00 m from the wall.)
Cheers,
Ben
Approaches to control (by Ben)
This is from Ben's brain, not Bill's.
Return-Path:
From: "Benjamin J. Kuipers"
Date: Thu, 2 Dec 1999 10:02:27 -0600
Emilio (and everyone),
Last night's message about stopping control may clarify an interesting
contrast between different approaches to control.
(1) Feedback control: x'(t) = -f(x(t)).
At each time-step, sense x, and set x' appropriately to
obey the law.
(2) Open-loop control: v(t) = k^2 t/2 - k.
(This may also be what people call "feed-forward control",
but I'm not sure.)
After first observing x(0), calculate appropriate values of k,
and the value of t such that x(t)=0, then control v(t) on the
above schedule, independent of further observations.
(3) Model-predictive control:
Repeat (2) at each time-step, with updated observations of x(t)
and updated values of k and t. This gives you a schedule to
follow between observations, plus updating the control policy
as new information arrives.
Feedback control is robust in the face of sensory and motor errors.
Open-loop control avoids inline processing costs of sensory input.
Model-predictive control combines the benefits.
Cheers,
Ben