I was looking for an intermediate/beginner level project so I made a line following robot using a motor shield, a bunch of switches, and homemade IR sensors. I went off the QTR-1A diagram http://www.pololu.com/docs/0J19/all and used cardboard and paper to try to isolate the sensors as best as possible. Using three sensors had the best options for detecting whether the robot is on, left, right or maybe on the line. Three switches allow for calibration values to be read for the first three states and the fourth state is assumed if none of the other states are met. The motion is pulsed because the DC motors tend to just make it tear right through any lines if they are powered constantly. Pictures and video at http://www.flickr.com/photos/8231259@N05/sets/72157618732764488/
/*
Line Following Robot Code for Arduino and Motorshield
by D Short
**forgive me if this isn't pretty code to you, I've never had a good CS course**
*/
#include <AFMotor.h>
int sensorpin[3] = {2,1,0};
// values to allow for some variance in the trigger values
int notlinethresh[3] = {100, 90, 100};
// arrays for current reading, calibrated with middle, left and right on line
int sensorvalue[3], online[3], lefton[3], righton[3];
//value to store how many times the car has opted to travel straight or turned
int straightcount, turncount;
//various control switches
int switch1pin = 2;
int switch2pin = 13;
int switch3pin = 4;
int switch4pin = 5;
int count;
//switch values
int switch1, switch2, switch3, switch4;
AF_DCMotor motorL(3, MOTOR12_64KHZ);
AF_DCMotor motorR(2, MOTOR12_64KHZ);
void setup() {
Serial.begin(9600);
straightcount=0;
turncount = 0;
}
void loop() {
//turncount stores how many times a turn decision was reached. t is incremented if a decision is reached
int i, t;
// how many times motors are driven straight before pausing to prevent a build up of momentum
int straightmax = 20;
int turnmax = 50;
int turnrest = 100;
int corner;
//checks various control switches. analog pins had to be used as digital pins
switch1 = digitalRead(switch1pin);
switch2 = digitalRead(switch2pin);
switch3 = analogRead(switch3pin);
switch4 = analogRead(switch4pin);
t=0;
corner = 0;
// for loop to read ir sensors into array
for( i=0; i<=2; i++){
sensorvalue[i] = analogRead(sensorpin[i]);
}
//this sets the left sensor on the line calibration
if(switch1==HIGH){
Serial.println("lefton");
for( i=0; i<=2; i++){
// for each sensor three readings 50 ms apart are taken and averaged
lefton[i] = getAverage(sensorpin[i], 2, 50);
Serial.print(lefton[i]);
Serial.print( "\t");
}
Serial.print("\n");
}
//the right sensor on the line calibration
if(switch3 >= 900){
Serial.println("righton");
for( i=0; i<=2; i++){
righton[i] = getAverage(sensorpin[i], 2, 50);
Serial.print(righton[i]);
Serial.print( "\t");
}
Serial.print("\n");
}
//middle sensor on the line calibration
if(switch2 == HIGH){
Serial.println("online");
for( i=0; i<=2; i++){
online[i] = getAverage(sensorpin[i], 2, 50);
Serial.print(online[i]);
Serial.print( "\t");
}
Serial.print("\n");
}
//switch 4 controls whether the car should begin to decide what direction to turn
if (switch4 >= 900){
t=0;
//first checks to see if the car needs to turn right
if( sensorvalue[2] >= righton[2] - notlinethresh[2]){
//checks to see if a delay is needed to prevent too much speed is needed
if(turncount <= turnmax){
motorR.setSpeed(255);
motorL.setSpeed(255);
motorL.run(FORWARD);
motorR.run(BACKWARD);
Serial.println("lil to the right");
t++;
corner++;
/*corner is so that if all three sensors are dark, the car decides to go left.
if a side and middle are black, like a 90 degree turn, it turns.*/
turncount++;
}
//pauses if too many right turns occur and the car moves too fast for good sensor readings
else {
motorL.run(RELEASE);
motorR.run(RELEASE);
delay(turnrest);
Serial.println("lil to the right");
t++;
corner++;
turncount = 0;
}
}
//checks if left turn is needed and also this checks to see if a right turn has already been made (helps with 90 degree corners)
if( sensorvalue[0] >= lefton[0] - notlinethresh[0] && corner < 1){
if(turncount <= turnmax){
motorL.setSpeed(255);
motorR.setSpeed(255);
motorR.run(FORWARD);
motorL.run(BACKWARD);
Serial.println("lil to the left");
t++;
corner++;
turncount++;
}
else {
motorL.run(RELEASE);
motorR.run(RELEASE);
delay(turnrest);
Serial.println("lil to the left");
t++;
corner++;
turncount = 0;
}
}
/*ok this is nasty but it checks to make sure if the two side sensors are on white and the middle is
on black and that no turns have already been made*/
if( sensorvalue[0] <= online[0] + notlinethresh[0] && sensorvalue[2] <= online[2] + notlinethresh[2]
&& sensorvalue[1] >= online[1] - notlinethresh[1] && corner < 1)
{
if(straightcount<=straightmax){
motorR.setSpeed(220);
motorL.setSpeed(220);
motorL.run(FORWARD);
motorR.run(FORWARD);
Serial.println("Yay");
t++;
straightcount++;
}
if(straightcount>straightmax){
motorL.run(RELEASE);
motorR.run(RELEASE);
straightcount=0;
delay(200);
}
}
//if t==0 then no decision was made and the car moves ahead slightly slower than if the middle sensor were to be on black
if(t==0){
if (straightcount<= straightmax){
motorR.setSpeed(175);
motorR.run(FORWARD);
motorL.setSpeed(175);
motorL.run(FORWARD);
Serial.println("no match");
straightcount++;
}
if (straightcount > straightmax){
motorL.run(RELEASE);
motorR.run(RELEASE);
straightcount = 0;
delay(200);
}
}
}
//if the switch is in calibration mode then the car will stand still
else
{
motorL.setSpeed(250);
motorL.run(RELEASE);
motorR.setSpeed(250);
motorR.run(RELEASE);
}
}
/*getaverage takes readings a set number of times and averages them.
this has some issues if you want anything other than 3 readings*/
int getAverage(int pin, int times, int rate) {
int val[3];
int sum;
int i, average;
for(i=0; i<=times; i++){
val[i] = analogRead(pin);
delay(rate);
}
for(i=0; i<=2; i++){
sum = sum + val[i];
}
average = sum / 3;
return average;
}