Line Follower Robot With Arduino - Very Fast and Very Simple





Introduction: Line Follower Robot With Arduino - Very Fast and Very Simple

Hey! In this Instructable you will learn how to make a line follower robot, made to follow a race track as fast as possible. This type of robot is very popular and can be used to teach some very powerful physics, electronics and programing skills and concepts while still being very fun and simple to make.

The original objective of this robot was to take it to competitions where it would race against other robots, so my design always focused in achieving the highest speed while conforming to the rules of the competitions. Nonethless, every or almost every aspect of this robot can be modified to adjust for the needs, skills or budget of the maker.

My robot, called Yeti, is almost 12cm wide and 20cm long, which are the competitions limits in my country, and the cost was aproximately 75 US dollars, but the price might be different for you depending on what components you choose.

The race track can be either a black line on a white background or the other way around. The competitions I go to have a 2cm line width of white over black and also have small horizontal lines at the start and end of every straight section for the robot to detect and change speeds taking advantage of the straight path.

Tools needed:

  • Soldering Iron, and solder
  • Wire Snips
  • Needle Nose Pliers
  • PCB etching tools and materials - Can be replaced with a breadboard but will take up more space
  • PC (for programming the robot)

Materials needed:

  • 2 motors
  • 2 motor brackets
  • 2 wheels
  • 1 ball caster
  • Arduino UNO
  • Some screws to hold things together - 1/8 inch or less if you want them to be able to hold the Arduino
  • LiPo battery of required voltage - Can be exchanged for regular batteries
  • 1 L293 chip
  • 1 16 (8x2) IC socket
  • 1 L7805 chip
  • 2 100nF capacitors
  • 1 sliding switch (recommended)
  • 2 push-buttons
  • 8 push-up resistors (can be anything from 10k ohm to 100k ohm)
  • 6 220 ohm resistors
  • 6 CNY70 infrared sensors
  • About 50 pins
  • 13 dupont cables female to female
  • Something to make the base, I used 3d printing but it can easily be made with wood, plastic, cardboard or whichever material you have and think would work

Materials chosen in this robot:

Step 1: Key Concepts for Achieving Maximum Speed

Before designing the robot and choosing its parts, it is very important to analize certain aspects of the robot to ensure we will get the most speed out of it.

Weight: If Force = Mass*Acceleration, it is pretty clear that with the same motors, meaning the same force, we need to decrease mass to get the most acceleration, which translates to the highest speed. So as a first key concept we need to keep the weight of the robot into consideration since less weight = more speed. This means we have to choose the smallest battery possible without it being so small that it runs out too fast, the lightest wheels possible without loosing the much needed traction, and also light materials in general like making a thin plastic base instead of using heavier materials.

Torque vs Speed: When choosing our motors we will see that often times the same model has different torque and speed options you can choose from. For this robot, we want the most speed we can get while still having enough torque to take advantage of it, since if we have too little torque the robot will struggle to move its weight in spite of having high speed rated motors. I found that the motors I chose, which are linked above, did the job flawlessly.

Height, Length, Width and Center of Mass: To make the most out of the force that the motors are making, we should make sure that all parts of the robots are as close to the source of the force as possible. So the center of mass should be right between the motors. This means that the heavier components like the battery and the electronics should be as close as possible to the motors. Furthermore, the height should be the least possible, just like the length and width, but we have to take into consideration that since the pivot point of the robot are the motors, then the furthest ahead the line sensors are, the sooner the robot detects when it has to turn and so the pivot point, which is far back, can start turning before it actually goes out of the line giving the robot a quick response time. Also, since the sensors will be at the front of the robot, far from the motors and wheels, and the angle at which the robot turns gets bigger the closer together the wheels are, then for this long robot I chose to have the wheels at a considerable distance from each other making slower turns to have more control, sensitivity, and reduce robot oscilations.

Step 2: Electronics: Arduino Shield

Arduino is a very powerful and versatile tool, but for our robot we will need some functionality that the Arduino doesn't have. For this aditional electronics I decided to make a small PCB shield that plugs on top of the Arduino. If you don't know how to make PCBs you can check another instructable on making one or have one made for you online and shipped to your house. If you prefer, you can even make the circuit on a breadboard.

The most essential part here is the H-Bridge IC, which is the L293. Although it may not be the most efficient motor driver, it is widely available, relatively cheap, and its 600mA per motor maximum current is sufficient for my chosen motors which in my tests consumed around 300mA each, since they made very little effort due to the light robot and low friction.

This IC is connected to the battery, the motors, and the Arduino. The Arduino sends information to the IC in the form of PWM so that the L293 transforms that information to the corresponding voltage which is fed to the motors, in a way that by varying the duty cycle of the PWM we can vary the speed of the motors. For more information about this topic you can research "PWM". It is very important that the pins of the Arduino that transmit this information are PWM-capable, in the case of the Arduino UNO these pins are numbers 3, 5, 6, 9, 10 and 11. You can simply connect both ENABLEs of the L293 to 5v and then the 4 inputs to any Arduino digital pins as long as at least 1 pin per motor is PWM-capable.

Apart from the L293, I added to the shield a switch connected to the battery to turn on the robot just for convinience, 2 push-buttons to calibrate the robot or to start the race which have proven to be useful, an L7805 to regulate the battery's voltage to the 5v that Arduino needs and 8 pins to connect the sensors that have 5v, GND, and the 6 analog inputs of the Arduino.

If you want to make this PCB, I have included the files of the Proteus 8.1 Project (PCB design software), a PDF to print for etching the PCB, the gerber files if you want to have it made for you, and the schematics if you want to design this yourself or if you use another design software.

Step 3: Electronics: Line Sensors

The track consists of a colored line on a different colored background, usually white and black. These colors have different brightness and so we can use infrared sensors that emit and recive reflected IR light. If the sensor, pointing to the ground, is over a bright surface like white, it will recieve a lot of reflected light, but if it is on a dark surface it will recieve little reflected light. The sensors I chose to use are the widely popular CNY70. I designed a PCB that has 5 of this sensors in the form of an arc to have a wider angle of sensing. I used 5 because an odd number of sensors allows me to have the middle sensor right over the line, so that I'm sure to be on top of the line. Every one of the 5 sensors are detecting the brightness under them and depending on these readings, the Arduino can know where the line is in relation to the middle of the robot, where it should be. Since the Arduino UNO has 6 analog inputs, I also added a 6th sensor at the side, in order to detect the straight path indicator at the side of the tracks, so that since it is easier to follow the line in a straight path I can increase the speed in these parts. I have included the same files as the shield but for this board, sadly I lost the design for the single sensor on the side but it is not necesary at all to follow the line but rather an aditional feature. In this case I decided to have the PCB made for me, as a test, and it came out great!

Step 4: Making the Structure

The method I chose for making the base of the robot was 3D printing. It allows me to make several iterations of the design and test what works best. Also, the plastic is very light but strong enough for this application. If you want you can make this part out of any material you want since it is a very simple part and easy to make or design, just take care of all the dimensions and distances, so that everything fits. I designed 2 parts, one for the main body of the robot and another for holding the sensor. This allowed me to print 2 sensor holders of different lengths and change them if a track at a competition is particulary difficult and I need to put a shorter sensor holder to have more maneuverability. You can find my designs at this Thingiverse post and I encourage you to design your own or modify mine to your liking: 3D printed base

Step 5: Assembly: Motors, Wheels and Battery

At first you need to solder some cables to the motors. I found that cutting a dupont cable in half is perfect for soldering each half to a pin of the motor.

The motors are attached to the base with 1/8 inch screws and with the 3d printed bracket linked at the beginning, but you can also buy a motor bracket if you adjust the holes in the design of the base.

For the wheels, I found that I could get a lot of traction by making my own covers out of silicone. What I did was use the wide Pololu wheels, took the black cover out, 3D printed a little cup 45mm in inner diameter, put the white plastic part of the wheel in the cup and filled the exterior with molding silicone. In this way I had the plastic part sorrounded with silicone, and when it finished hardening I had my high traction wheels. If you want to make this you have to make a cup about 40 or 45 mm in inner diameter and hold the plastic part of the wheels in the middle. Make sure it is in the EXACT middle! Then you should pour the silicone. This is silicon used for making the molds but here I used it for the molded part itself. Beware of having the plastic part loose because it may float and you want it to be perfectly parallel to the plane of the wheel.

The wheels go pressed gently into the motor shaft, taking care to align the D shaped hole with the shaft, and the pressure will keep them secure in place.

In my design I took into consideration the dimensions of my battery and so it fits tightly between the motors, make any modifications needed to the base if your battery needs more space or if it is smaller and you want a more compact robot. The battery is simply held by the components around it and the cable connection. Since this robot doesn't shake, vibrate, or bump into stuff, the battery won't jump around and there won't be too much tension on the battery cable.

Step 6: Assembly: Arduino and Shield

The Arduino should be screwed to the base and then the Shield needs to be inserted on top of it. I found that 2 screws could very easily hold the Arduino but you can add up to four if you modify the base. You should then connect to the shield the 2 motors and the battery, the sensors will be connected later. The USB port in the Arduino should be facing to the back of the robot since we will be connecting to it when we program the robot.

Step 7: Assembly: Sensor Board, Ball Caster and Sensor Holder

Now that we have the base completed, we need to attach the sensor holder which holds the sensor board and a ball caster that provides a low friction support to the robot.

The sensor holder is screwed to the base in two points and then the sensors and the ball caster are screwed to the sensor holder with 2mm screws. If you want you can now attach a 6th sensor to the holder and connect it to the main sensor board. Now we have to connect the sensor board to the shield with 8 dupont cables. In the design of the sensor holder I made a hole through which the cables can pass but that is just for convenience.

We have now finished assembling the robot. If you already have all the parts ready, the whole assembly takes between 15 and 20 minutes, and maybe a little bit more if you are unexperienced with tightening screws and connecing electronics.

All that needs to be done now is programming the robot with the Arduino IDE. I will asume you know how to program an Arduino in general, but if you don't, you can check an instructable on doing so.

Step 8: Programming: Line Sensors Calibration

This will be the first thing the robot has to do before it can start the race, and it is calibrating the line sensors. Not all tracks are the same, and sometimes there is little light in the room while other times all windows are open and sun is shining through lighting up the track with nasty infrared light. In order to make the robot be able to function in all enviroments, it first calibrates its sensors. To do this, you simply press the first button, and the robot will rotate around the line trying to have all its sensors go through every possible state, so basically, it wants to teach the sensors what is white and what is black in this particular track so its important that during the less than 3 seconds of calibration, where the Arduino takes thousands of samples of the sensors, that all sensors go over white and over black, and also that they don't see the floor, get lifted so that they see nothing, or anything weird, they should just see the track. If you mess up the calibration which sometimes happen if, for example, you realize that the last sensor didn't actually go over the line, then you should turn off and on the robot and do the calibration again.

For the time that it is calibrating, this is the code that sets the maximum and minimum values for each sensor:

int CNYread[CANT_SENS_MAX];			//Maximum sensor quantity is 6
for(int x = 0; x <= CANT_SENS_MAX-1; x++){
	CNYread[x] = analogRead(CNY[x]);
	CNYmin[x] = (CNYread[x] < CNYmin[x]) ? CNYread[x] : CNYmin[x];
	CNYmax[x] = (CNYread[x] > CNYmax[x]) ? CNYread[x] : CNYmax[x];

Step 9: Programming: Reading the Sensors - Weighted Average

When we press the second button, once the robot is calibrated, the first thing our program will do for following the line is detecting its position in order to make changes in the speed of the motors. Since the line might be between 2 sensors, or maybe the line is so wide that the sensors next to the one over the line reflect some light and give an intermediate value, then we need an algorithm that makes an average out of the readings in order to determine the lines position. The way the algorithm works is by asigning a numerical "weight" to each sensor, which are 0, 1000, 2000, 3000, 4000. The analog value of each sensor is scaled from the measured range during the calibration to a new range between 0 and 1000 for convenience and uniformity. Depending if the line is white or black we might have to flip this value. Then every value is multiplied by their weight, summed together, and divided by the sum of the readings. It will look something like this:

Line_position = (Value[0]*weight[0] + Value[1]*weight[1] + ... ) / 
		(Value[0]+Value[1]+Value[2]+ ... );

If you wish to further investigate about the weighted average you can find really good resources online.

Step 10: Programming: the PD Algorithm for Following the Line

This is the most important part of the code, it is the algorithm that takes the position of the line as an input and outputs 2 values for the speed of the motors.

It takes an error value going from -2000 to 2000 (it is just the line position from 0 to 4000, -2000), and calculates a correction value based on 2 things: The distance to the line which is the proportional part, and the rate of change of the line position which is the derivative part. From there the name PD or Proportional-Derivative. The program I made is also prepared for using a PID algorithm which includes the integral of the line position, but I didn't found it to be useful. It is best to stick to PD.

The proportional part is the easier to understand, if I am far from the line I make a big turn, if I am close to the line I make a small turn and if I am on top of the line I make no turn at all. By tweaking a KP variable we can set the relationship between the error and correction values to make the smoothest possible following of the line and avoid overshooting for the line. This looks like this:

 proportional_correction = KP * line_position

The derivative part is a little more complicated, but still simple enough. The correction here corresponds to the substraction between the actual position and the last position. If the substraction is positive then the value is increasing, which means I should make a correction the other way to stop it from increasing. If the substraction is negative then the value is decreasing, which means I should make a correction the other way to stop it from decreasing. If the substraction is null then I make no correction at all. In conclusion, the derivative correction tries to avoid change and keep the line in whichever position it is, even if it is not in the right position. If we multiply this correction with a KD variable, we can tweak the variable and eliminate any oscilation that the proportional might cause. This will look like this:

derivative_correction = KD * (line_position - last_line_position)
last_line_position = line_position	//update the last value

Each of the two corrections is essential, the proportional for making sure the line is right in the middle, and the derivative for smoothing the change and avoid an oscilating robot.

The final correction is the sum of both, and it corresponds to the difference between the speeds of both motors, which causes the robot to turn.

correction = proportional_correction + derivative_correction

if(correction > 0){
	motorSpeed(maxVel, maxVel-correction);	//motorSpeed sets both motors speed
	motorSpeed(maxVel+correction, maxVel);	//maxVel is the maximum speed

This marks the end of the program explanation, the full program is attached to this instructables if you want to use it.

This version uses the 6th sensor but if you comment the updatePID function in the start part it will be stuck on straight (called RECTA) and will always use the same KP, KD, and maxVel values.

When I made the program I wrote several variable names in spanish since it is my native language, so you are very welcome to ask anything you want about the program. Also, suggestions and improvements are greatly appreciated.

EEVBlog made an awesome video explaining PID controllers and it may be helpful to you:

Step 11: Final Results and National Championship !

In my opinion this robot is an excelent and very fun project to make. In the last year I took my robot, Yeti, to my county's national championship and it won 1st place! I hope you have as much fun with it as I did and you can learn something from this Instructable. I would love to answer your questions and see the line followers that you have managed to create.

These are two videos from the final match on the championship:

3 People Made This Project!


  • Paper Contest 2018

    Paper Contest 2018
  • Pocket-Sized Contest

    Pocket-Sized Contest
  • Science of Cooking

    Science of Cooking

We have a be nice policy.
Please be positive and constructive.


Weights should be 1000-2000-3000-4000-5000. If you used 0 as a weigh, that sensor value would be lost, or zeroed out here: Value[0]*weight[0]

8 Questions


hello I'm using the program that you left in the post and when it reaches the curve the robot does not double simply continue straight, I wanted to know why it happens I have to calibrate in the program so that it does not happen, thank you very much

Hello! I got to apologize for asking a second question already. I am pretty familiar with coding, but I do not understand a part of the code you have written. An explanation would be highly appreciated. What do the following 3 if statements in the get_line_pos function do and where do those formulas and numerical values come from?(For example why is P_LINE_MIN 0.5).



The function that calculates the line position, first scales the raw values from the sensors to the actual range that they can measure.

Before the race, you can calibrate the sensors so that the white is read as 1000 and the black is read as 0 or viceversa, because is reality the sensor reads for example 956 for white and 68 for black, or some other similar values, because the sensor is not perfect. So first I map the values to a scale from 0 to R_SENS (1000 in my code).

Then where it says "reverse scale", if I want to follow a black line instead of a white line then the measurements have to be flipped, so that a high value means you are on the line.

After that, I use a strategy for following the line when the robot can't see anything because it has gone too far away from the line and none of the sensors see some part of the line. The strategy is to keep turning in the direction it was going to. So if the last reading of the line was 1950 and now no sensor reads part of the line, I "pretend" that the line is the maximum 2000 so that the robot spins and gets back to the line.

In order to detect if none of the sensors can read the line, I ask for each sensor if the reading is at least halfway between black and white. That is P_LINE_MIN. If you define P_LINE_MIN to be higher then the sensor reading will have to be higher in order to consider that particular sensor to be on top of the line. This is a value that you can tweek in order to properly detect if the robot has gone outside of the line. However I found that 0.5, exactly halfway between black and white is a good value and works pretty much everytime.

Please ask me if you have any other question, I am sorry if it takes time to answer

i downloaded the code but it is in two parts which part should i upload and can i use motors with only 2 wires . I am using 12v 1000rpm geared motor . i will be using black line on white track so what do i change in the code

Hi, the code is divided in two files for better structure and when you open the file with the same name as the folder, the Arduino IDE should automatically open both files in different tabs. So you can upload the code even if it is in two separate files.

The motor driver I used works with brushed DC motors, which have 2 wired each.

In the Yeti_v4.ino file there is the folowing line of code:

#define COLOR BLACK //Line color

You can leave it like this for a black line on white background, or change it to:

#define COLOR WHITE //Line color

for white line on black background


In your I cud'nt understand which pin MOT1,MOT2,MOT3,MOT4 works in which direction like in my code I am using FL,FR,BL,BR i.e (Left side motor rotates in Front direction , Right side motor rotates in Front direction , ... respectively) so according to my names in your program which (MOT) goes with (FL,FR,BL or BR). and will it follow a single strip of BLACK line or WHITE line ? can you please help me out...

2018-03-10_LI.jpg2018-03-10 (1)_LI.jpg

ooh ohk I got it... but after pressing calibration button it starts following white line I guess


Please just go through this program once and help me selecting the motors in the program in right place..and my bot has to follow a BLACK strip of 3cm width and it contains 8 IR SENSORS and can be calibrated using screws... please help me.

My motors spin very slow ...but I used the same ones in the instructable will it be a problem with the code

i tried that connecting them directly to the supply the robot can take to load perfectly


hey .... can i get all the connections ouline of aurduino pins to motor driver and to sensors!?

GREAT PROJECT, thank you very much for sharing!!! One note so far:

"The way the algorithm works is by asigning a numerical "weight" to each sensor, which are 0, 1000, 2000, 3000, 4000. ... Line_position = (Value[0]*weight[0] + Value[1]*weight[1] + ... ) / (Value[0]+Value[1]+Value[2]+ ... );

I guess a sensor reading is lost, if you weight it with ZERO! I guess weights should be 1000, 2000, 3000, 4000 and 5000, or am I missing something?

Hi! I was also a little confused with that at first, but it is perfectly possible to have a weight be zero because the value of the sensor is summed up with the others and appears in the denominator of the average:

(Value[0]*weight[0] + Value[1]*weight[1] + ... ) /

(Value[0]+Value[1]+Value[2]+ ... )

So the Value[0] matters just as much as the others.

For example:

If you have 4 objects that cost $0 and 1 object that costs $10, then the average cost of the 5 objets would be (4 * $0 + 1 * $10) / (4+1) = $2

The number 4 is multiplied by 0 in the denominator but it appears in the denominator of the fraction.

You can think of the sensor values like they are "pulling" the result towards their weight, so the Value[0] tries to pull towards 0 and the bigger that value is then the closer to 0 the result will be.

Hope that this clears up why the weights start with 0.

Awesome, thanks!

my motors spin too slow ......... when placed on the ground the bot barely moves its not a problem with the motors as they move smooth when given some other code

do you know what the problem might be??
if yes please help

Can you please make a video Of circuit connection
Or please send me the step by step procedure of circuit connection.
I am not able to understand the above diagram
The Arduino is getting shorted

What is the maximum speed of the robot while tracking without getting off the track?

Hi, it heavily depends on the particular track. Also the grip of the wheels, the motors you chose and plenty other things matter aswell. I would say that for my case the robot can go faster than 1 m/s but definitely slower than 2 m/s

hello sir,

i need the entire program code for these project for arduino

please send the code sir

thank you