Autonomous Line-following Robot

Other than writing code, I enjoy building things as a hobby. I made a line following robot using DIY components and open-source Arduino platform. The purpose of this robot was to transport stuff between two users back and forth. Robot moves from point A to point B along a line defined by the black sticky tape on the floor, while avoiding collision with a passing object (e.g. a person) in its way. When reaching the end, robot stops and waits for the user to pick up the stuff (a basket with things in it). Once user replaces the basket back on the robot, it turns back and moves towards the other end.

The whole project took around 5-6 months (few mins/hours here and there at night and weekends), where I taught myself various aspects of robot design - mechanical engineering, electronics and computer programming. I chose Arduino microcontroller for the brain of the robot, since it's an open-source platform with tons of resources available online and YouTube videos. The most challenging part in this project for me was the physical design of the robot itself, with the constrains on weight distribution, power management and its stability.

I utilized three sensors in the robot:

  1. An array of 3 reflectance sensors on the underside of the robotic platform to detect and follow the line.
  2. An ultrasonic sensor mounted on a servo motor at the front to detect and avoid collision with an object - a moving person.
  3. Light-dependent resistor (LDR) embedded in the top platform to detect when the user lifts the basket off and puts it back, signaling that robot needs to turn back and move towards the other end.

Following are a series of videos describing the robot evolution while I implemented and optimized different features:

Version #1: first try...

Version #2: Moved wheels close to the center, which led to better weight distribution and improved robot's agility.

Version #3:

Implemented a series of changes:

1. Better power management by having separate battery packs for the Arduino board and the motors.

2. Refined sensors and the code for the robot to follow a thinner line.

3. Added code for the robot to stop at the "T" intersection.

Version #4 (Final design):

Implemented following features:

1. Added the four legs and top platform; embedded the LDR sensor into the top platform, which senses the basket movement.

2. Got all three sensors (LDR sensor at the top, ultrasonic object avoidance sensor at the front and reflectance sensors at the bottom) to work together in the code.

Robot is working as expected. If a person comes in the way, robot stops and waits until the path is cleared. Robot stops at the end of the line (T intersection), waits for the user to pick up the basket. When user puts the basket back on the platform, robot turns back and starts moving towards the other end.

Here is the code I wrote, which runs this autonomous robot:

// Ved P. Sharma, May 27, 2013
// Using analog pins A1-4 for H-bridge logical inputs
// Line following with 3 sensors, Robot follows black line, stops at 
// the end, turns 180 deg and follows the same line back again

#include <QTRSensors.h>
#define NUM_SENSORS   3
#define TIMEOUT       2500
#define EMITTER_PIN   QTR_NO_EMITTER_PIN

QTRSensorsRC qtrrc((unsigned char[]) {2, 3, 4}, NUM_SENSORS, TIMEOUT, EMITTER_PIN);
unsigned int sensors[NUM_SENSORS];

const int m1Pin1 = A1;  const int m1Pin2 = A2;
const int m2Pin1 = A3;  const int m2Pin2 = A4;
const int m1enablePin = 5;  const int m2enablePin = 6;
unsigned int a=0, b=0, c=0, i=0;
//unsigned int minSen0=440, minSen1=768, minSen2=816, maxSen0=880, maxSen1=1676, maxSen2=1804;
unsigned int minSen0=360, minSen1=652, minSen2=712, maxSen0=832, maxSen1=1704, maxSen2=1816;
unsigned int wheelSpeed = 200, turnSpeed = 100, lower = 300, upper = 800;

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT); 
  pinMode(m1Pin1, OUTPUT);  pinMode(m1Pin2, OUTPUT); 
  pinMode(m2Pin1, OUTPUT);  pinMode(m2Pin2, OUTPUT); 
  pinMode(m1enablePin, OUTPUT);  pinMode(m2enablePin, OUTPUT);
  do {
    delay(250);
    readSensors();
   } while (b<upper);

  digitalWrite(13, HIGH); delay(2000);digitalWrite(13, LOW); // LED indicates that both the sensors are on black line
  analogWrite(m1enablePin, wheelSpeed);  analogWrite(m2enablePin, wheelSpeed); 
  digitalWrite(m1Pin1, HIGH); digitalWrite(m1Pin2, LOW);
  digitalWrite(m2Pin1, HIGH); digitalWrite(m2Pin2, LOW);
}

void loop() {
  for(i=0;i<200;i++){
    check();
    delay(20);
  }
//  turn();
  for(i=0;i<200;i++){
    check();
    delay(20);
  }
//  turn();
//  analogWrite(m1enablePin, 0);  analogWrite(m2enablePin, 0); 
//  delay(1000);
//  analogWrite(m1enablePin, wheelSpeed);  analogWrite(m2enablePin, wheelSpeed); 
}

void readSensors() {
  delay(10);
  qtrrc.read(sensors);
  if(sensors[0]<minSen0)  sensors[0]=minSen0;
  if(sensors[0]>maxSen0)  sensors[0]=maxSen0;
  if(sensors[1]<minSen1)  sensors[1]=minSen1;
  if(sensors[1]>maxSen1)  sensors[1]=maxSen1;
  if(sensors[2]<minSen2)  sensors[2]=minSen2;
  if(sensors[2]>maxSen2)  sensors[2]=maxSen2;
  a = (((unsigned long)sensors[0])-minSen0) * 1000 / (maxSen0-minSen0);
  b = (((unsigned long)sensors[1])-minSen1) * 1000 / (maxSen1-minSen1);
  c = (((unsigned long)sensors[2])-minSen2) * 1000 / (maxSen2-minSen2);
  Serial.print(a);Serial.print('\t');  Serial.print(b);Serial.print('\t');  Serial.print(c);Serial.println();
}

void check() {
  readSensors();
  if(b<upper && c>a){
    do {
      analogWrite (m2enablePin, 0);
      readSensors();
    }
    while (b<upper);
    analogWrite (m2enablePin, wheelSpeed);
  }
  else if (b<upper && a>c){
    do {
      analogWrite (m1enablePin, 0);
      readSensors();
    }
    while (b<upper);
    analogWrite (m1enablePin, wheelSpeed);
  }
//
  else if (b>upper && a>upper && c>upper){
    analogWrite (m1enablePin, 0);
    analogWrite (m2enablePin, 0);
    delay(3000);
    turn();
  }
//
}

void turn(){
  analogWrite(m1enablePin, turnSpeed);  analogWrite(m2enablePin, turnSpeed); 
  digitalWrite(m1Pin1, LOW); digitalWrite(m1Pin2, HIGH);
  delay(1000); // this delay imp for robot to move out of the line
  do {
    delay(10);
    readSensors();
  }
  while (a<upper && b<upper && c<upper); // while any one of the sensors comes on the line
  digitalWrite(m1Pin1, HIGH); digitalWrite(m1Pin2, LOW); 
  analogWrite(m1enablePin, wheelSpeed);  analogWrite(m2enablePin, wheelSpeed); 
}

Last updated: March 14, 2020