Firefighter Robot

Welcome graphic

1. Contest

            The objective of the firefighter contest is to traverse a maze with 4 rooms, checking each room to see if there is a candle in it. If there is a candle in the room, the robot must enter the room and move towards the candle; once some part of the robot is within 33cm of the candle, marked by a whole or partial circle of poster board, then the robot can put out the flame. The base of the flame would be 15cm to 20cm from the base of the candle. Once the flame was extinguished, the robot should shutoff. Otherwise, if the candle is not in the room, the robot should proceed to check the next room to see if the candle is there. Please see http://www.trincoll.edu/events/robot/rules04.htm for complete details.

 

2. Robot Design

            One of the rules for the firefighter contest states that robot must be 33cm x 33cm x 33cm or smaller; the robot must always remain within these dimensions, that is, the robot must not expand beyond these limitations. Keeping this in mind, I opted to create a small robot, based on the bone structure of a bird hollowed boned, which implicitly would make the robot light. I created a based for which the Handy Board to dwell, and directly above this base, I created a frame (Figure 2.1).

            To extinguish the flame, I intend to use a fan with a misting device. However, given the 3 week time constraint for the final project, I opted to use just the fan alone. I bought a handheld personal fan from a dollar store (Figure 2.2). I proceeded to dismantle it, in order to be able to directly connect the fan to a motor port on the Handy Board. Playing on the safe side that the robot would not necessarily be directly in front of the candle when it was supposed to be extinguished, I mounted the fan on a servo motor. I then programmed the servo to oscillate, starting from the middle, rotating to the robots right side, fully to the robots left side, and then back to the middle. I then mounted the oscillating fan on top of the frame, angled downward so that the fan would be able to blow out the flame, whose base should be 15cm to 20cm from the ground.

            To detect if a flame existed, I originally was going to build flame detectors from flashlight shielding and thermistors; please see http://david.solarbotics.net/Tutorials/tutheat%20sensor.htm for full details. Instead, I opted to use Hamamatsu UVTron flame detector (Figure 2.3); please see http://www.acroname.com/robotics/parts/R67-UVTRON.html for full details. I found that the flame detector was sensitive enough to sense the flame up to approximately 3 feet away, but not so sensitive that it could detect a flame behind the walls. To find the flame itself, I intended to use a pyro sensor, to sense the heat emitted from the flame; however, the results returned were very difficult to understand. Instead, I opted to simply use a pair of photodiodes (Figure 2.4), with which previous light seeking code could be slightly altered to find the flame. Using photodiodes is risky though due to the uncertainties of ambient lighting.

            Since the candle is placed on a white piece of cardboard, radius 33cm, I put 2 IR tophat detectors in two locations on the bottom of my robot (Figure 2.5). This way when the robot was looking for the flame, the white lines separating the rooms from the corridors would not be confused with the partial circle. Both IR sensors would return similar values if the robot were completely on the white cardboard circle.

            To navigate, I placed 3 infrared ET sensors on the robot (Figure 2.6); I actually only used 2 of the 3. These sensors were used to navigate along the right wall. One ET sensor was mounted facing forward, 4 inches inward from the front of the chassis; the second ET was mounted on the top of the robots frame, angled, also set inward 4 inches. The third ET I intend to use for left wall following capabilities to help navigate the onion room.

Upon successful navigation, I noticed that occasionally the robot would not be able to turn sharply around corners and walls. Although points are deducted if the robot hits the wall, I felt that it was more important for the robot to take the deduction and finish the contest. Thus to avoid the robot being stuck in that situation, I added a digital switch to the robots right side, mounted on the frame (Figure 2.7).

 

3. Code Theory

            After an entire semester, I have learned that simplicity is key. Thus, I began by playing with each sensor before creating the main code; this way mostly for the flame detector and oscillating fan interaction. Basically, if at any time while the robot is checking the rooms for the candle, if the flame detector determines that there is a flame in that room, the fan would turn on, oscillating back and forth until the flame was out.

            However, there was a rule stipulating that the robot had to be within 12 inches of the candle before attempting to put out the flame. Thus, I used a modified light seeking code from a previous lab to work with the mounted photodiodes. Once the robot was within the 12 inches, that is, the robot was on the partial circle of white cardboard, then it could proceed to put out the flame

            To determine if the robot was on the partial circle of white cardboard, I had placed 2 tophat IR sensors on the bottom of my robot, in opposing corners. When both of the IR sensors returned a value of less than 200, then I assumed the robot was on the circle enough to put out the flame.

            Putting out the flame entailed turning on the fan, which was merely issuing a fd(2); to a motor port. After the fan was turned on, it oscillated back and forth via a servo motor set to various values; this continues until the flame is out; then the fan turns off.

            For navigation, I used right wall following, nearly identical to a previous lab. This allowed for the robot to navigate 3 of the 4 rooms.

            I then combined these functions into a main code into a 2 state machine: 1 being checking the onion room, and 2 begin the remaining 3 rooms. I attempted to have a right wall following for the first 30 seconds to jump over the choice of corridors at HOME. At a certain point for values of the 2 right wall following ET sensors, the robot should then turn left into the onion room. If the flame was detected in that room, then the light seeking code, etc. would kick in; otherwise, the robot should back out and turn right, continuing to right wall follow into the other 3 rooms.

Using the processes, the right wall following navigation starts and continues until a candle is found via the flame detector. Once this occurs, the navigation process is stopped. Then the light seeking process starts until both IR sensors read that the robot is on the partial circle of white cardboard. All power to the motor ports is killed, thus preventing any further movement towards the candle. The fan turns on and oscillates until the flame is out; then it turns off. A time lapse of approximately 2 seconds occurs, and then we check again to see if a flame exists. Perhaps due to smoldering the flame may rekindle. If this occurs, the fan turns back on and oscillates again until the flame is out. Then the fan it turned off and a message of Fires Out is printed to screen with an accompanying high pitched beep.

 

4. Analysis

            Due to the 3 week time constraint, I kept the functionality to a bare minimum for the final project. For instance, the method for extinguishing the flame was only a fan; in the Trinity competition, I plan on using a misting device in front of the fan.

Also, the navigation was only mediocre. In my light seeking, I had used code slightly modified from a previous lab. However, I also used a normalize function in this light seeking, which created some problems for the robot going towards the light. At greater distances, the motor outputs were very weak, while at smaller distances, the motor outputs nearly knocked over the candle. After the in-class demonstration, Aron suggested not using a normalize function, and simply compare the two photodiode values; whichever was greater, power the opposing motor with a constant value, say 30.

Another navigation problem I had was when the flame detector found the flame, the code was designed to go immediately into the light seeking code. This wound up driving my robot straight into a wall, twice. I had not realized this because in my previous run before the in-class demonstration, the robot had perfect conditions to light seek because it was past the wall before going to that process. I believe that if we ignore the flame detector until the robot is past the edge of the wall, then the light seeking will not drive the robot to beach itself.

The ultimate navigation issue is having the robot go into the onion room. I had first tried ET distance sensors, but realized that the range was inadequate and returned faulty values. Then I switched to the sonar, but also received faulty values because of the 30 degree span from which the values are returned. So I opted to use angled ET sensors and specify the values at which the robot should stop and turn. This was ineffective because the robot would sometimes not read the values at the correct spot, meaning it would find those values too early or too late. My new strategy is to use right wall following for the first 30 seconds of the maze, and then move to left wall following to check the onion room. If the candle is not in that room, then go straight almost until it hits the opposing wall, that is until the front facing ET sensor reaches a certain value, then turn left, and use right wall following for the remaining 3 rooms.

For Trinity, I hope to make marked improvements on the navigation. Since a low time and as few mistakes as possible makes for the best run, I plan to change the navigation from right wall following for the entire maze, after the onion room, to only right wall follow in the corridors. Once one of the undercarriage IR sensors detects a white line and the flame detector has not gone off, then the robot should back out and move to the next room.

Another idea, if I finish my robot to perfect navigation and extinguishing, is having the robot start upon smoke detector alarm input instead of pushing a button.

overview.jpg
Figure 2.1 Finished Final Project Robot

fan.jpg
Figure 2.2 Oscillating Fan

flame_detector.jpg
Figure 2.3 Flame Detector

photodiodes.jpg
Figure 2.4 Photodiodes

ir.jpg
Figure 2.5 IR Sensors: top-left & bottom-right

ets.jpg
Figure 2.6 ET Distance Sensors

switch.jpg
Figure 2.7 Digital Switch

//MOTOR PORTS

#define LEFT_MOTOR 3

#define RIGHT_MOTOR 1

#define fan 2

//FLOATING ANALOG - ET

#define look_right 17

#define look_forward 18

#define look_left 16

//DIGITAL PORTS

#define switch 14

//ANALOG PORTS, IR & PHOTODIODES

#define IR 3

#define IR_verify 4

#define RIGHT_EYE 5

#define LEFT_EYE 6

 

//CODE FOR FLAME DETECTOR

#use hb_uv15.icb

/*

 * Test of UVTron interrupt routine

 * for handy board.

 */

 

void flame_detector()

{

    /* set sampling rate of 250ms */

   

    uv_stime=250;

   

    while(1)

      {

        if (uv_count) {

            printf("FIRE!\n");

            beep();

        } else {

            printf(" \n");

        }

        //        msleep(250L);

        sleep(0.1);

    }

}

 

//CODE USED IN NAVIGATION

#define forward() {motor(LEFT_MOTOR, 60); motor(RIGHT_MOTOR, 60);}

#define backward() {motor(LEFT_MOTOR, -60); motor(RIGHT_MOTOR, -60);}

#define right() {motor(LEFT_MOTOR, 60); motor(RIGHT_MOTOR, -30);}

#define left() {motor(LEFT_MOTOR, -30); motor(RIGHT_MOTOR, 60);}

#define sharp_left() {motor(LEFT_MOTOR, -60); motor(RIGHT_MOTOR, 60);}

 

//NAVIGATION

// three state approach; from Lab 4

void navigate_right_wall()

{

    sleep(2.); //give user time to setup

    while (1)

      {   

        //state 3 - if between values 99 - 65, go forward along wall

        if ((analog(look_right) < 75) && (analog(look_right) >= 60))

          {

            printf("State 3; lr=%d f=%d\n", analog(look_right), analog(look_forward));

            forward();

            msleep(50L); 

            ao();

        }

       

        //state 1 - if ETa reports values too close to wall

        else if (analog(look_right) >= 75)

            {

              printf("State 1; lr=%d f=%d\n", analog(look_right), analog(look_forward));

              left();

              msleep(50L);

              ao();

          }

         

          //state 2 - if ETa reports values too far from wall

          else if (analog(look_right) < 60)

              {

                printf("State 2; ETa=%d ETb=%d\n", analog(look_right), analog(look_forward));     

                right();

                msleep(50L);

                ao();

            }

           

            if (digital(switch) == 1)

          {

            backward();

            msleep(100L);

            left();

            msleep(100L);

            ao();

        }

       

        //Do not run into the wall!!!!

        if (analog(look_forward) >= 40)

          {

            printf("Steering clear! lr=%d f=%d\n", analog(look_right), analog(look_forward));

            //            beep();

            sharp_left();

            msleep(300L);

            ao();

        }

    }

}

 

void seek_light()

{

    while(1)

      {

        //while the right sensor gets more light...

        if (analog(RIGHT_EYE) < analog(LEFT_EYE))

          {

            //turn right, towards light

            motor(LEFT_MOTOR, 30); //turns wheel fwd

            motor(RIGHT_MOTOR, 0);

            msleep(50L);

        }

       

        //while the left sensor gets more light...

        if (analog(RIGHT_EYE) > analog(LEFT_EYE))

          {           

            //turn left, towards light

            motor(RIGHT_MOTOR, 30); //turns wheel fwd

            motor(LEFT_MOTOR, 0);

            msleep(50L);

        }

       

        //while both sensors receive equal amounts of light...

        if (analog(RIGHT_EYE) == analog(LEFT_EYE))

          {

            //go forward, towards light

            motor(LEFT_MOTOR, 30);

            motor(RIGHT_MOTOR, 30);

            msleep(50L);

        }

    }

}

 

//CODE INVOLVING THE FAN

void fan_on()

{

    motor(fan, 100);

}

 

void rotate_fan()

{

    while (uv_count)

      {     

        //start in middle, goes right, goes full left, back to center

        servo0 = 2250;

        sleep(1.);

        servo0 = 2500;

        sleep(1.);

        servo0 = 2750;

        sleep(1.);

        servo0 = 3000;

        sleep(1.);

        servo0 = 2750;

        sleep(1.);

        servo0 = 2500;

        sleep(1.);

        servo0 = 2250;

        sleep(1.);

        servo0 = 2000;

        sleep(1.);

        servo0 = 1750;

        sleep(1.);

        servo0 = 1500;

        sleep(1.);

        servo0 = 1750;

        sleep(1.);

        servo0 = 2000;

        sleep(1.);

    }

}

 

void fan_off()

{

    motor(fan, 0);

}

 

void put_out_flame(int pid)

{

    int pid, pid_rotate;

   

    forward();

    sleep(1.);

   

    //seek_light while not on white circle

    pid = start_process (seek_light());

    while (1)

      {      

        if ((analog(IR) <= 50) && (analog(IR_verify) <= 50))

          {

            kill_process(pid);

            break;

        }

    }

    ao();

   

    //turn on and rotate fan until flame is out...

    while(1)

      {

        if (uv_count)

          {

            fan_on();

            pid_rotate = start_process(rotate_fan());

            while(1)

              if (!uv_count)

                {

                  kill_process(pid_rotate);

                  break;

              }

        }

        else

          {

            fan_off();

            break;

        }

    }

   

    sleep(2.);

   

    //check to make sure flame is really out- JUST IN CASE

    //turn on and rotate fan until flame is out...

    while(1)

      {

        if (uv_count)

          {

            fan_on();

            pid_rotate = start_process(rotate_fan());

            while(1)

              if (!uv_count)

                {

                  kill_process(pid_rotate);

                  break;

              }

        }

        else

          {

            fan_off();

            break;

        }

    }

   

    kill_process(pid); //stop beeping from flame detector

}

 

void main()

{

    int pid, pid_flame;

    sleep(2.); //give user time to setup

    init_expbd_servos(1);

   

    //start flame detecting code; give audio cue if flame is present

    pid_flame = start_process(flame_detector());

   

    /*    //SKIPPING OVER ONION ROOM

   

    //start robot navigating right wall without looking at left or front sensor

    //allows robot to "skip" over perpendicular corridor

    pid = start_process(navigate_right_wall());

    sleep(30.);

    kill_process(pid);   

   

    //STATE 1: Go to top right room and check for flame

   

    //start robot navigating right wall without looking at left or front sensor

    //allows robot to "skip" over perpendicular corridor

    pid = start_process(navigate_right_wall());

    sleep(30.);

    kill_process(pid);

   

    //navigate_right_wall enables the robot to visits 3 of the 4 rooms...

    pid = start_process(navigate_right_wall());

   

    //stay here until we get into room A

    while(1)

      {

        //now to tackle the onion roon...

        if ((analog(look_forward) >= 20) && (analog(look_left) <= 25) && (analog(look_left) >= 15))

          {

            kill_process(pid);

           

            sharp_left();

            msleep(500L);

            ao();

           

            while (analog(IR) > 200)

              forward();

           

            ao();

            beep();

            break;

        }

    }

   

    if (uv_count) //if candle is in room, put_out_flame

      put_out_flame(pid_flame);

    else //otherwise, backup, turn, and keep searching

      {

        //STATE 2 - all other rooms

        while (analog(look_forward) > 16)

          backward();

        right_turn();

        

        //not in room A, so check other 3 rooms

        pid = start_process(navigate_right_wall());

       

        //keep navigating until we find the candle

        while(1)

          {

            if (uv_count) //if candle is in room, stop navigating and put_out_flame

              {

                kill_process(pid);

                put_out_flame(pid_flame);

                break;

            }

        }  

    }

*/

    //CHECK OTHER 3 ROOMS

    pid = start_process(navigate_right_wall());

   

    //keep navigating until we find the candle

    while(1)

      {

        if (uv_count) //if candle is in room, stop navigating and put_out_flame

          {

            kill_process(pid);

            put_out_flame(pid_flame);

            break;

        }

    }

   

    printf("Fire's Out!\n");

    set_beeper_pitch(1000.);

    beeper_on();

    sleep(1.);

    beeper_off();

    ao(); //shut all motors off

}