Lawrence Technological University
College of Arts and Science
Department of Mathematics and Computer Sciences

Handouts

Not Quite C and Robofest 2006

Robofest Academy Course, Hosted at the Academy of Michigan

Academy of Michigan Staff

Robofest Academy Staff

Sessions

Monday, January 9th:
In sessions at the end of last month, Dr. Chung and Mr. Tedder have introduced all the relevant hardware and software. Today we will present our objectives to the students and work out a plan to accomplish the objectives. The key points emphasized will be the importance of planning and the importance of teamwork in completing an engineering task. The formal name for our course would be, "Learning to program autonomous robots." On a resume this would be, "Embedded Systems Programming" and not, "Playing with Legos." Just last semester, one of my students got a job because of his experience with this same LEGO Mindstorms kit. The programming language we will use is Not Quite C. NQC was invented by Dave Baum for his students at the University of Utrecht. Our practice sessions will be on the playing field for Robofest 2006.

In a world where a long term job commitment might be 1 year, and teamwork is much more common than solo work, becoming literate programmers is very important. We need to be able to communicate clearly what our programs do and how they do it. Otherwise they will be not much help to our team and useless, as soon as we move to our next job. Using #define's and // comments in NQC make programs easier to read. Use them!

Wednesday, January 11th:
Today we will have 2 missions

  1. Do nothing until the touch sensor is pressed a second time. Then go forward 3 seconds.
  2. Follow the straight black line and stop at the aluminum foil.
In order to complete the first mission we will need to know how to count and how to compare numbers in NQC. In order to complete the second mission we will need to understand how to use the light sensor and how to use a second task to watch for the aluminum foil.

First, what does this program do and why doesn't it do what you might expect? How can we fix it?

// touches.nqc
// a program to count the number of times the touch sensor is pressed
#define TS SENSOR_1
#define FOREVER 0
int count;
task main()
{
  SetSensor(TS,SENSOR_TOUCH);
  count = 0;
  SetUserDisplay(count,0);
  until (count == 10) {
    until (TS == 1) ;
    count = count + 1;
    // adding until (TS == 0) ; would keep the RCX from counting too fast
  }
  until (FOREVER) ;  // keep the display on
}

Try stopping on a square of bright foil.

// foil.nqc
// a program to stop when you find a bright patch
#define LEFT OUT_A
#define RIGHT OUT_C
#define LS SENSOR_2
#define FOIL 60

task main()
{
  SetSensor(LS,SENSOR_LIGHT);
  OnFwd(LEFT+RIGHT);
  start find_foil;
}

task find_foil()
{
   until (LS >= FOIL) ;
   Off(LEFT+RIGHT);
   StopAllTasks();
}

Simple line following from Dave Baum. Does this work from either side of the line?

// line1.nqc
// a program to follow a line after the example by Dave Baum
// turn by stopping 1 wheel
#define LEFT OUT_A
#define RIGHT OUT_C
#define LS SENSOR_2
#define TOO_DARK 44
#define TOO_BRIGHT 48

task main()
{
  SetSensor(LS,SENSOR_LIGHT);
  OnFwd(LEFT+RIGHT);
  while(true) {
    if (LS <= TOO_DARK) {
      Off(LEFT);
      On(RIGHT);
    } else if (LS >= TOO_BRIGHT) {
      Off(RIGHT);
      On(LEFT);
    } else {
      On(LEFT+RIGHT);
    }
  }
}

Wednesday, January 18th:
Today we will have one more mission.

  1. Start when the touch sensor is tapped a second time. Then follow the line through the foil patch and finally stop when a bottle is touched.
Today's focus is to solve bigger problems by breaking them down into smaller problems. Each smaller problem can be solved by a smaller piece of code. In NQC there are two kinds of smaller pieces of code: tasks and functions. In order to complete this mission we will reuse some of the task code from last week and write one new task.

Last week we combined foil.nqc and line1.nqc into something like

// line2.nqc
// a program to follow a line and stop when you find a bright patch
// turn by stopping 1 wheel
#define LEFT OUT_A
#define RIGHT OUT_C
#define LS SENSOR_2
#define TOO_DARK 44
#define TOO_BRIGHT 48
#define FOIL 60

task main()
{
  SetSensor(LS,SENSOR_LIGHT);
  start find_foil;
  OnFwd(LEFT+RIGHT);
  while(true) {
    if (LS <= TOO_DARK) {
      Off(LEFT);
      On(RIGHT);
    } else if (LS >= TOO_BRIGHT) {
      Off(RIGHT);
      On(LEFT);
    } else {
      On(LEFT+RIGHT);
    }
  }
}

task find_foil()
{
   until (LS >= FOIL) ;
   Off(LEFT+RIGHT);
   StopAllTasks();
}

Today we will change find_foil a little. Before it could just stop. Now we will make it so it can say to the other tasks, "Hey, I found the foil!" (In programming this is called raising a flag or signalling with a semaphore.) For times when things are going wrong, adding a little sound can help the programmer figure out what is going on.

int foil;
task main()
{
  ...
  foil = false;
  start find_foil;
  ...
}
task find_foil()
{
   until (LS >= FOIL) ;
   foil = true; // let task main know what happened
   PlaySound(SOUND_UP); // let person know what happened
}
A new task to find the bottle is similar.
int bottle;
task main()
{
  ...
  bottle = false;
  start find_bottle_by_touch;
  ...
}
task find_bottle_by_touch()
{
   until (TS == 1) ;
   bottle = true; // let task main know what happened
   PlaySound(SOUND_DOUBLE_BEEP);  // let person know what happened
}

A task is one kind of building block. A function is another kind of NQC building block. Both tasks and functions are just blocks or paragraphs of code statements. English sentences end with a period and NQC statements end with a semicolon. English paragraphs begin with an indentation and end with a blank line. NQC blocks begin with a { and end with a }. Functions run one after the other. Tasks run at the same time. The main task in line1.nqc followed a line. At the same time, the find_foil task kept a watch for the patch of foil. Tasks of course start with the word "task". In NQC, all functions start with the word "void".

Let us make line following into a task of its own. Let us make waiting to start into a function. For the start, instead of counting up, let us count down like at a rocket launch.

#define LEFT OUT_A
#define RIGHT OUT_C
#define LS SENSOR_2
#define TOO_DARK 44
#define TOO_BRIGHT 48

task follow_line()
{
  SetSensor(LS,SENSOR_LIGHT);
  OnFwd(LEFT+RIGHT);
  while(true) {
    if (LS <= TOO_DARK) {
      Off(LEFT);
      On(RIGHT);
    } else if (LS >= TOO_BRIGHT) {
      Off(RIGHT);
      On(LEFT);
    } else {
      On(LEFT+RIGHT);
    }
  }
}

int count;
void wait_start()
{
  count = 2;
  SetUserDisplay(count,0);
  until (count == 0) {
    until (TS == 1) ; // wait for the press
    count = count - 1;
    PlaySound(SOUND_CLICK);
    until (TS == 0) ; // wait for the release to keep
  }                   // the RCX from counting too fast
}
turn_right() can also be a function. You can write this one to make a right turn when the robot gets to the foil patch.
void turn_right()
{
  // your statements go here
}
push_bottle() can also be a function. We will leave it empty for now. You can fill it in next week or later today if you finish early.
void push_bottle()
{
}

Grouping the #defines and any variables used in more than 1 task or function helps make your program more readable on our quest to become literate programmers. Now our program and main task will begin something like this.

// line3.nqc
// by your-name, your-teammate's-name
// A program to:
//   start following a line after 2 taps on the touch sensor
//   turn at the foil patch
//   continue following the line
//   stop when a bottle is found.
#define LEFT OUT_A
#define RIGHT OUT_C
#define TS SENSOR_1
#define LS SENSOR_2
#define TOO_DARK 44
#define TOO_BRIGHT 48
#define FOIL 60
int count,foil,bottle;

task main()
{
  SetSensor(TS,SENSOR_TOUCH); // if setup here don't need to later
  SetSensor(LS,SENSOR_LIGHT); // if setup here don't need to later
  wait_start();
  start follow_line;
  foil = false;
  start find_foil;
  until (foil) ;
  stop follow_line;
  stop find_foil;
  Off(LEFT+RIGHT);
  turn_right();
  start follow_line;
  bottle = false;
  start find_bottle_by_touch;
  until (bottle) ;
  stop follow_line;
  stop find_bottle_by_touch;
  Off(LEFT+RIGHT);
}

Last week we said that our simple line following program only worked from one side of the line. Be careful that when you make your right turn at the foil, you do not cross to the other side of the line!

Monday, January 23rd:
Review and improvement.

Today we will review what we know and try to use that knowledge to solve a few problems.

turn_right. Remember that to turn toward the right, the left wheel must go forward faster than the right wheel. One way is to simply stop the right wheel and turn the left one on for a little while. How long you leave the motor on, depends on your robot's battery charge and how much you want your robot to turn. You can experiment

// rturn.nqc
// A program to turn right a bit
#define LEFT OUT_A
#define RIGHT OUT_C
#define A_LITTLE_WHILE 20
task main()
{
   OnFwd(LEFT);
   Wait(A_LITTLE_WHILE);
   Off(LEFT);
   // another way to say the same thing would be:
   // Fwd(LEFT);
   // OnFor(LEFT,A_LITTLE_WHILE);
}

Try taking your robot with the line following program (now with the improved right turn) and see if it works

  1. starting on the black line is Ok,
  2. starting just to the left of the black line is Ok,
  3. starting just to the right of the black line does not work. The robot gets lost because it only knows how to turn right looking to get back on the line. If there is enough space to turn the robot will turn 180 degrees and start following the line the wrong way.
How could we make the start more reliable?
  1. Be careful to always start following a line either on the line or to the left of the line
  2. Change our line following program look for the line on the right side of the robot, but if it is not found after a short time, start looking back toward the left. The "improved line following program" in Baum's book is an example of this.

Last week we noticed many of the robots did not stop when they found the bottle. Watching the View screen with the ^ pointing at the touch sensor, we could see the touch sensor was not always pressed when the robot first hit the bottle. Again we need to make the robot more reliable. But this time, we need to redesign the "hardware." We found that adding a brick to stick out in front of the touch sensor worked. The brick was mounted on the touch sensor supports with a beam. (A beam is a brick with holes in it.) While we were modifying the touch sensor, the prongs were turned around so that we could push the bottle better.

With our improved touch sensor, it was time to push the bottle over the edge and then get back to the line. The result would be something like this. Because the floor is relatively far away from the light sensor, it reflects even less light than the black line. The light sensor reading when hanging over the edge for one robot was 33.

// line4.nqc
// by your-name, your-teammate's-name
// A program to:
//   start following a line after 2 taps on the touch sensor
//   turn at the foil patch
//   continue following the line
//   stop when a bottle is found
//   push the bottle over the edge
//   get back to following the line
//   all the way back to the foil
#define LEFT OUT_A
#define RIGHT OUT_C
#define TS SENSOR_1
#define LS SENSOR_2
#define TOO_DARK 44
#define TOO_BRIGHT 48
#define FOIL 60
#define EDGE 36
int count,foil,bottle;

task main()
{
  SetSensor(TS,SENSOR_TOUCH); // if setup here don't need to later
  SetSensor(LS,SENSOR_LIGHT); // if setup here don't need to later
  wait_start();
  // follow the line and watch for the foil at the same time
  start follow_line;
  foil = false;
  start find_foil;
  until (foil) ;
  stop follow_line;
  stop find_foil;
  Off(LEFT+RIGHT);
  // now robot is over the foil
  turn_right();  // get a little nearer line
  // follow line and watch for bottle at the same time
  start follow_line;
  bottle = false;
  start find_bottle_by_touch;
  until (bottle) ;
  stop follow_line;
  stop find_bottle_by_touch;
  Off(LEFT+RIGHT);
  // now robot is touching bottle
  push_bottle();  // new function for you to write
  back_to_line(); // new function for you to write
  // Now that robot is back on or just to the left of the line,
  // (Remember your robot gets lost when it starts on the right of the line!)
  // do the same things as before when you wanted to follow the line back
  // to the foil. 
  // follow the line and watch for the foil at the same time
  start follow_line;
  foil = false;
  start find_foil;
  until (foil) ;
  stop follow_line;
  stop find_foil;
  Off(LEFT+RIGHT);
  // now robot is over the foil
}

// your other tasks and void functions from line3.nqc
// ...
// ...

void push_bottle()
{
  // push forward...
  // you could also push to the right, maybe faster
  OnFwd(LEFT+RIGHT);
  // stop at the edge with light sensor
  until (LS < EDGE) ;  // until find dark edge keep on going
  // to find the edge by feel, wait until the bottle drops away instead
  // until (TS == 0) ;  // until touch sensor releases keep on going
  Off(LEFT+RIGHT); // stop
}

void back_to_line()
{
  // back up until near the oval line
  OnRev(LEFT+RIGHT);
  Wait(200); // This is just a guess.
  // If you pushed your bottle by turning to the right you may be
  // to the right of the line now.  If you pushed straight then you
  // may have to turn a little to the left.
  Off(RIGHT);  // Going backwards this will turn to the left
  Wait(100);   // This is just a guess.
  Off(LEFT+RIGHT);
}

Monday, January 30th:
Robot to robot communication and building a proximity sensor.

The first step, as usual, is to get your computer and robot ready and then check the robot's battery level. Next, see George Miller about raising your yellow brick high enough that it can "see" over the touch sensor.

Our robots will work in pairs. One robot will send a message to the other robot that it is time to start. The second robot will wait for the message and then go forward and turn toward the line and follow the line to the foil. Prepare your robot to act in either role. You could download your send message program to Program Slot 1 and your receive program to Program Slot 2.

Cooperate with another team to test both of your programs.

The sending program will use a new function: SendMessage() and a new control statement: repeat (number-of-times) { }. Your teams have to agree on the same CODE for the message.

// sender.nqc
// by your-names
// A program to send a start message
#define CODE some-number-from-1-to-255
task main()
{
  repeat (5) {          // Send message several times to be sure
    SendMessage(CODE);  // it is received in a room crowded with
    Wait(15);           // other robots.
  }
}

For today's practice session we will all use

#define CODE 3

The receiving program requires rewriting wait_start() so that the robot waits for the correct message instead of 2 taps. We will use another kind of comments,

 /* multi-line
    style
    comments */
to temporarily remove the code that takes the robot from the foil patch to the bottle and beyond. We need to write another function: go_to_line(). This receiving program will use another new built-in function Message().
// line5.nqc
// by your-name, your-teammate's-name
// A program to:
//   start following a line after: the correct start CODE is received
//   *** for testing, the robot will stop at this point ***
//   turn at the foil patch 
//   continue following the line
//   stop when a bottle is found
//   push the bottle over the edge
//   get back to following the line
//   all the way back to the foil
#define LEFT OUT_A
#define RIGHT OUT_C
#define TS SENSOR_1
#define LS SENSOR_2
#define TOO_DARK 44
#define TOO_BRIGHT 48
#define FOIL 60
#define EDGE 36
#define CODE number-from-1-to-255
int mesg,foil,bottle;  // change the name of the variable to watch
                       // while waiting from count to mesg
task main()
{
  SetSensor(TS,SENSOR_TOUCH); // if setup here don't need to later
  SetSensor(LS,SENSOR_LIGHT); // if setup here don't need to later
  wait_start(); // wait for correct CODE
  go_to_line(); // move robot over to, or near the line
  // follow the line and watch for the foil at the same time
  start follow_line;
  foil = false;
  start find_foil;
  until (foil) ;
  stop follow_line;
  stop find_foil;
  Off(LEFT+RIGHT);
/* temporarily finish here by "commenting out" the rest
  // now robot is over the foil
  turn_right();  // get a little nearer line
  // follow line and watch for bottle at the same time
  start follow_line;
  bottle = false;
  start find_bottle_by_touch;
  until (bottle) ;
  stop follow_line;
  stop find_bottle_by_touch;
  Off(LEFT+RIGHT);
  // now robot is touching bottle
  push_bottle();  // new function for you to write
  back_to_line(); // new function for you to write
  // Now that robot is back on or just to the left of the line,
  // (Remember your robot gets lost when it starts on the right of the line!)
  // do the same things as before when you wanted to follow the line back
  // to the foil. 
  // follow the line and watch for the foil at the same time
  start follow_line;
  foil = false;
  start find_foil;
  until (foil) ;
  stop follow_line;
  stop find_foil;
  Off(LEFT+RIGHT);
  // now robot is over the foil
  end of temporarily removed section */ 
}

void wait_start()  // Version 2.0
{
  ClearMessage(); // This line is very important in a room
                  // with lots of other robots!
  mesg = 0;  // this means we cannot use 0 as a valid CODE
  SetUserDisplay(mesg,0);  // display mesg for debugging
  until (mesg == CODE) {
    mesg = Message();
  }
}

void go_to_line()
{
   // Go forward a little.
   // Turn left a little until the robot is on the line or
   // just a bit to the left of the line. 
}

// your other tasks and void functions from line4.nqc
// ...
// ...

Our second challenge today is to learn how to detect a pop bottle with a second light sensor instead of with the touch sensor. First, we will try a new NQC tool, the datalog. Then we need to learn another concept: responding to events.

The Datalog allows us to remember sensor values even after the program finishes running. It was Ok to use the little LCD to see one value at a time using the View Button or SetUserDisplay(). What if you want to see 50 values? The Datalog will remember them and later you can upload them to your computer for viewing. In this experiment we want to discover the reading on a light sensor, that is pointing forward and not down, when it gets close to a bottle. Remount your light sensor so it points the same way as the IR port. Then place a bottle in front of a robot, about 3 feet away. Set a yardstick or meterstick next to the robot. Now run a program like this and then upload the datalog and study the results. For this program we will try having the same function do different things by passing different "arguments" to the record() function. We will also try an "if" statement to decide whether an answer is the highest so far, and we will use a "local" variable called "answer" in the ping task. A local variable cannot be shared with other tasks. Our other variables were all "global", meaning they could be used by any part of the program.

// rangir.nqc
// by your-names
// A program to see how the light sensor value changes as a robot
// comes closer to the bottle.  The light will be sent out from the
// IR port in tiny pulses.  We will try 5 different messages to see
// if 0 bit, 1 bit, 2 bit, 4 bit or 8 bit pulses work best.
// This experiment requires human help.  For each of the 5 test
// messages, the robot is placed 36 inches from the robot and then
// moved 6 inches closer at each double beep.  After every placement
// tell the robot to continue by tapping the touch sensor.  When the
// robot beeps at 6 inches from the bottle, it will also play a
// SOUND_FAST_UP to prompt you to move it back to 36 inches.

#define LEFT OUT_A
#define RIGHT OUT_C
#define TS SENSOR_1
// attach the light sensor for proximity testing to Input 3
#define PS SENSOR_3
// binary 00000000
#define NO_BIT 0
// binary 01010101
#define ONE_BIT 85
// binary 01010101
#define TWO_BIT 34
// binary 00001111
#define FOUR_BIT 15 
// binary 11111111
#define EIGHT_BIT 255
#define FOREVER 0

int bits = 0;   // the message sent from IR port to bounce off the bottle
int max_answer; // the highest answer received by the light sensor recently
int inches;     // the distance from the bottle
task main ()
{
  SetSensor(PS,SENSOR_LIGHT);
  SetSensor(TS,SENSOR_TOUCH);
  CreateDatalog(0);    // erase any previous experiments
  CreateDatalog(50);   // 6 * 5 spaces, plus some extras
  SetTxPower(TX_POWER_HI); // use high power for IR port
  start ping;
  record(NO_BIT);
  record(ONE_BIT);
  record(TWO_BIT);
  record(FOUR_BIT);
  record(EIGHT_BIT);
  stop ping;
}

task ping()
{
  int answer = 0;  // This local variable is only used inside this task,
                   // max_answer is a global variable read and reset to 0
                   // in record(). We use the maximum reading because
                   // PS will bounce around a lot during a message: sometimes
                   // giving a reading when the IR light is on and sometimes
                   // when the IR light is off.
  SetUserDisplay(max_answer,0);
  until (FOREVER) {
    SendMessage(bits);
    answer = PS;
    if (answer > max_answer) {
      max_answer = answer;
    }
  }
}

void record (const int mesg)
{
   AddToDatalog(mesg); // Mark the sections of this long Datalog.
   bits = mesg;
   inches = 36;
   repeat (6) {
     until (TS == 1) ; // do nothing until human taps touch sensor
     max_answer = 0;   // reset before each measurement
     Wait(50);         // measure for a generous 1/2 second 
     AddToDatalog(inches * 100 + max_answer);
     PlaySound(SOUND_DOUBLE_BEEP);  // tell human to move closer
     inches = inches - 6; // move closer
  }
  PlaySound(SOUND_FAST_UP); // tell human to start over at 36 inches
}

From your datalog, find a good "threshold" for detecting that a bottle is near. Use the message code that gives the "earliest" warning. To be continued...

Wednesday, February 1st:
Moving the bar and find_bottle_by_light.

There are a few problems left for our robots to solve. First, the path clearing robot needs to find a way to move the bar at the entrance. The NQC for this is not hard but depends on your robot design. The robot design, in turn, depends on your strategy. Consider things like

Many ways are possible. Some of these methods need a third motor. Some need just a fixed arm or ramp for the front of the robot.

Another problem we discovered was that our robots did not always find the line in the center of the T when starting from behind the cross line on the top of the T. The problem seemed to be that you had to be very careful to place your robot the same distance behind the line each time. To make sure that the robot would not start turning until after it crossed the line, we could alter go_to_line().

void go_to_line()
{
   // Add two more lines to be sure you have crossed the start line
   // before starting to turn.
   until (LS < TOO_DARK) ;   // until robot finds the crossing line
   until (LS > TOO_BRIGHT) ; // until robot has completely crossed the line
   // Go forward a little to get closer to the center.
   // Turn left a little until the robot is on the line or
   // just a bit to the left of the line. 
}

Another problem is to write the function find_bottle_by_light. Continued from Monday...

Because we ran out of time you probably did not get to try this experiment yourself. Here is the datalog my robot produced.

0
3642
3037
2439
1844
1252
664
85
3639
3037
2439
1844
1251
664
34
3639
3038
2439
1843
1251
664
15
3637
3037
2439
1843
1251
663
255
3637
3037
2439
1844
1251
664
Rearranged for readability
message  0   85  34  15  255
36in.    42  39  39  37  37
30in.    37  37  38  37  37
24in.    39  39  39  39  39
18in.    44  44  43  43  44
12in.    52  51  51  51  51
 6in.    64  64  64  63  64
We see that
  1. It does not make much difference what number we use as a message. Baum's suggestion of using 85 as the message is as good as any.
  2. The robot begins to notice the bottle at about 18 inches away.
  3. The robot is pretty certain a bottle is in range at about 12 inches away.
  4. The threshold of 52 suggested in Baum's book is likely to work for our robots as well.

Now write a program that heads toward the bottle and stops when the following event is detected: The light sensor reading is at or above the threshold you found in the previous experiment.

// prox.nqc
// by your-names
// A program to travel toward a bottle and stop when the proximity
// sensor detects the bottle.
// Similar to a program in Chapter 13 of Baum's book.
#define LEFT OUT_A
#define RIGHT OUT_C
#define PS SENSOR_3
#define PS_EVENT 1
#define PS_THRESHOLD 52
#define PS_MESSAGE 85
#define FOREVER 0
task main()
{
  SetSensor(PS,SENSOR_LIGHT);
  // Set our event to "monitor" or watch for, to be when the light sensor,
  // used as a proximity sensor, reads above the threshold.
  SetEvent(PS_EVENT,PS,EVENT_TYPE_HIGH);
  SetUpperLimit(PS_EVENT,PS_THRESHOLD);
  OnFwd(LEFT+RIGHT); // go forward
  SetTxPower(TX_POWER_HI); // use high power for IR port
  monitor (EVENT_MASK(PS_EVENT)) { // Start watching for a
    until (FOREVER) {              // proximity-sensor-high event.
      SendMessage(PS_MESSAGE);
    }
  } catch {                       // When a proximity-sensor-high event is
    PlaySound(SOUND_DOUBLE_BEEP); // noticed or caught, stop sending messages
    Off(LEFT+RIGHT);              // forever and play a tone and stop the
  }                               // motors.
}

Now back to our mission problem: When you have cleared off the bottles on the line and your robot is back at the foil patch, your robot could do something like

void find_bottle_by_light()
{
  // set proximity event
  until (LS < TOO_DARK) { // stop at the tape on the other side of the oval
    // go forward a little 
    // turn left 90 degrees
    // turn back right 180 degrees
    // turn left 90 degrees
  }
  // when event happens stop turning and go straight to bottle
  // until touch sensor touches
}

    

You might want to practice a little more with your range-finder. Alter your robot's program to turn slowly, instead of going straight forward. Then start your robot near the bottle, but pointing away from the bottle. Then see if it will stop turning, and if it is then pointing at the bottle.

Monday, February 8th:
Warm up competition.

The competition will be a time trial. The total time will be the sum of a team's 3 fastest runs. The team with the lowest total time wins. Use 1 robot per team. The robot must

  1. Wait behind the line on the left side of the T, until the message "3" is received.
  2. Go to the center of the T and start following the line.
  3. Continue on until you encounter a bottle placed on a straight section of the loop.
  4. Push the bottle over the edge.
  5. Return to the foil and stop.

At the end of the competition you must turn in your program.

You can use your sender.nqc program as a practice starter. Alternatively, this is the program we will use as a starter's stopwatch. This program shows one use of the built in timers. Notice that Timer(0) gives 1/10ths of a second. You are used to Wait(1/100ths-of-a-second). FastTimer(0) will give you the 1/100ths of a second.

// starter.nqc
// A program to send a start message and act as a
// stopwatch.
#define STOP_BUTTON SENSOR_1
#define CODE 3
#define FOREVER 0
int time = 0;
task main()
{
  SetSensor(STOP_BUTTON,SENSOR_TOUCH);
  SetUserDisplay(time,1);
  ClearTimer(0);
  PlaySound(SOUND_FAST_UP);
  repeat (5) {         // Send message several times to be sure
    SendMessage(CODE); // it is received in a room crowded with
    Wait(15);          // other robots.
  }
  until (STOP_BUTTON == 1) {
    time = Timer(0);
  }
  until (FOREVER) ;    // Keep the final time displayed.
}

10 Things to Remember

  1. Read the directions first: Before you start, be sure you know where you are going. When you think you are done, read those directions again.
  2. Make a plan: If you do not make a plan before you start programming you will waste a lot of time with false starts. Worse, if you don't have a plan, you will have no clue why, when or where your robot started to go awry.
  3. Be prepared: You can waste a lot of time because you forgot to check your robot's batteries and have no spares. Laptop batteries will always run down if you forgot the power cord.
  4. Add comments to your programs: You will have many jobs in the future. You will come to hope that the last person at your next job wrote down what she did and why she did it. To keep a job, you need to be able to tell others what you have done and explain why and how you did it.
  5. Debugging takes thought: Thought before, in the form of adding clues as to what your robot is doing, like playing sounds and displaying variables and keeping a Datalog. Thought during, in the form of not just changing a bunch of things, but making a plan like, "If we change this then we expect that will happen.
  6. Keep your workspace clean and organized: Time spent searching for lost, small parts is wasted. Save your work frequently. Use names for your programs that you will remember. Make backups.
  7. Remember robotics is a real world engineering discipline: This is not an exact science. No two runs of your robot will be precisely the same.
  8. Learn to work with your team: The whole is greater than the sum of its parts.
  9. Use the Internet: There are many people, like Dave Baum who wrote NQC, who have posted many, many Web pages that will be of help in your future studies.
  10. Have fun: Even when studying important subjects like embedded systems and robotics, it's Ok to play with Legos.

Reported by John M. Miller M.D.

Revised February 4, 2006