BEETS Camp 2013

Hands-on Engineering Activity with Dominic Canare and Tom McGuire

It's 2013, and driverless cars are only recently becoming relevant. The automated vehicle brings challenges from multiple engineering disciplines. By building small-scale vehicles, students will identify and overcome challenges while exploring the engineering process, working as a team, and being creative.

Project Synopsis

Working in groups of two or more, students will design and build an autonomous or semi-autonomous vehicle. Each vehicle must sense one or more aspect of its environment and respond in an observable manner.

Students will also need to design a course for their vehicle to navigate. The course may consist of whatever is available to the students, but it should be easy to move and/or tear-down and rebuild.

On the final day, students will evaluate other group projects based on the the complexity/difficulty of the course they have designed for their vehicle, and the ability of their vehicle to negotiate their course. The highest scoring team will be awarded a prize!

Inspiration Videos

Tools and parts

Students will be provided with a kit, including DC motors, an Arduino, photocells, rods, tubing, wire, foam, plexiglass, glue, rubber bands, screws, etc. Students will also be provided with instruction to design pieces and parts on the foam-cutting and laser cutting machines.

Documentation

Each group will need to prepare a report on their course and vehicle. The report should include (but is not limited to) the following:

  • A list of members and their roles. E.g.:
    • Project Manager
    • Designer
    • Mechanic
    • Programmer
    • Reporter
    • Etc.
  • Course specifications/obstacles
  • Vehicle specifications (planned) E.g.:
    • Physical dimensions
    • Sensors / reaction
    • Response time
    • Course specifications
    • Min/max speed of vehicle
    • Cargo bearing capacity
    • Additional capabilities
    • Etc
  • Description of completed project and process
    • Include challenges and solutions
    • Compare completed project to target specifications
    • Lessons learned
    • Areas for improvement
    • Description of course
    • How does the course play to your vehicles strengths/weaknesses?
  • How can your vehicle be applied to the real world? What limits it from being used in passenger vehicles? How could those challenges be overcome?

Building blocks and samples from Tom and Dom

Controlling a DC motor with a transistor

A transistor can function like a switch which we can be controlled electronically.

In this example, the Arduino controls the speed of a motor by rapidly flipping the "switch" off and on using Pulse Width Modulation.

								// DC Motor Control Example

								void setup() {                // setup() - called once at the very beginning
								  pinMode(A2, INPUT);         //   Potentiometer's value is read on Analog pin 2
								  pinMode(3, OUTPUT);         //   Motor speed is controlled using Digital pin 3

								  digitalWrite(3, 0);         //   Turn the motor off initially
								}

								void loop() {                 // loop() - called repeatedly
								  int value = analogRead(A2); //   Read the current value (10 bits)
								                              //   NOTE: We can only send 8 bits of data to the
								                              //     analogWrite function below. So we must the 2
								                              //     least significant digits from the value we
								                              //     read in.

								  value = value << 2;         //   Drop the 2 least significant digits

								  analogWrite(3, value);      //   Flip the transistor on and off really fast
								                              //   Higher values = transistor on longer
								                              //   0 = off, 128 = 50%, 255 = 100%
								}
							

Controlling a DC motor with a driver

The Pololu motor driver is a circuit designed to simplify bi-directional motor control.

In this example, the motor spins in one direction for one second, then the opposite direction for one second, and repeats this pattern continuously.

Note: because pin 9 controls the power to the motor, we can pulse a PWM signal using the analogWrite function, instead of digitalWrite. Remember that analogWrite expects an integer value between 0 and 255. The closer the number is to 255, the more power the motor will receive.

								// Bi-direction DC Motor Control with Pololu Driver

								void setup() {             // setup() - called once at the very beginning
								  pinMode(9, OUTPUT);      //   Pin 9 gives power to the motor
								  pinMode(8, OUTPUT);      //   Pin 8 controls the direction of the motor

								  digitalWrite(9, HIGH);   //   Turn the motor on
								}

								void loop() {              // loop() - called repeatedly
								  digitalWrite(8, HIGH);   //   Spin the motor in one direction
								  delay(1000);             //   Let it spin that way for one second

								  digitalWrite(8, LOW);    //   Spin the motor in the opposite direction
								  delay(1000);             //   Let it spin that way for one second
								}
							

Controlling two DC motors with a driver

In the example above, pins 9 and 8 control the power and direction of the motor respecitvely. A HIGH signal on pin 9 will enable the motor and a LOW will disable it. A HIGH signal on pin 8 will spin the motor in one direction (provided that motor is on), and a LOW value will spin the motor in the opposite direction.

A second motor can easily be connected to the motor driver chip. If you look at the chip, you'll see labels marked "AEN" and "APH". "AEN" controls the power to Motor A, and "APH" controls its direction. Similarly, "BEN" and "BPH" control the power and direction of Motor B respectively.

In the example below, Motor A has power/direction connected to pins 8/9. Motor B has power/direction connected to pins 5/6.

								int motor1Power = 8;
								int motor1Direction = 9;

								int motor2Power = 5;
								int motor2Direction = 6;

								void setup(){
								  pinMode(motor1Power, OUTPUT);
								  pinMode(motor1Direction, OUTPUT);
								  
								  pinMode(motor2Power, OUTPUT);
								  pinMode(motor2Direction, OUTPUT);
								  
								  Serial.begin(9600);
								  delay(2000);
								}

								long wait = 3000;

								void loop(){
								  Serial.println("Forward 1");
								  digitalWrite(motor1Power, HIGH);
								  digitalWrite(motor1Direction, LOW);
								  delay(wait);
								  
								  Serial.println("Forward 2");
								  digitalWrite(motor2Power, HIGH);
								  digitalWrite(motor2Direction, LOW);
								  delay(wait);
								  
								  Serial.println("Reverse 1");
								  digitalWrite(motor1Direction, HIGH);
								  delay(wait);
								  
								  Serial.println("Reverse 2");
								  digitalWrite(motor2Direction, HIGH);
								  delay(wait);
								  
								  Serial.println("Motor 1 off");
								  digitalWrite(motor1Power, LOW);
								  delay(wait);
								  
								  Serial.println("Motor 2 off");
								  digitalWrite(motor2Power, LOW);
								  delay(wait);
								}
							

Sensing bumps with "whiskers"

This is a very simple sensor. Whiskers can be made out of simple metal wire, which can be connected to the Arduino. When a whisker touches an object, the object pushes the whisker to another metal contact which is also connected to the Arduino. This closes a circuit, just like a switch.

In this example, the Arduino will enable LEDs corresponding to the whiskers when they are activated.

								// "Whisker" / Bump Sensing Example

								// Refer to LED pins using a name instead of a number
								int LED_R = 11;                      // Red LED is on pin 11
								int LED_G = 12;                      // Green LED is on 12

								// Refer to whisker pins using a name instead of a number
								int WHISKER_L = A4;                  // Left whisker is on analog 4
								int WHISKER_R = A5;                  // Right whisker is on analog 5

								void setup() {                       // setup() - called once at the beginning
								  pinMode(LED_RED, OUTPUT);          //   We are outputting data to the LEDs
								  pinMode(LED_GREEN, OUTPUT);

								  pinMode(WHISKER_L INPUT);          //   And receiving data from the whiskers
								  pinMode(WHISKER_R, INPUT);

								  digitalWrite(WHISKER_L, HIGH);     //   Set a default value for the whiskers
								  digitalWrite(WHISKER_R, HIGH);     //   This prevents environment noise
								}

								void loop() {                        // loop() - called repeatedly
								  if(digitalRead(WHISKER_L)==LOW){   //   If the left whisker is bumped
								      digitalWrite(LED_R, HIGH);     //     Turn ON the red LED
								  }else{                             //   If it's not bumped
								      digitalWrite(LED_R, LOW);      //     Turn OFF the red LED
								  }

								  if (digitalRead(WHISKER_R)==LOW){  //   If the right whisker is bumped
								      digitalWrite(LED_G, HIGH);     //     Turn ON the green LED
								  }else{                             //   If it's not bumped
								      digitalWrite(LED_G, LOW);      //     Turn OFF the green LED
								  }
								}

Sensing light with a photocell

The photocell is a semiconductor whose electrical resistance is dependent on the amount of light shining into it.

In this example, the photocell is connect to an analog pin, so that its value can be read. With that information, the Arduino adjusts the brightness of the LED.

								// Photocell Example

								void setup(){                       // setup() - called once at the beginning
								  pinMode(A0, INPUT);               //   We'll read the photocell on Analog pin 0
								  digitalWrite(A0, HIGH);           //   Set a default value

								  pinMode(3, OUTPUT);               //   We will control the LED on pin 3
								  digitalWrite(3, 0);               //   Turn the LED off
								}

								void loop(){                        // loop() - called repeatedly
								  int value = analogRead(A0);       //   Read the photocell's value

								  float percent = (float)value/340; //   By experimenting, we found that 340 is
								                                    //   about the highest possible value
								                                    //   So we'll calculate a number as a
								                                    //   percentage of 340

								  int output = 255 * percent;       //   The highest value we can send to the
								                                    //   LED is 255, so find the % of that

								  if(output > 255){                 //   If, somehow, we've calculated a higher
								    output = 255;                   //   number than 255, cap it.
								  }

								  analogWrite(3, output);           //   Turn the LED on and off really fast
								                                    //   So fast, the LED seems dimmer/brighter
								                                    //   Higher values = LED on longer
								                                    //   0 = off, 128 = 50%, 255 = 100%
								}
							

Sensing proximity with an IR emitter/receiver

Infrared light is invisible to the human eye, but not to this sensor. By shining IR light and detecting its reflection, the sensor can determine how close or far an object is.

In this example, the distance is measured first and a far object activates the green LED. A somewhat close object activates the yellow LED, and a very close object activates the red LED.

The servo sweeps the sensor back and forth, giving it a great field of view. This is optional.

								// IR Proximity Sensing Example

								#include <Servo.h>            // Load a library which makes it really easy to
								Servo myServo;                // control a servo.

								void setup(){                 // setup() - called once at the beginning
								  pinMode(A0, INPUT);         //   Read the distance sensor on Analog pin 0

								  pinMode(2, OUTPUT);         //   Pin 2 controls the green LED
								  pinMode(3, OUTPUT);         //   Pin 3 controls the yellow LED
								  pinMode(4, OUTPUT);         //   Pin 3 controls the red LED

								  myServo.attach(9);          //   Pin 9 controls the servo
								  myServo.write(0);           //   Set the servo to 0 degrees
								}

								int dir = 1;                  // Keep track of which direction we're turning
								int pos = 0;                  // Keep track of current servo position

								void loop(){                  // loop() - called repeatedly
								  int led_r = 0;              //   Only one LED will be enabled. Assume all
								  int led_y = 0;              //   will be turned off until we know which one
								  int led_g = 0;              //   will be turned on

								  int value = analogRead(A0); //   Read the current distance

								  if(value < 100){            //   If the distance is small
								    led_r = 1;                //     we should enable the red LED
								  }else if(value < 500){      //   If the distance is moderate
								    led_y = 1;                //     we should enable the yellow LED
								  }else{                      //   If the distance is large
								    led_g = 1;                //     we should enable the green LED
								  }

								  digitalWrite(2, led_r);     //   Update the LED's
								  digitalWrite(3, led_y);
								  digitalWrite(4, led_g);

								  if(pos > 179){              //   If the servo is far right then
								    dir = -1;                 //     we should start moving left
								  }else if(pos < 1){          //   If the servo is far left then
								    dir = 1;                  //     we should start moving right
								  }
								  pos += dir;                 //   Increase the position in the correct direction

								  myServo.write(pos);         //   Tell the servo where it should be
								  delay(20);                  //   Do nothing for 20ms (gives time to move)
								}
							

Sensing IR remote signals

When you press a button on a remote control, the remote flashes an infrared LED in a specific pattern unique for each button. Using the arduino, we can decode this pattern to distinguish between different button presses.

In this example, we will control 4 LED's by listening for 4 unique remote buttons.

								// IR Remote Decoding Example

								#include <IRremote.h>                  // Load a library which makes remote
								IRrecv irrecv(9);                      // codes easy. The receiver is on pin 9

								typedef struct {                       // For each LED, we need to keep track of:
								  long code;                           //   Its remote code
								  boolean value;                       //   Its current on/off state
								  int pin;                             //   Which pin it's connected to
								} RemoteLED;

								State leds[4] = {
								  { 4278200535l, false, 7 },           // Power button
								  { 4278203085l, false, 6 },           // 1 button
								  { 4278192885l, false, 5 },           // 2 button
								  { 4278201045l, false, 4 }            // 3 button
								};

								void setup(){                          // setup() - called once at the beginning
								  for(int i=0; i<4; i++){              // For each RemoteLED,
								    pinMode(leds[i].pin, OUTPUT);      //   Set the associated pin to output
								    digitalWrite(leds[i].pin, LOW);    //   And turn the LED off
								  }

								  irrecv.enableIRIn();                 // Start the IR receiver
								}

								void loop() {                          // loop() - called repeatedly
								  decode_results results;              //   create a variable to hold IR results
								  if(irrecv.decode(&results)){         //   If any code is read from the remote
								                                       //     Check if code matches any of ours

								  for(int i=0; i<stateCount; i++){      // For each of our codes
								    if(results.value == leds[i].code){  //   Check to see if the input matches
								      leds[i].value = !leds[i].value;   //     Toggle the state of the LED

								      digitalWrite(leds[i].pin, leds[i].value);
								                                        //     Update the actual LED
								      break;                            //     Stop looking for matches
								    }
								  }

								  irrecv.resume();                      // resume the IR receiver
								}
							

A simple Tachometer

A tachometer can sense the speed at which an object is sensing in - i.e., the number of rotations per minute or second. This can be accomplished by coloring half of the wheel black, and half white. Then, with a photocell (see above), the Arduino can detect when the wheel changes from white to black. This is counted as one rotation.

In this example, the tachometer senses the speed of a spinning wheel and enables a red LED if it's stopped, a yellow LED if it's slow, and a green LED if it's fast.

The Arduino will also change the speed of the motor every 5 seconds.

								// Simple Tachometer Demo

								void setup(){                             // setup() - called once at the beginning
								  pinMode(A0, INPUT);                     //   Analog pin 0 used for reading photocell

								  pinMode(11, OUTPUT);                    //   Pin 11 used to control the motor
								  pinMode(7, OUTPUT);                     //   Pin 7 used for red LED
								  pinMode(8, OUTPUT);                     //   Pin 8 used for yellow LED
								  pinMode(9, OUTPUT);                     //   Pin 9 used for green LED

								  digitalWrite(A0, HIGH);                 //   Set a default value for the photocell
								  digitalWrite(11, HIGH);                 //   Set the motor to full speed

								  Serial.begin(9600);                     //   Enable PC communications for debugging
								}

								long lastSample = 0;                      // Timestamp of last sensor read
								long lastChange = 0;                      // Timestamp of last motor speed change
								int state = 0;                            // Wheel state (black or white)
								int count = 0;                            // # of times the wheel has spun since last report

								int sampleDelay = 500;                    // How long to sample sensor before reporting
								int threshold_b = 40;                     // Minimum value for black detection
								int threshold_w = 30;                     // Maximum value for white detection

								int currentSpeed = 255;                   // Speed of motor

								void loop(){                              // loop() - called repeatedly
								  int value = analogRead(A0);	          //   Read the photocell
								  /* To test sensor readings, uncomment the next two lines */
								  //Serial.println(value);
								  //return;

								  if(value > threshold_b && state == 0){  //   If black is detected and last one was white
								    state = 1;                            //     Set the current state to black
								    count++;                              //     Increase the count of rotations
								  }else if(value < threshold_w){          //   If white is seen
								    state = 0;                            //     Set the current state to white
								  }

								  // LED Control
								  if(millis()-lastSample > sampleDelay){  //   If enough time has elapsed
								    Serial.print("count = ");             //     Send data to the PC
								    Serial.println(count);
								    int led_r = 0;                        //   Assume none of the LEDs are disabled
								    int led_y = 0;                        //   Until we know which one to enable
								    int led_g = 0;

								    if(count < 1){                        //   If no rotations were counted
								      led_r = 1;                          //     Enable the red LED
								    }else if(count < 6){                  //   If rotation count > 0 but < 6
								      led_y = 1;                          //     Enable the yellow LED
								    }else{                                //   If rotation count >= 6
								      led_g = 1;                          //     Enable the green LED
								    }

								    digitalWrite(7, led_r);               //   Give the LEDs their new values
								    digitalWrite(8, led_y);
								    digitalWrite(9, led_g);

								    count = 0;                            //   Reset the rotation counter
								    lastSample = millis();                //   Reset our reporting timestamp
								  }

								  // Motor Control
								  if(millis()-lastChange > 5000){         //   If 5 secs since last motor change
								    if(currentSpeed >= 255){              //     If the motor is currently maxed out
								      currentSpeed = 64;                  //       Set the motor speed to someting slow
								    }else{                                //     If it's not maxed out
								      currentSpeed *= 2;                  //       Double its current speed
								    }
								    if(currentSpeed > 255){               //     Highest possible motor speed is 255
								        currentSpeed = 255;
								    }

								    analogWrite(11, currentSpeed);        //     Now that we know the new speed, set it
								    lastChange = millis();                //     Reset our motor change timestamp
								  }
								}
							

Other resources

Most of the tools we're using for this course are 100% Open Source and FREE!

Project Judging Form

Have fun!