Introduction: Advanced Line Following Robot

This is an advanced line following robot based on Teensy 3.6 and QTRX line sensor that I have built and have been working on for quite some time. There are some major improvements in the design and performance from my earlier line following robot. The speed and response of the robot has improved. The overall structure is compact and lightweight. The components are arranged close to the wheel axis so as to minimize angular momentum. High power micro metal gear motors provide the adequate torque and aluminium hub silicone wheels offer much-needed traction at high speeds. Prop shield and wheel encoders enable the robot to determine its position and orientation. With Teensyview mounted on board, all the relevant information can be visualized and important program parameters can be updated using pushbuttons.

To begin building this robot, you will need the following supplies (and a lot of time and patience at your disposal).


Step 1: Systems Overview

As with my earlier design of a self-balancing robot, this robot is an assemblage of breakout boards mounted on a perfboard which also serves the purpose of a structure.

The major systems of the robot are outlined below.

Microcontroller : Teensy 3.6 development board featuring 32-bit 180MHz ARM Cortex-M4 processor.

Line sensor : Pololu's QTRX-MD-16A 16-channel analog output line sensor array in medium-density arrangement (8mm sensor pitch).

Drive : 6V, 1580rpm, high power micro metal gear motors with magnetic wheel encoder and silicone wheels fitted on aluminium hubs.

Odometry : Magnetic wheel encoder pairs for estimating the coordinates and distance covered.

Orientation sensor : Prop shield with motion sensors for estimating position and heading of the robot.

Power supply : 3.7V, 750mAh lipo battery as power source. 3.3V step-up/down regulator powers microcontroller, sensors, and display device. Adjustable step-up regulator powers the two motors.

User interface : Teensyview for displaying information. Three-pushbutton breakout for accepting user inputs. Two numbers of 10mm diameter green LEDs for status indication while running.

Step 2: Let's Start Prototyping

We will be implementing the above circuit on the perfboard. We have to first keep our breakout boards ready by soldering headers on them. The video will provide an idea regarding which headers should be soldered on which breakout boards.

After soldering headers on breakout boards, stack the Teensyview and pushbutton breakout on top of Teensy.

Step 3: Prototyping - Perfboard

Get the 15x20cm double side prototype perfboard and mark the boundary with a permanent marker as shown in the picture. Drill M2 size holes for mounting the sensor array, caster wheel, and micro metal gear motors on locations marked with a white circle. We will later cut the perfboard along the boundary after soldering and testing all components.

We will begin our prototyping by soldering the header pins and sockets on the perfboard. The breakout boards will be later inserted on these headers. Give careful attention to the position of the headers on the perfboard. We will be connecting all wires based on this layout of headers.

Step 4: Prototyping - Prop Shield

We will first solder the connections to the prop shield. Since we are using only the motion sensors of the prop shield, we need to connect only SCL, SDA and IRQ pins apart from the 3V and ground pins of prop shield.

Once the connection is complete, insert Teensy and prop shield and calibrate the motion sensors by following the steps mentioned here.

Step 5: Prototyping - Power and Ground

Solder all the power and ground connections referring to the picture. Insert all breakout boards in place and ensure continuity using a multimeter. Verify the different voltage levels on board.

  • Li-po output voltage (usually between 3V and 4.2V)
  • Step-up/down regulator output voltage (3.3V)
  • Adjustable step-up regulator output voltage (set to 6V)

Step 6: Prototyping - Motor Driver Carrier

The DRV8833 dual motor driver carrier board can deliver 1.2A continuous and 2A peak currents per channel. We will connect the two channels in parallel to drive one motor. Solder the connections by following the steps below.

  • Parallel the two inputs and the two outputs of the motor driver carrier as shown in picture.
  • Connect input control wires to the motor driver.
  • Connect a 1000uF electrolytic capacitor and a 0.1uF ceramic capacitor across the Vin and Gnd terminals of the two carrier boards.
  • Connect a 0.1uF ceramic capacitor across motor driver output terminals.

Step 7: Prototyping - Line Sensor Array Header

Teensy 3.6 has two ADCs - ADC0 and ADC1 that are multiplexed to 25 accessible pins. We can access any two pins from the two ADCs at the same time. We will connect eight line sensors each to ADC0 and ADC1. The even number sensors will be connected to ADC1 and odd number sensors to ADC0. Solder the connections by following the steps below. We will later connect the line sensor using FFC to DIP adapter and cable.

  • Connect all even sensor pins (16,14,12,10,8,6,4,2) as shown in picture. Route the wire for connecting sensor pin 12 through the reverse side of perfboard.
  • Connect emitter control pin (EVEN) to Teensy pin 30.
  • Connect all odd sensor pins (15,13,11,9,7,5,3,1) as shown in picture.
  • Connect a 470uF electrolytic capacitor across Vcc and Gnd.

If you closely observe the line sensor pins and their corresponding header pins on the perboard, you will notice that the top row of the line sensor maps to the bottom row of the header on the perboard and vice versa. This is because when we connect the line sensor to the perfboard using dual row right-angled headers, the rows will align correctly. It took me quite some time to figure this out and correct the pin assignments in the program.

Step 8: Prototyping - Micro Gear Motor and Encoder

  • Fix the micro metal gear motor with encoder using N20 motor mounts.
  • Connect the motor and encoder wires as shown in the picture.
  • Left encoder - Teensy pins 4 & 0
  • Right encoder - Teensy pins 9 & 27

Step 9: Prototyping - LEDs

The two LEDs indicate whether the robot has detected a turn or not. I have used a 470-ohm series resistor to connect the LEDs to Teensy.

  • Left LED anode to Teensy pin 6
  • Right LED anode to Teensy pin 8

Step 10: Prototyping - Breakouts

Now that we have completed all our soldering on the perfboard, we can carefully cut along the boundary marked on the perfboard and remove the extra bits of perfboard. Also, attach the two wheels and caster wheel.

Insert all the breakout boards in their respective sockets. For inserting the FFC-DIP breakout and for fixing the QTRX-MD-16A line sensor, refer to the video.

Step 11: Software Libraries Overview

We will program the Teensy in Arduino IDE. We will need some libraries before we begin. The libraries that we will use are:

And some that have been written specifically for this robot,

  • PushButton
  • LineSensor
  • TeensyviewMenu
  • Motors

The libraries specific to this robot are discussed in detail and are available for download in the next steps.

Step 12: Libraries Explained - PushButton

This library is for interfacing the pushbutton breakout board with the Teensy. The functions used are

PushButton(int leftButtonPin, int centreButtonPin, int rightButtonPin);

Calling this constructor by creating an object configures the pushbutton pins to INPUT_PULLUP mode.

int8_t waitForButtonPress(void);

This function waits until a button is pressed and released and returns the key code.

int8_t getSingleButtonPress(void);

This function checks if a button is pressed and released. If yes, returns the key code else returns zero.

Step 13: Libraries Explained - Line Sensor

LineSensor is the library for interfacing the line sensor array with Teensy. The following are the functions used.


Calling this constructor by creating an object initializes ADC0 and ADC1, reads threshold, minimum and maximum values from EEPROM and configures the sensor pins to input mode and emitter control pin to output mode.

void calibrate(uint8_t calibrationMode);

This function calibrates the line sensors. The calibrationMode can be either MIN_MAX or MEDIAN_FILTER. This function is explained in detail in a later step.

void getSensorsAnalog(uint16_t *sensorValue, uint8_t mode);

Reads sensor array in any of the three modes passed as argument. The mode is the state of the emitters and can be ON, OFF or TOGGLE. TOGGLE mode compensates the sensor readings of reflectance due to ambient light. The sensors connected to ADC0 and ADC1 are read synchronously.

int getLinePosition(uint16_t *sensorValue);

Calculates the position of the sensor array over the line by the weighted average method.

uint16_t getSensorsBinary(uint16_t *sensorValue);

Returns a 16-bit representation of the state of the sensors. A binary one indicates that the sensor is over the line and a binary zero indicates that the sensor is off the line.

uint8_t countBinary(uint16_t binaryValue);

Passing the 16-bit representation of sensor values to this function returns the number of sensors that are over the line.

void getSensorsNormalized(uint16_t *sensorValue, uint8_t mode);

Reads the sensor values and constrains each sensor value to its corresponding min and max values. The sensor values are then mapped from their corresponding min to max range to 0 to 1000 range.

Step 14: Libraries Explained - TeensyviewMenu

TeensyviewMenu is the library where the functions for the display menu can be accessed. The following are the functions used.


Calling this constructor creates an object of class LineSensor, PushButton and TeensyView.

void intro(void);

This is for navigating the menu.

void test(void);

This is called internally within the menu when the line sensor values are to be displayed on Teensyview for testing.

Step 15: Libraries Explained - Motors

Motors is the library used for driving the two motors. The following are the functions used.


Calling this constructor by creating an object configures the motor direction control and PWM control pins to output mode.

void setSpeed(int leftMotorSpeed, int rightMotorSpeed);

Calling this function drives the two motors at speeds passed as arguments. The value of speed can range from -255 to +255 with a negative sign indicating that the direction of rotation is reversed.

Step 16: Testing - Encoder Odometry

We will test the magnetic wheel encoders and display the position and distance covered by the robot.

Upload the DualEncoderTeensyview.ino. The program displays the encoder ticks on Teensyview. The encoder ticks increment if you move the robot forward and decrement if you move it backward.

Now upload the EncoderOdometry.ino. This program displays the position of the robot in terms of x-y coordinates, displays the total distance covered in centimeter and the angle turned in degrees.

I have referred the Implementing Dead Reckoning by Odometry on a Robot with R/C Servo Differential Drive by Seattle Robotics Society for determining position from encoder ticks.

Step 17: Testing - Prop Shield Motion Sensors

Make sure you have calibrated the motion sensors by following the steps mentioned here.

Now upload the PropShieldTeensyView.ino. You should be able to see the accelerometer, gyro and magnetometer values of all three axes on the Teensyview.

Step 18: Program Overview

The program for the advanced line follower is written in Arduino IDE. The program works in the following sequence explained below.

  • Values stored in EEPROM are read and menu is displayed.
  • On pressing LAUNCH, the program enters the loop.
  • Normalized line sensor values are read.
  • Binary value of line position is obtained using normalized sensor values.
  • The count of the number of sensors that are over the line is calculated from binary value of line position.
  • Encoder ticks are updated and total distance covered, x-y coordinates and angle are updated.
  • For different values of binary count ranging from 0 to 16, a set of instructions are executed. If the binary count is in the range 1 to 5 and if the sensors that are over the line are adjacent to each other, PID routine is called. Rotation is performed in other combinations of binary value and binary count.
  • In PID routine (which is infact a PD routine), the motors are driven at speeds calculated based on error, change in error, Kp and Kd values.

The program at present does not measure orientation values from prop shield. This is a work in progress and is being updated.

Upload TestRun20.ino. We will see how to navigate the menu, adjust settings and how to calibrate the line sensors in the next steps following which we will test our robot.

Step 19: Navigating Menu and Settings

The menu has the following settings which can be navigated using the left and right pushbuttons and selected using the center pushbutton. The settings and their functions are described below.

  1. CALIBRATE: To calibrate line sensors.
  2. TEST: To display line sensor values.
  3. LAUNCH: To start line following.
  4. MAX SPEED: To set the upper limit of the speed of the robot.
  5. ROTATE SPEED: To set the upper limit of the speed of the robot when it performs a turn i.e. when both wheels turn at equal speeds in opposite directions.
  6. KP: Proportional constant.
  7. KD: Derivative constant.
  8. RUN MODE: To select between two operating modes - NORMAL and ACCL. In NORMAL mode, the robot runs at predefined speeds corresponding to line position values. In ACCL mode, the MAX SPEED of the robot is substituted by ACCL SPEED at predefined stages of the track. This can be utilized to speed up the robot at straight sections of the track. The following settings are accessible only if the RUN MODE is set as ACCL.
  9. LAP DISTANCE: To set the total length of the race track.
  10. ACCL SPEED: To set the acceleration speed of the robot. This speed replaces MAX SPEED at different stages of the track as defined below.
  11. NO. OF STAGES: To set the number of stages where ACCL SPEED is used.
  12. STAGE 1: To set the start and end distances of the stage in which MAX SPEED is substituted by ACCL SPEED. For each stage, the start and end distances can be set separately.

Step 20: Line Sensor Calibration

Line sensor calibration is the process by which the threshold value of each of the 16 sensors is determined. This threshold value is used to decide whether a particular sensor is over the line or not. To determine the threshold values of 16 sensors, we use either of the two methods.

MEDIAN FILTER: In this method, the line sensors are placed above the white surface and a pre-defined number of sensor readings are taken for all 16 sensors. The median values of all 16 sensors are determined. The same process is repeated after placing the line sensors over the black surface. The threshold value is the average of median values of black and white surfaces.

MIN MAX: In this method, the sensor values are read repeatedly until the user prompts for a stop. The maximum and minimum values encountered by each sensor are stored. The threshold value is the average of minimum and maximum values.

The threshold values thus obtained are mapped to 0 to 1000 range.

The calibration of line sensors by the MIN MAX method is shown in the video. After calibrating the line sensors, the data can be visualized as shown in the picture. The following information is displayed.

  • A 16-bit binary representation of line position with a binary 1 indicating that the corresponding line sensor is over the line and a binary 0 indicating that the line sensor is off the line.
  • A count of the total number of sensors that are over the line.
  • Minimum, maximum and sensor values (raw and normalized) of the 16 sensors, one sensor at a time.
  • Line position in the range -7500 to +7500.

The minimum and maximum line sensor values are then stored in EEPROM.

Step 21: Test Run

The video is of a test run in which the robot is programmed to stop after it completes one lap.

Step 22: Final Thoughts and Improvements

The hardware which is put together to build this robot is not utilized to the full by the program which runs it. Lot of improvements could be made on the program part. The motion sensors of prop shield is not used at present for determining position and orientation. The odometry data from encoders can be combined with the orientation data from prop shield to accurately determine the position and heading of the robot. This data may then be used to program the robot to learn the track in multiple laps. I encourage you to experiment on this part and share your results.

Good luck.

Robots Contest

Second Prize in the
Robots Contest