Advance Line Following Robot
Advance Line Following Robot
FOLLOWING ROBOT
Applied Mechatronic Construction Project-
EEEE-1031
Vigneshwaren Sunder
STUDENT ID: 20021065
1.0 INTRODUCTION:
1.1 OVERVIEW:
1.2 OBJECTIVES:
Fig: 1.0
Fig: 1.2
The primary use of the MPU-6050 module in our project is to
measure the angle of the ramp on either side and print the
output on the LCD display. We have also used this module to
make a 360 degrees turn by measuring the yaw value. When
the robot climbs the ramp, the value is processed by the
DMP(Digital Motion Processor). The maximum value processed
is then displayed on the LCD screen as ramp angle.
Fig: 1.3
3.2 Woking of the I2C Bus:
The two wires are called Serial Cloak Line(SCL) and Serial
Data Line(SDA). The SCL will synchronize the data transfer
between the devices on the I2C bus and it is generated by the
master device. The SDA line carries the data. Data are
transferred in a sequence of 8-bits which are sent with the
most significant bits first. They can have up to 128 devices.
Now to communicate with the relevant devices, we first need
to find the unique address of that device. Each Slave device
has to have its own unique address and both master and
slave devices need to take turns communicating over the
same data line.
Now we have to create the relevant code to initiate the I2C
bus communication. We can use the Arduino Wire Library.
Firstly, we need to define the sensor address and internal
register addresses. Then, the Wire.begin() function is used to
initiate the Arduino wire library and the serial communication.
Save calibration
Start Calibration End calibration.
values
Fig: 1.5
Once the values have been noted down, the individual values
from the sensors for black and white readings need to be
mapped to a common range. The flowchart below shows how
to map the acquired readings and get the calibrated values.
An analog signal is
obtained in the
output based on
the amount of
reflected light and
is fed to the
Fig: 1.6 comparator and the
digital signal is sent
to the microcontroller. Here, the analog signal is converted
into digital by the array.
4.2 Weighted Average Algorithm for Line Following:
A weighted average algorithm can provide a measure of error.
Each sensor is calibrated to return a maximum and minimum
value. The center point of the sensor module forms the
reference point. Each sensor point is given a weighting. i.e.,
distance from the reference point which is given by,
Let's assume that when the sensor senses the black line, it
reads 0 and when it is off the black line, it reads 1. The
microcontroller corresponds to the algorithm and executes the
next movement in such a way that the center most
sensors(L1 and R1) reads 0 and the rest of the sensors read
1. In this way, we need to do the calibration process and
implement the weighted average algorithm.
5.0 The PID CONTROL SYSTEM:
A PID controller is a feedback loop system.
The basic idea is to steer the robot towards the line, and the
farther off it is, the more it corrects itself.
The PID algorithm can be described as the following equation.
Fig: 1.7
Fig: 1.8
Compares the
The PID system
desired position to
decides how much
the current
to steer and
position and the
executes the
result is stored as
output.
ERROR
Fig: 1.9
Data
Read Sensor Calibrate PID Error PID Output
Weighted
Data Data Calculation Control
Average
6.0 Conclusion:
In our project we implemented the PID control system and
used the microcontroller to build a line following robot. Our
expectation for this project is to make the car move with PID
controller and use the MPU-6050 module to display the angle
of the ramp kept in the track, climb down the track and move
for certain distance and show the angle of the other side of
the ramp. Though we achieved the ultimate goal of the
project, we faced many challenges during the making of the
bot.
One main challenge was the MPU-6050 library crashed
whenever new codes are added to it. To overcome that issue,
we edited the library code so that it gets executed in a loop.
Therefore, the code gets looped back to be reset until the
desired sensor value is obtained.
There are many flaws in the design and operation of our
robot. The movement of our robot was not smooth as there
was high degree of oscillation in the movement of the robot.
That was because the PID constants that we used were not so
accurate. Also, the initial placement of the robot has to be at
the center of the black line. If not, the initial error will not be
accurate and the robot may fail to follow the track smoothly.
The output on the LCD screen is given below.
7.0 References:
1. Jamie. PID Control. Arduino Platforms.
[Online][cited: 1yr ago] https:// car
www.mvcode.com/lessons/pid-control-jamie.
2. Arduino official. MPU-6050 Arduino
platforms.[Online]
https://playground.arduino.cc/Main/MPU-6050.
3. MTaylor. Line Follower Array Hookup Guide. [Online]
https://learn.sparkfun.com/tutorials/sparkfun-line-
follower-array-hookup-guide/introduction.
4. Peter Harison. Simpler Line follower sensors.
[Online][Cited: April 15, 2011]
http://www.micromouseonline.com/2011/04/15/sim
pler-line-follower-sensors/.
5. Dejen Nedelkovski. Arduino Tutorial. [Online]
http://howtomechatronics.com/tutorials/arduino/how
-i2c-communication-works-and-how-to-use-it-with-
arduino/.
6. Enigmerald. PID Tutorials for Line Following. [Online]
[cited: January 6, 2014]
https://www.robotshop.com/letsmakerobots/pid-
tutorials-line-following.
Appendix:
The error will be negative when the car is on the right side of
the black line. Similarly, the error will be positive when the
car in on the left side.
The Fig. shows how the speed of the wheel is changed
based on the error value obtained
The codes used in this project is given below.
CODES:
This code is uploaded to Arduino duemilanove.
#include "motordriver_4wd.h"
#include <seeed_pwm.h>
#include <Wire.h>
#include "motordriver_4wd.h"
#include <seeed_pwm.h>
#define leftMotorBaseSpeed 15
#define rightMotorBaseSpeed 15
int leftMotorSpeed;
int rightMotorSpeed;
unsigned char data[16];
unsigned int sensorData[8];
unsigned calData [8];
char input;
void setup() {
// put your setup code here, to run once:
Wire.begin();
Serial.begin (115200);
MOTOR.init();
PIDconstants();
readSensorData();
lastError = weightedAverage(); // set the first detected error
as the reference error to calculate differential error when
Hercules starts to move
previousTime = millis(); // set 0 as the initial time before
Hercules starts to move
}
void loop() {
// put your main code here, to run repeatedly:
if (Serial.available () > 0)
{
input = Serial.read (); // read message sent from Uno
switch (input)
{
case 'a': // move forward if Uno sends an 'a'
MOTOR.setSpeedDir (20, DIRF);
break;
int condition;
condition = 1;
}
else if (leftMotorSpeed < 0 && rightMotorSpeed > 0) // set
negative leftMotorSpeed to positive
{
leftMotorSpeed = constrain(leftMotorSpeed, min_speed,
0);
leftMotorSpeed = leftMotorSpeed * -1;
rightMotorSpeed = constrain(rightMotorSpeed, 0,
max_speed);
condition = 2;
}
else if (leftMotorSpeed > 0 && rightMotorSpeed < 0) // set
negative rightMotorSpeed to positive
{
rightMotorSpeed = constrain(rightMotorSpeed, min_speed,
0);
rightMotorSpeed = rightMotorSpeed * -1;
leftMotorSpeed = constrain(leftMotorSpeed, 0,
max_speed);
condition = 3;
}
switch (condition)
{
case 1: // Move forward
MOTOR.setSpeedDir1 (rightMotorSpeed, DIRF);
MOTOR.setSpeedDir2 (leftMotorSpeed, DIRF);
break;
default:
break;
}
}
lastError = error;
previousTime = currentTime;
return output;
}
This code is uploaded to the Arduino UNO.
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include <LiquidCrystal.h>
MPU6050 mpu;
#define OUTPUT_READABLE_YAWPITCHROLL
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor
measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel
sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel
sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll
container and gravity vector
int encoder = 3;
int count = 0;
float distance;
boolean displayControl = true;
boolean countControl = true;
void dmpDataReady()
{
mpuInterrupt = true;
}
void setup()
{
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this
line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION ==
I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
// verify connection
//Serial.println(F("Testing device connections..."));
//Serial.println(mpu.testConnection() ? F("MPU6050
connection successful") : F("MPU6050 connection failed"));
void loop()
{
// put your main code here, to run repeatedly:
/* while (pitch < 5)
{
Serial.println ('e');
} */
int startDistance = distance; // set the current distance as
reference distance
while (distance - startDistance < 20) // follow line with PID
until the difference between the current distance and
reference distance is more than 20cm
{
Serial.println ('e');
}
while (pitch < 20) // go straight until MPU 6050 senses the
pitch is more than 20 degrees
{
MPU (0);
forward (1);
}
forward(300); // move forward for 300 milliseconds
displayControl = false; // stop the screen from displaying
total distance and total time
lcd.clear(); // clear LCD screen
lcd.setCursor(0, 0);
lcd.print("Ramp Angle:"); // print ramp angle
MPU (2);
freeze (500); // stop for 500 milliseconds
forward (1800); // move forward for 1800 milliseconds
freeze (3000); // stop for 3000 milliseconds
countControl = false; // stop counting total distance starting
from now
turnLeft (2000); // turn left for 2000 milliseconds
freeze (500); // stop for 500 milliseconds
countControl = true; // start counting total distance
whenever it moves
lcd.clear();
displayControl = true; // start displaying total distance and
total time
startDistance = distance;
while (distance - startDistance < 30) // follow line for 30 cm
with PID
{
Serial.println ('e');
}
startDistance = distance;
displayControl = false; // stop displaying total distance and
total time
lcd.clear();
lcd.setCursor (0, 0);
lcd.print ("Count Distance:");
while (distance - startDistance < 130) // follow line for 130
cm with PID
{
lcd.setCursor (13, 1);
lcd.print (distance - startDistance);
Serial.println ('e');
}
unsigned long int refertime = millis ();
while (millis () - refertime <= 3000) // pause and displaying
timer up to 3000 milliseconds
{
lcd.setCursor (0,1);
lcd.print ("Time: ");
lcd.print (millis() - refertime);
freeze (1);
}
lcd.clear ();
displayControl = true;
while (distance < 1430) // follow line with PID until the total
distance is no more than 1430 cm
{
Serial.println ('e');
}
while (true) // stop forever
{
freeze (1);
}
}
case 2:
lcd.print (pitch);
break;
case 3:
lcd.print (roll);
break;
default:
break;
}
#endif
}
} while (repeat == true);
}
void forward (int duration) // this functions allows Hercules to
move forward for whatever duration inputed in the argument
{
unsigned long int before = millis ();
while (millis() - before < duration)
{
Serial.println ('a');
}
}