In this article, I’m going to go over how to build a PID line following robot from scratch. We will start with just a list of parts and I’ll walk through the entire process up to a fully functional line following beast.
Jump To
What parts do we need?
First off we have to determine the parts we need for a minimum viable product. That is, what is the least amount of parts we need to build a line following robot.
NOTES:
- I did not include wire, solder, etc in the cost, usually people have those on hand.
- If you have a similar part you can use that, if you’re unsure then ask me in the comments
- Some items are only available in packs so I put the price for the whole pack in there so the actual total cost is less
*Alternatives are parts that can be replaced and still work with this guide, note that the cost is for “My Choice”
Part | Purpose | My choice | *Alternatives | Cost Per | Qty | Total Cost |
---|---|---|---|---|---|---|
Microcontroller | Controlling the robot and making decisions | Teensy 3.2 | Teensy 3.5, Arduino Uno, Arduino Nano, Arduino Mega | $20 | 1 | $20 |
Motors | Moving the robot (actuators) | 25:1 Metal Gearmotor 20Dx41L mm 12V CB with Extended Motor Shaft | Any other gear ratio of Metal GearMotor 20D | $23 | 2 | $46 |
Motor Brackets | Mounting Motors to chassis | 20D mm Metal Gearmotor Bracket Pair | $7 | 1 | $7 | |
Wheels | Moving the robot | Wheel 60x8mm Pair ā Blue | Any other size or color wheel of this type | $8 | 2 | $16 |
Wheel Hubs | Mounting wheels to the motors | Universal Aluminum Mounting Hub for 4mm Shaft, M3 Holes | $7 | 1 | $7 | |
Motor Driver | Controlling power to the motors (canāt be controlled directly from a microcontroller) | TB67H420FTG Dual/Single Motor Driver Carrier | Any motor driver that can support two motors bidirectionally with at least 1.5A continuous and 3A peak current per motor | $10 | 1 | $10 |
IR Sensor Array | For reading the line | QTR-8RC Reflectance Sensor Array | QTRX-MD-08RC Reflectance Sensor Array: 8-Channel, 8mm Pitch, RC Output, Low Current | $10 | 1 | $10 |
Caster Ball | Allows the front of the robot to glide in any direction | Ball Caster with 1/2ā³ Metal Ball | Basically any caster wheel close to this size. The plastic ones tend to squeak. | $2 | 1 | $2 |
Chassis | Something to mount our parts to and act as a robot | Cardboard box the parts were shipped in | You can use anything, from 3D printed chassis to cardboard or plastic, donāt overthink it. | FREE! | 1 | $0 |
5V Regulator | For supplying 5V logic voltage | LM7805 Voltage Regulator (only come in packs) | Any 5V regulator with a minimum of 1A output | $8 | 1 | $8 |
12V Regulator | For supplying 12V Motor voltage | 12V Step-Up Voltage Regulator U3V70F12 | $13 | 1 | $13 | |
Battery | For powering our robot | 1000mAh 2S Lithium Polymer | Any 2S LiPo with at least 750mAh capacity | $12 | 1 | $12 |
Solderless Breadboard | For creating our circuit | Half Size (Only come in packs) | Any breadboard that will fit the microcontroller, regulators, and motor driver. | $7 | 1 | $7 |
Total | $158 |
Why I chose these parts
Microcontroller
I prefer to use the Teensy microcontrollers because they are just a bit more robust than Arduino in my experience. Many times I’ve had a bootloader go bad on an Arduino and it’s annoying to need to reburn the bootloader. The Teensy microcontrollers are also much faster, starting at 72-96Mhz with the 3.2 and they only get faster from there. I chose the 3.2 vs the 3.5 simply because I don’t need all the extra pins and SD card reader.
If you’d prefer to use an Arduino, any of those will work but you’re going to come very close to the pin limit which doesn’t leave you any room to add other components if you want to. The Teensy is much smaller, has way more pins, is much faster, has more memory and is just overall, more robust so I highly recommend using it.
Using the Teensy only requires one extra step, you just need to install the Teensyduino software so you can upload your program. You can still write your code in the Arduino IDE and use Arduino libraries, you just get a much better microcontroller.
Motors
I chose the 20D motors because, while I like the micrometal geared motors, they seem to burn out faster, especially on heavier robots since they don’t have a whole lot of torque. With the 20D motors, you have a lot more current, but you have a lot more torque and speed as well so while they are big and bulky, they will last a long time.
I also chose to get the motors with theĀ dual shaft, I like this because if I want to expand my robot, I can add encoders later. If you don’t buy the dual shaft, you can’t add the integrated encoders so I always buy the dual shaft, it doesn’t cost any more and if I need them, they are there, if I don’t they don’t take up much space and if they really did cause a problem I could cut them off if I needed to. Why limit yourself to a single shaft if you don’t need to?
Motor Brackets
My choice of motor bracket was simply based on the fact that I am using the 20D motors. If you use micrometal gearmotors for your project, make sure you get the motor mounts for them instead. You could also 3D print or mill your own if you have access to those tools.
Wheels
I went with some basic wheels, I didn’t really put much thought into it, these wheels seemed not too big and not too small. The bigger you go, the faster speeds you get but with less torque. Inversely, the smaller you go the more torque and less speed you will get.
Wheel Hubs
I need a way to mount my wheels on my motors and the wheels I ordered don’t have a direct slip on connection. Therefore I need an adapter known as a wheel hub. There are wheels that are capable of sliding onto my motors directly but when I purchased these wheels they did not exist so I needed the adapter. If you can find wheels that mount directly to your spindle (typically a press fit) then you won’t need a hub.
Motor Driver
We need a motor driver, most motors cannot be directly controlled by a microcontroller pin. There are very few motors that can be directly controlled and in almost every case you will need a motor driver. What the motor driver does is provides you with electronic switches called transistors. These electronic switches allow you to power the motors from a different source of power like a battery and you can control the speed and direction using your microcontroller pins. Typically only one motor driver is needed as many of them have dual motor support. If you have a motor driver with only single motor support you will need one for each motor.
IR Sensor Array
We need a way to see the line and make decisions, an IR sensor array allows us to do that. You don’t need to use the same IR sensor array, as few as three sensors will work but these are nicely bundled into a single package so it’s easy to use for very little cost. I’ve made my own array from four single IR sensors that I wired up so it’s definitely possible to save some costs here if you already have some single IR sensors.
Caster Ball
We have two wheels in the back to support the robot but nothing in the front. The caster ball provides low friction support for the front of the robot. It’s not entirely necessary, you can use just a piece of plastic but a rolling ball really works the best for the least amount of drag.
Chassis
Really you can use almost anything for a chassis and to prove that, I am simply using the box that my components came in.
5V Regulator
We have a 2S 7.4V battery but our microcontroller so we need a way to step that down to 5V which is what the microcontroller will accept. Typically a motor driver will need a 5V power source but the one I’ve chosen has its own built in regulator so it steps the motor voltage down on its own.
12V Regulator
Again, we have a 2S 7.4V battery but our motors use 12V, we could use a 3S battery to get close to 12 volts but either way, your motor voltage should always be regulated so that the motor output is consistent. If you don’t regulate your motor voltage then the motors will have different results with a fully charged battery vs a near depleted battery. Honestly it isn’t necessary if you don’t need the consistency from your motors but it’s good practice so I would recommend it. This specific regulator was picked because it will support my current needs for my motors
Battery
The battery I chose is a 2S 7.4V Lithium Polymer battery. I prefer to use 2S batteries because they are smaller than 3S and it actually happened that a step up regulator that supported my current needs (6A+) was less than half the price of a 12V step down regulator so it worked out great.
Preparing our components
First, you’ll have a box full of components with no pins or wires anywhere, everything needs to be attached. So, I’ve created videos for soldering up these components.
Soldering the 12V step up regulator
Soldering the motor driver carrier
Soldering motor wires
Replacing the connector end on a battery
Build the motor circuit
Now that we have all of our components ready to go, we can start building. We will start with just the motor circuit, I know you want to go crazy and build the whole thing all at once but I recommend breaking the task up. Break up your build into smaller pieces like:
- Build a motor circuit
- Mounting the motors on a chassis
- Add line sensors to your circuit and possibly also adding the line sensor to the chassis at the same time since it’s a small task.
I created a separate article on how to build this motor circuit and it can be found hereĀ How to build a motor circuit with TB67H420FTG
Programming the motors
Next we want to get our motors moving and make sure we know they’re going in the right directions before mounting them to our chassis.
I created a separate article on how to build this motor circuit and it can be found hereĀ How to program a motor circuit with the TB67H420FTG
Mounting motors to the chassis
Now let’s get this baby moving, literally. Here’s how to mount the motors to a chassis and how to mount the wheels.
Hooking up and programming IR sensor array
The IR sensors will provide us with sensory data so we can make informed decisions on how to adjust our motors, let’s not worry about manipulating the motors just yet though. Again, we wan’t to break things up so we have small bits to work on and we can integrate it later. It would be confusing to try and figure out what’s wrong with my code if I’m also trying to mess with motors and I don’t know if it’s my array code or motor code that’s failing.
I created a separate article for this step which can be found hereĀ How to Hookup and Program a QTR-8 Sensor Array
Start programming our PID algorithm
Now we’re getting to the good stuff, let’s start building our PID algorithm.
The final code
#include <QTRSensors.h> #include "TB67H420FTG.h" // Line Sensor Properties #define NUM_SENSORS 8 // number of sensors used #define NUM_SAMPLES_PER_SENSOR 4 // average 4 analog samples per sensor reading #define EMITTER_PIN QTR_NO_EMITTER_PIN // emitter is controlled by digital pin 2 QTRSensorsAnalog qtra((unsigned char[]) {A9, A8, A7, A6, A5, A4, A3, A2}, NUM_SENSORS, NUM_SAMPLES_PER_SENSOR, EMITTER_PIN); unsigned int sensorValues[NUM_SENSORS]; // Motor Driver Properties TB67H420FTG driver(6, 5, 9, 8, 7, 10); // PID Properties const double KP = 0.02; const double KD = 0.0; double lastError = 0; const int GOAL = 3500; const unsigned char MAX_SPEED = 50; void setup() { driver.init(); // Initialize line sensor array calibrateLineSensor(); } void loop() { // Get line position unsigned int position = qtra.readLine(sensorValues); // Compute error from line int error = GOAL - position; // Compute motor adjustment int adjustment = KP*error + KD*(error - lastError); // Store error for next increment lastError = error; // Adjust motors driver.setMotorAPower(constrain(MAX_SPEED - adjustment, 0, MAX_SPEED)); driver.setMotorBPower(constrain(MAX_SPEED + adjustment, 0, MAX_SPEED)); } void calibrateLineSensor() { delay(500); pinMode(13, OUTPUT); digitalWrite(13, HIGH); // turn on Arduino's LED to indicate we are in calibration mode for (int i = 0; i < 3000; i++) // make the calibration take about 10 seconds { qtra.calibrate(); // reads all sensors 10 times at 2.5 ms per six sensors (i.e. ~25 ms per call) } digitalWrite(13, LOW); // turn off Arduino's LED to indicate we are through with calibration }
As you can see, there isn’t a whole lot of actual code, there’s more code to set up our robot than there is to actually perform a PID algorithm. I hope you enjoyed this series and you were able to learn at least one thing from it.
Library Links:
QTR Sensors – https://github.com/gberl001/qtr-sensors-arduino – This is a copy of https://github.com/pololu/qtr-sensors-arduino, the code would need to be updated if you want to use pololu’s version of the library.
Motor Driver – https://github.com/mcc-robotics/Dynamic_Motor_Driver – This is a custom library that I wrote, allowing you to use the same code for multiple motor drivers.
Tune our PID algorithm
I already wrote an article on tuning a PID algorithm so I’ll just redirect you there, after you’ve tuned your PID you have successfully built a PID line following robot from scratch. I hope this article was helpful and if you have any troubles along the way, post a comment in this article or on youtube. I’ll do my best to help you out.
Hello sir,
My qtr 8rc sensors are giving reading in 0 and 1 when i am using teensy 3.2. Can you help me what is the exact problem?
Yeah I can try to help you, can you use the “Contact Me” link on the right sidebar to send me a message? Once you send me a message I’ll have your email so you can send me your code and I can take a look.
You might be using
digitalRead(pin);
instead of that use
analogRead(pin);
and make sure you have used an analog input pin.
Hi, thank you for this tutorial i just wamted to ask how can you add some more conditions for example if i am working in map that has intersections between the lines
Sorry about the late response, I seem to have stopped getting notifications of comments on my site for some reason.
I just uploaded a video on how to count lines while line following, you can find that here https://youtu.be/R1UMd1FXDwU, is that what you are asking about or are you trying to keep following the line and ignore intersections so they don’t throw your line follower off track?
Fantastic write up and easy to follow, thank you!
hello
i hope that you are doing well
i have project in my university to make omni wheel robot ( 4 wheels /motors EMG30 are used )
i already use EMG30 and MD25 and QTR analog 8 , I have no idea about programming ,
I need code for the encoders and QTR
Thank you so much to help me
The same code for the QTR sensors that is used in this article can be used for your project. Actually all of the code in this article can be used but you would need to make a change for the motor driver.
I haven’t used the MD25 motor controller board but there are libraries on github for this controller. I would try this one first since it contains instructions to show you exactly how to add it. https://github.com/joshvillbrandt/MD25
What you would need to do is remove the line of code where I create the motor driver object and create your MD25 object instead, then anywhere where the code says driver.setSpeed() or something similar (starting with “driver”) you would change it to do the matching thing with your controller.
I can try to help you through this by email, if you contact me through the link http://robotresearchlab.com/contact-me/ then I can start an email conversation.
can u kindly give links to all the libraries you used? coz the updated ones are giving errors.
Sorry for the late reply, I’ve updated the post to include links to the libraries. Thank you for the suggestion.
Here’s a quick link http://robotresearchlab.com/2019/03/13/build-a-custom-pid-line-following-robot-from-scratch/#Library_Links
why didnt u use any sampling time ?
That’s an excellent question and I should have covered this in the write up but just hadn’t thought of it. Because I’m performing the same task in every iteration of the loop, there is no time difference between the loop iterations, therefore the delta time is theoretically constant. If I were to start adding in conditional statements that would alter each loop iteration then I would definitely consider a delta/sample time in the calculation.