Milestone 1

Line Tracking Robot

Objective

The objective of Milestone 1 was to modify the robot such that it had line tracking and maze traversing functionality. By the end of the lab, we should have a robot that can follow a line and traverse a grid in a figure 8.

Materials

  • 3 QRE1113 Sensors on SparkFun breakout boards
  • Breadboard
  • 3D printed legs
  • Wires
  • Resistors

Lab Work

We got the robot to follow a line, using three line sensors at the front of the robot.

Intuition:

  • If the middle sensor sees white and the left and right sensors see black, go straight
  • If middle and right sensor sees black but left sees white, bump left
  • If middle and left sensor sees black but right white, bump right

Hardware:

We decided to implement three line sensors in the front of the robot. They are spaced close together, but apart enough such that only one sensor should be on the white line at a time unless the robot reaches an intersection. Use of the analog pins is a careful consideration, as there are very few, but we need a minimum of three line sensors for our approach. We chose three sensors because we need two on either side to detmine left or right, and the one in the middle to confirm the robot is on top of the line.

Each line sensor can detect a range of values, and we set the thresholds based on what we observed in lab. We set the threshold to be greater than 700 is black and under 450 is white. We implemented this in line detection portion and the figure eight portion. We found it to be easier to make the white threshold higher because we didn't want the robot to start making corrections or turns simply because it was having difficulty sensing the white line.

Wiring:

  • Servos are connected to digital pins 3 (right servo) and 5 (left servo) as well as power and ground.
  • Line sensors are conected to analog pins A0 (middle sensor), A1 (left sensor), and A2 (right sensor) as well as power and ground. These line sensors are analog inputs.
This is the circuit on the robot which successfully implements line sensors and servo motors.

Software:

Line Correction

We decided our code would most easily implemented with functions for the different movements of the robot: straight, turn left, and turn right.

The logic of which movement is necessary depends on the line sensors. This logic takes place in our loop, where the robot is continuously reading the line sensor values and making decisions based on those values in relation to our white/black threshold (greater than 700 is black, less than 450 is white)

We use delays to allow the robot to execute the movement (either moving straight or turning) for a short period of time before looping back through and detecting to what extent that helped. We choose a delay of 30 through exxperimentation, deciding that it was enough time to make a movement, but not too much time such that the robot would go off track.

 
#include <Servo.h>

Servo rightServo; // right
Servo leftServo; // left
int speed1 = 0;
int speed2 = 103;
int speedStop = 90;

int lineMiddle = A0;
int readM; // middle line sensor reading

int lineLeft = A1;
int readL; //left line sensor reading

int lineRight = A2;
int readR; //right line sensor reading
int num = 0; //number of turns by robot

void setup() {
  Serial.begin(9600);
  rightServo.attach(3);
  leftServo.attach(5);
  pinMode(lineMiddle, INPUT);
  pinMode(lineLeft, INPUT);
  pinMode(lineRight, INPUT);
}

// robot moves straight by having wheels move in same directions
void straight(){
  rightServo.write(speed1);
  leftServo.write(speed2);
}

void turnLeft(){
  rightServo.write(speed1);
  leftServo.write(90);
}

void turnRight(){
  rightServo.write(90);
  leftServo.write(speed2);
}


void loop() {
  readM = analogRead(lineMiddle);
  readL = analogRead(lineLeft);
  readR = analogRead(lineRight);
  // > 700 is black
  // < 450 is white

  // middle detects, left and right don't
  if (readM < 450 && readL > 700 && readR > 700) {
    straight();
    delay(300);
  }
  // correct path if the robot is on the right side of the line
  else if (readM > 700 && readL < 450 && readR > 700) {
    turnLeft();
    delay(100);
    turnRight();
    delay(30);
    straight();
  }
  // correct path if the robot is on the left side of the line
  else if (readM > 700 && readL > 700 && readR < 450) {
    turnRight();
    delay(100);
    turnLeft();
    delay(30);
    straight();
  }
}
          
Robot follows white line

Figure Eight

We then were tasked with getting our robot to draw a figure 8 on the grid provided in lab. This meant that we couldn't hardcode how long it took for each turn, as it was dependent on the grid itself.

We modified out movement functions to include "bumpRight()" for example to differentiate between line correcting and full 90 degree turns.

 
#include <Servo.h>

Servo rightServo; // right
Servo leftServo; // left
int speed1 = 85;
int speed2 = 95;
int speedStop = 90;

int lineMiddle = A0;
int readM; // middle line sensor reading

int lineLeft = A1;
int readL; // left line sensor reading

int lineRight = A2;
int readR; // right line sensor reading
int num = 0; // number of turns by robot

void setup() {
  Serial.begin(9600);
  rightServo.attach(3);
  leftServo.attach(5);
  pinMode(lineMiddle, INPUT);
  pinMode(lineLeft, INPUT);
  pinMode(lineRight, INPUT);
}

// robot moves straight by having wheels move in same directions
void straight(){
  rightServo.write(speed1);
  leftServo.write(speed2);
}

// make wheels stop spinning (might need to manually calibrate wheels)
void servoStop(){
  rightServo.write(speedStop);
  leftServo.write(speedStop);
}

// robot turns right by having wheels move in opposite directions, with right wheel moving forward
void turnRight(){
  rightServo.write(speed2);
  leftServo.write(speed2);
  delay(1050);
}

// robot turns left by having wheels move in opposite directions, with left wheel moving forward
void turnLeft(){
  rightServo.write(speed1);
  leftServo.write(speed1);
  delay(1050);
}

void bumpLeft(){
  rightServo.write(speed1);
  leftServo.write(90);
}

void bumpRight(){
  rightServo.write(90);
  leftServo.write(speed2);
}

void loop() {
  readM = analogRead(lineMiddle);
  readL = analogRead(lineLeft);
  readR = analogRead(lineRight);
  // > 700 is black
  // < 450 is white

  // middle is black and left and right are white
  // if middle and right, then turn. If only right, then middle correct.
  if (readM < 450 && readR < 450 && readL < 450 && num < 3) { // turn right
    delay(850); // remove the delay if sensors are moved near the wheels; otherwise find a good delay time
    turnRight();
    num++;
  }
  else if (readM < 450 && readR < 450 && readL < 450 && num >= 3 && num < 6) { // turn left
    delay(850); // remove the delay if sensors are moved near the wheels; otherwise find a good delay time
    turnLeft();
    num++;
  }
  else if (readM < 450 && readL < 450 && readR < 450 && num == 6) {
    delay(100); // modify this value as needed
    servoStop();
  }
  // middle is black
  else if (readM < 450 && readL > 700 && readR > 700) { // middle detects, left and right don't
    straight();
    // delay(300);
  }
  else if (readM > 700 && readL < 450 && readR > 700) { // correct path if the robot is on the right side of the line
    bumpLeft();
    delay(150);
    bumpRight();
    delay(50);
    straight();
  }
  else if (readM > 700 && readL > 700 && readR < 450) { // correct path if the robot is on the left side of the line
    bumpRight();
    delay(150);
    bumpLeft();
    delay(50);
    straight();
  }
  else{
    straight();
  }
          
Robot traverses figure eight

Robust Code

To make our turns more robust, we added more logic to our turnRight() and turnLeft() function to remove hardcoding delays that only work for certain robot speeds. Instead we continously read the middle line sensor, and stop turning when the middle line sensor detects white again.

 
  void turnLeft(){
  rightServo.write(speed1);
  leftServo.write(speed1);
  delay(300);
  readM = analogRead(lineMiddle);
  while(readM >700){
    readM = analogRead(lineMiddle);
    rightServo.write(speed1);
    leftServo.write(speed1);
  }
}