Introduction: Simple Light Following Robot



This simple project will demonstrate how to create a robot that will use two low cost photo resistors to follow a flash light. It will also demonstrate a very simple control loop.  A control loop is an important concept in the world of robotics.  Control loops are used for everything from regulated the speed of the motors to helping the robot travel in the right direction.

The following components will be used:

1.  Magician Mobile Robot Base - http://www.zagrosrobotics.com/shop/item.aspx?itemid=859
2.  Arduino UNO R3 or compatable - http://www.zagrosrobotics.com/shop/item.aspx?itemid=868
3.  Ardumoto motor driver - http://www.zagrosrobotics.com/shop/item.aspx?itemid=782
4.  Solderless Breadboard - http://www.zagrosrobotics.com/shop/item.aspx?itemid=827
5.  Jumper Wires
6.  (2) 4.7k ohm resistors - Radio Shack
7.  (2) Photoresistors - Radio Shack

All of the components for this project can be purchased in a discount bundle here :

Zagros Robotics Bundle

Video of the robot in action:


Step 1: Assembly of the Magician Chassis

Follow the installation instructions included with the Magician chassis. 

Magician Assembly Hints:

1.  Insert the batteries before attaching the upper deck.

2.  Attach the Arduino to the upper deck before attaching it to the lower deck.



Step 2: Assembly of the Ardumoto

The Ardumoto motor driver shield ships without headers or terminal installed.  Solder these into place, then simply plug into the Arduino.  Once the motor driver is plugged into the Arduino, attach the motors to the motor terminals.  If later, you discover the motors turn the wrong direction, just reverse the wires for that motor. 

Step 3: Attach the Solderless Breadboard

Attached the solderless breadboard to the upper deck.

Step 4: Build Sensor Circuit

1.  Connect +5V from the Ardumoto to one of the bus strips on the breadboard.
2.  Connect GND from the Ardumoto to the other bus strip on the breadboard.
3.  Mount (2) photoresistors on the breadboard. Using needle nose pliers can make inserting the leads easier.
4.  Tie one lead of the 4.7k ohm resistor to one lead of the photoresistor.  Tie the other resistor lead to the GND bus.
5.  Tie one lead of each photo resistor to the +5v bus.
6.  Tie the junction of the left photoresistor and the 4.7k ohm resistor to the (0) Analog In pin of the Ardumoto.
7.  Tie the junction of the right photoresistor and the 4.7k ohm resistor to the (1) Analog In pin of the Ardumoto.

Step 5: Programming the Arduino

Load the following program into the Arduino.

The program can be downloaded from here:

http://www.zagrosrobotics.com/files/Mag_Light_Follower_04062013.ino

This program uses a terminal program to communicate with the robot.  You can use the Serial Monitor within the Arduino development environment.  You can also use Hyperterminal or Realterm (either of which will be easier to use).

The program includes basic motor control functions:

'f' - Forward
'b'- Backward
'l' - Rotate Left
'r' - Rotate Right
's'- Stop

These command can be used to set the direction of your motors. 

The new command 'g' which was added to the program for this project will tell the robot to start looking for the light source.

The analog input pins 0 and 1 will read the analog value from the photosensors and convert it to an integer value between 0 and 255.

Each motor has a direction and PWM (speed) value.  The PWM value are limited between 0 and 255. 


###################################   Arduino code   ###############################

//Line following Example 4/6/2013

int pwm_a = 10;  //PWM control for motor outputs 1 and 2 is on digital pin 10
int pwm_b = 11;  //PWM control for motor outputs 3 and 4 is on digital pin 11
int dir_a = 12;  //direction control for motor outputs 1 and 2 is on digital pin 12
int dir_b = 13;  //direction control for motor outputs 3 and 4 is on digital pin 13
int incomingByte = 0; // for incoming serial data
int encoderCount=0;
int encoderOld=0;
int encoderState=0;
int sensorState=0;
int lightsensor_left=0;
int lightsensor_right=0;
int temp_var=0;
int gain=2; // control system gain var
const int encoderPin = 2;
const int sensorPin = 3;


void setup()
{
  pinMode(pwm_a, OUTPUT);  //Set control pins to be outputs
  pinMode(pwm_b, OUTPUT);
  pinMode(dir_a, OUTPUT);
  pinMode(dir_b, OUTPUT);

  analogWrite(pwm_a, 0);  //set both motors to run at (100/255 = 39)% duty cycle (slow)
  analogWrite(pwm_b, 0);



  Serial.begin(115200);  


  Serial.print("Zagros Robotics, Inc.");
  Serial.print("Magician Light Follower Demo 4/6/2013\n ");


}

void loop()
{


        // Serial.print("Zagros Robotics, Inc.");

   if (Serial.available() > 0) {
  // read the incoming byte:
  incomingByte = Serial.read();
  }

  //read light sensors

lightsensor_left=analogRead(0);
lightsensor_right=analogRead(1);
Serial.println(lightsensor_left);
Serial.println(lightsensor_right);







  switch(incomingByte)
  {
     case 's':
       digitalWrite(dir_a, LOW); 
       analogWrite(pwm_a, 0);
       digitalWrite(dir_b, LOW); 
       analogWrite(pwm_b, 0);
       Serial.println("Stop\n");
       incomingByte='*';

     break;

     case 'f':
       digitalWrite(dir_a, LOW); 
       analogWrite(pwm_a, 255);
       digitalWrite(dir_b, LOW); 
       analogWrite(pwm_b, 255);
       Serial.println("Forward\n");
       incomingByte='*';
     break;

      case 'b':
       digitalWrite(dir_a, HIGH); 
       analogWrite(pwm_a, 255);
       digitalWrite(dir_b, HIGH); 
       analogWrite(pwm_b, 255);
       Serial.println("Backward\n");
       incomingByte='*';
     break;

     case 'l':
       digitalWrite(dir_a, LOW);
       analogWrite(pwm_a, 255);
       digitalWrite(dir_b, HIGH); 
       analogWrite(pwm_b, 255);
       Serial.println("Rotate Left\n");
       incomingByte='*';
     break;


       case 'r':
       digitalWrite(dir_a, HIGH);
       analogWrite(pwm_a, 255);
       digitalWrite(dir_b, LOW); 
       analogWrite(pwm_b, 255);
       Serial.println("Rotate Left\n");
       incomingByte='*';
     break;

     case 'g': //follow the light

     gain = 3;

     temp_var = (lightsensor_left - lightsensor_right)*gain;

     if (temp_var > 0)
     {
       digitalWrite(dir_a, HIGH);
       digitalWrite(dir_b, LOW); 
       Serial.println("Rotate Left\n");
     }
     else
     {
      digitalWrite(dir_a, LOW);
       digitalWrite(dir_b, HIGH); 
       Serial.println("Rotate Right\n");
     }


     Serial.println(temp_var);

     temp_var=abs(temp_var);


      if(temp_var > 255)
     {
       temp_var = 255;
     }


         analogWrite(pwm_a, temp_var);
         analogWrite(pwm_b, temp_var);




     break;

     case 'v':
     Serial.println("Version 04042013a\n");
     incomingByte='*';
     break;

    delay(500);

  }



}  // end loop


Step 6: Tuning the Control Loop

Command 'g' tells the robot to start looking for the light. 

The objective is to get both photoresistors to read the same level of light.  The program calculates this by subtracting one light sensor value from the other.  If this value equals zero (0), the light sensors are both reading the same level of light.  If not, the program will rotate the robot to correct this error.  Once this is achieved, the robot should be facing the light source.

In this very simple control loop, the setpoint is equal to zero (where both light sensor are reading the same value).  The output is the motor speed and direction. 

It is unlikely that the program will work very well without some adjustment.   This is called "tuning the loop".

The gain value is set to "3" to start.  Adjusting the value is how the control loop is tuned.  If the robot is turning to fast or is acting "jumpy" reduce the gain.  If the robot is moving to slowly, increase the gain value.  After adjusting the gain, reload the program and see if the robot's behavior has improved.   Good luck!


case 'g': //follow the light

     gain = 3;

     temp_var = (lightsensor_left - lightsensor_right)*gain;

     if (temp_var > 0)
     {
       digitalWrite(dir_a, HIGH);
       digitalWrite(dir_b, LOW); 
       Serial.println("Rotate Left\n");
     }
     else
     {
      digitalWrite(dir_a, LOW);
       digitalWrite(dir_b, HIGH); 
       Serial.println("Rotate Right\n");
     }


     Serial.println(temp_var);

     temp_var=abs(temp_var);


      if(temp_var > 255)
     {
       temp_var = 255;
     }


         analogWrite(pwm_a, temp_var);
         analogWrite(pwm_b, temp_var);



     break;