Newsgroups: comp.robotics
Path: brunix!ks
From: ks@cs.brown.edu (Kate Sanders)
Subject: RBL/AAAI93 reports: Baldrick
Message-ID: <1993Jul23.174658.10719@cs.brown.edu>
Sender: news@cs.brown.edu
Organization: Brown University Department of Computer Science
Distribution: comp
Date: Fri, 23 Jul 1993 17:46:58 GMT
Lines: 528


Fun with baldrick: another installment in the "what we did during the
aaai robot building event" series.

our team consisted of robert mccartney from uconn, kate sanders
from brown, and karl wurst from uconn.  

  robert@cse.uconn.edu
  ks@cs.brown.edu
  wurst@cse.uconn.edu

none of us had any experience at robots, but karl had experience with
lego, hot glue, and soldering, which helped a lot.  we read the manual
provided by the organizers and talked briefly about design, but
otherwise did minimal preparation before the event. 

we too would like to thank the organizers and TAs; not only was this a
fun project, but it quickly became obvious that if you want to know
about robots, there's no substitute for making one. on the first day,
we were confronted by a   box of legos and things with wires attached
and something that was probably the processor; by the end, we had a
working robot and we were talking  knowledgeably (well, more or less)
about IR sensors and gearboxes and downloading code. it's amazing how
much you can learn in only three days. 

and as dave miller said in the tutorial, "AI is fun -- but it doesn't
move." watching your robot move on its own is quite a trip.

note: this is a fairly long posting. we will start by discussing the
general design, give a brief overview of the competition, and then
append the code we used. skip ahead to "CODE:" if that is your main
interest. 

baldrick, our robot, was based on a 2-drive-wheel, one-rear-caster
design provided by the organizers. we changed the wheel configuration
to a "diamond" with independent drive wheels on sides and
casters front and rear with the hope (which turned out to be
justified) that the robot would be able to pivot in place around 
its center, where the ir-detection mast would be. we added a
U-shape made out of legos sticking forward from the front of the
robot, above the height of the walls, to push the coffeepot.

its sensors included: three ir sensors,
one narrow focus facing forward, two with wider focus at about 40
degrees off forward; bump sensors at each front corner and in the
center of the front, an ir reflectance sensor for detecting black vs.
white surface below, and a photosensor facing down to detect the
starting light.

once we began testing the robot, on tuesday afternoon, our first
lesson was -- legos fall apart. for an object that finds its way
around in the world by banging into walls, a robust structure is key.
after two or three hours when one of the wheels fell off every time we
tested it, we found a better design for the caster that made all the
difference. the box-like shape was effective in protecting the
processor and wiring connections, and after that, we had no further
problems with the structure.

after that, we could concentrate on mounting and testing the rest of
the sensors and on the code. our control routines included
ones to 1) face the beacon (done by slowly rotating until beacon
detected by a sensor, then fine movement based on side unit readings
til center sensor facing source), 2) track the beacon (drive along,
maintaining beacon ahead), 3) drive forward or backward a specified
distance, 4) spin right or left a specific angle, 5) stop on
collision, 6) avoid-wall (on collision, back up and turn away a bit),
and 7) stop-on-black.  The last three were processes that could
disable (or interrupt) any forward motion processes. 

Both of our top-level strategies were very simple, even stupid. Given
the time limit, it seemed best to get something working, no matter how
primitive; and that turned out to be a good policy. Not only were we
able to enter both competitions (which not every team did), but our
robot lasted for several rounds in each, and generally performed
surprisingly well.

Strategy for office escape:
	face the beacon to find which direction is front
	go forward with stop-on-collision enabled a couple of feet.
	if you dont collide (the front door is open)
   	   follow the beacon with stop-on-black enabled
	else
           back up a foot
	   turn outward
	   drive a foot to get out of start area
	   drive a foot toward the beacon
	   turn toward the center
	   drive 2 feet (putting you in the center in front of the exit)
	   face the beacon
	   follow the beacon with stop-on-black enabled

(Whenever stop-on-collision is not enabled, avoid wall is enabled)

The biggest problem was that turn angles and distances were fairly
unpredictable; also that "wall avoidance", which changed the direction away
from the wall could interact badly with beacon tracking if the beacon
was toward the wall.  the beacon finding in the middle of the
side-door-open code slowed it down, but correcting the course was
often necessary.  the observed behavior was that it always worked if
the front door was open, and worked something over half the time
otherwise.


Strategy for capture the coffee pot:
This was written in about 10 minutes after working
all night. really stupid plan, but sometimes worked:

	face the beacon to find out what direction is forward
	turn away from center about 30 degrees
	drive forward 6 feet
	follow the beacon

(all done with wall avoidance enabled)

Optimally, it worked like this:  it finds the beacon, heads down the
side of the course until it hits a side wall, at which point avoiding the 
wall straigtens it out somewhat and it goes down to near the end.  it
then tries to follow the beacon; if it doesnt end up on the wrong side
of a wall from the pot or hung up on the end of a board, it runs into
the pot and keeps pushing it (since its 'potcatcher' is a U shaped
thing forward of the sensors, it doesnt detect that it is pushing the
pot) until the time is out.  in testing (about 5 minutes worth) it
found the pot almost half the time and pushed it into a corner. since
you got one point for being the first to touch the coffeepot and one
for being the last to touch it, and getting the third point (for
maneuvering the coffeepot to the black coffee cup painted on the
table) would have been much harder, playing "keep away" seemed like a
viable strategy. 

THE COMPETITION

baldrick lasted for several rounds in each competition and was
(to our amazement) the first to get all three points in the coffee pot
competition. Sometimes it was very lucky, sometimes it was unlucky,
and very occasionally, things worked accordingly to plan. 

We were most lucky in the second round of the coffeepot competition. 
baldrick found the coffeepot easily and pushed it towards the side of
the table. Then, just as baldrick reached the corner, the other robot
reached the coffeepot on the other side. baldrick got hung up on the
other robot, causing it to pivot, rotating the coffeepot
around the second robot and onto the black coffee cup just in time to
stop at the sixty-second limit. Observers said this looked planned,
but it wasnt -- everything after finding the coffeepot was pure luck.

Bad luck intervened in the escape-from-the-office competition, where
baldrick halted suddenly just before the finish line, before the time
limit ran out, and was passed by another robot (Bottom Feeder) which
had been way behind it. Our best guess at this point is that the ir
reflectance sensor was detecting black too soon, perhaps misled by a
shadow; we didnt have much time to test this part of the code
beforehand and changed batteries mid contest which may have affected
the sensitivity. 



THE CODE:

(note: the code which follows has not been edited other than
concatenating files, is undoubtably full of bad style and bugs, and
was patched together as needed in very little time, without a C
manual. don't try this at home.)


/* globals */

int left_speed = 0;
int right_speed = 0;
int max_left = 100;
int max_right = 100;

int avoid_enabled = 1 ;
int drive_enabled = 1;

/* convert force, distance or rotation to time  */

float fd_to_time(int force, float distance)
{
    float forcon = .01;
    float discon = 1.25;
    return ((distance*discon)/((float)(force)*forcon));
}

float fr_to_time(int force, float rotation)
{
    float forcon = .01;
    float rotcon = .50;
    return ((rotation*rotcon)/((float)(force)*forcon));
}

/* Bumper/sensor Functions */

int FL_bumper()
{
    return digital(1);
}

int FR_bumper()
{
    return digital(2);
}

int BL_bumper()
{
    return digital(3);
}

int BR_bumper()
{
    return digital(4);
}

int center_bumper()
{
    return digital(0);
}

int ir_left()
{
    return(ir_counts(5));
}

int ir_center()
{
    return(ir_counts(6));
}

int ir_right()
{
    return(ir_counts(7));
}

int right_side()
{
    return(dip_switch(1));
}

int black_sensor()
{
    return(analog(16) > 240);
}

/* ***   top-level code  ***  */

void main()  /*  coffeepot contest */
{
    start_machine(15); 
    cunning_plan2(); 
    led_out0(0);
}

void cunning_plan1()
{
     if (right_side())
        {
            turn_toward_beacon(0);
            if (front_door_closed()) 
                office_right_side(); 
            else  
                drive_toward_beacon_and_stop(100,200.0);
    }
    else
         {
            turn_toward_beacon(1);
            if (front_door_closed())
                 office_left_side();
             else
                 drive_toward_beacon_and_stop(100,200.0);

}}


void cunning_plan2()
{
    int pid;
    if (right_side())
        {
            turn_toward_beacon(1);
            spin_right(50,.3);
            avoid_enabled = 1;
            pid = start_process(avoid_the_wall());
            drive_fwd(100,6.0);
            turn_toward_beacon(1);
            drive_toward_beacon(100,600.0);
            kill_process(pid);
    }
    else
        {
            turn_toward_beacon(0);
            spin_left(50,.3);
            avoid_enabled = 1;
            pid = start_process(avoid_the_wall());
            drive_fwd(100,6.0);
            turn_toward_beacon(0);
            drive_toward_beacon(100,600.0);
            kill_process(pid);
        }
}


void office_right_side()
{
    int pid;
    avoid_enabled = 1;
    pid = start_process(avoid_the_wall());
    spin_right(50,.9);
    drive_fwd(100,2.0);
    spin_left(50,.9);
    drive_fwd(100,1.25);
    turn_toward_beacon(0);
    spin_left(50,.45);
    drive_fwd(100,2.5);
    turn_toward_beacon(1);
    drive_toward_beacon_and_stop(100,600.0);
    kill_process(pid);
}

void office_left_side()
{
    int pid;
    avoid_enabled = 1;
    pid = start_process(avoid_the_wall());
    spin_left(50,.9);
    drive_fwd(100,1.9);
    spin_right(50,.8);
    drive_fwd(100,1.0);
    turn_toward_beacon(1);
    spin_right(50,.45);
    drive_fwd(100,2.5);
    turn_toward_beacon(0);
    drive_toward_beacon_and_stop(100,600.0);
    kill_process(pid);
}


/*  reactive stuff */

void avoid_the_wall()
{
    int right=0;
    int left=0;
    int center=0;
    int save_dr_enabled = drive_enabled;
    while (avoid_enabled)
        {
            while (!(left=FL_bumper()) && !(center=center_bumper()) && !(right=FR_bumper()))
                {  };
            printf("hit bumper!!! \n");
            hog_processor();
            drive_enabled = 0;
            drive_rev(50,.25);
            if (right)
                spin_left(50, .5);
            else spin_right(50,.5);
            drive_enabled = save_dr_enabled;
}}

void stop_at_the_wall()
{
    while (!FL_bumper() && !center_bumper() &&  !FR_bumper())
        {};
    left_speed = 0;
    right_speed = 0;
    }


void stop_on_black()
{
    while (!black_sensor())
        {};
    drive_enabled = 0;
}

int enable_black_shutdown()
{
    led_out0(1);
    return(start_process(stop_on_black()));
}

    
int front_door_closed()
{
    int pid;
    float duration = fd_to_time(100,2.0);
    float sofar = 0.0;
    left_speed = 100;
    right_speed = 100;
    pid = start_process(stop_at_the_wall(),10);
    while(sofar < duration && left_speed>0)
        {
            sofar = sofar + .05;
            drive(left_speed,right_speed, .05);
    }
    kill_process(pid);
    drive_rev(100,.75);  /* this is a bug--should only be done for closed */
    if (left_speed == 0)
        return(1);
    else return(0);
}


/*    ir beacon following   */

 
void monitor_beacon_direction()    /* speeds doubled, tholds halved */
{
    set_ir_transmit_period(10000);
    set_ir_receive_frequency(100);
    ir_transmit_on();
    ir_receive_on();
    while(1)
        {
            if (ir_center() > 100)
               {
                   left_speed=max_left;
                   right_speed = max_right;
            }
            else if (ir_left() > 50 && ir_left() > ir_right())
                {
                    right_speed  = 15;
                    left_speed =-15;
            }
            else if (ir_right() > 50 && ir_left() < ir_right())
                   {
                       left_speed = 15;
                       right_speed = -15;
    }
            printf("%d %d \n", left_speed,right_speed);
            sleep(.001);
}}
  
void turn_toward_beacon(int right)
{
    set_ir_receive_frequency(100);
    ir_receive_on();
    while (ir_left()<50 && ir_right()<50)   /* was 100 */
        if (right) spin_right(30,.1); else spin_left(30,.1);  /*  was .05 */
    while (ir_center() < 100)  /*was 200 */
        {
            if (ir_left()<ir_right())
            {
                spin_right(30,.02); /* was 15  */
            }
            else
                spin_left(30,.02);  /* was 15  */
    }
}
           

/*   drive control   */

void drive_toward_beacon(int force,float distance)
{
    int pid;
    float duration = fd_to_time(force,distance);
    float sofar = 0.0;
    max_left =force;
    max_right= force;
    pid = start_process(monitor_beacon_direction(),10);
    while(sofar < duration )
    if (drive_enabled)
        {
            sofar = sofar + .05;
            drive(left_speed,right_speed, .05);
    }
    kill_process(pid);
}
    
void drive_toward_beacon_and_stop(int force,float distance)
{
    int pid;
    int black_pid;
    float duration = fd_to_time(force,distance);
    float sofar = 0.0;
    max_left =force;
    max_right= force;
    pid = start_process(monitor_beacon_direction(),10);
    black_pid = enable_black_shutdown();
    while(sofar < duration )
    if (drive_enabled)
        {
            sofar = sofar + .05;
            drive(left_speed,right_speed, .05);
    }
    kill_process(pid);
    kill_process(black_pid);
}
    
    
void drive(int force0, int force1, float durat)
{
    motor(0,force0);
    motor(2,force1);
    sleep(durat);
    off(0);
    off(2);
}

void drive_fwd(int force, float distance)  /*  uses drive_enabled  */
{
    float sofar=0.0;
    float enough = fd_to_time(force,distance);
    while (sofar < enough)
    if (drive_enabled || !avoid_enabled)
        {
            drive(force,force, 0.5);
            sofar= sofar+0.5;
} }
           
void drive_rev(int force, float distance)
{
    drive(-force,-force,fd_to_time(force,distance));
}
 
void spin_left(int force, float rotation)
{
    drive(-force,force,fr_to_time(force,rotation));
}
 
void spin_right(int force, float rotation)
{
    drive(force,-force,fr_to_time(force,rotation));
}
            




