3.5. Using the Line Follower¶
3.5.1. Reading the Sensor¶
In order to run this example program, we need to have a GoPiGo3 because bus "GPG3_AD1"
is used in this case and it’s specific to the GoPiGo3 platform.
The "GPG3_AD1"
bus translates to port "AD1"
on the GoPiGo3, so the Line Follower Sensor has to be connected to port "AD1"
.
The default "RPI_1SW"
bus could have been used, but since this is an example, let’s do it with a GoPiGo3.
This is the exact same scenario as the one in IMU sensor example.
The source file for this example program can be found here on github.
import time
from di_sensors import line_follower
print("Example program for reading a Dexter Industries Line Follower sensor on GPG3 AD1 port")
lf = line_follower.LineFollower(bus = "GPG3_AD1")
'''
cd ~/Dexter/DI_Sensors/Python
sudo python setup.py install
python ~/Dexter/DI_Sensors/Python/Examples/LineFollower.py
'''
print("Manufacturer : %s" % lf.get_manufacturer())
print("Name : %s" % lf.get_board())
print("Firmware Version : %d" % lf.get_version_firmware())
while True:
# Read the line sensors values
values = lf.read_sensors()
str = ""
for v in range(len(values)):
str += "%.3f " % values[v]
print(str)
time.sleep(0.1)
The console output of this script should look like:
Example program for reading a Dexter Industries Line Follower sensor on GPG3 AD1 port
Manufacturer : Dexter Industries
Name : Line Follower
Firmware Version : 1
0.031 0.044 0.051 0.038 0.033 0.017
0.035 0.054 0.063 0.051 0.048 0.026
0.048 0.065 0.075 0.061 0.059 0.034
0.047 0.065 0.076 0.063 0.064 0.040
0.063 0.080 0.096 0.091 0.091 0.071
0.070 0.087 0.104 0.103 0.107 0.087
0.070 0.088 0.107 0.103 0.108 0.090
0.120 0.090 0.115 0.121 0.128 0.116
0.283 0.089 0.121 0.141 0.177 0.196
0.383 0.109 0.155 0.206 0.299 0.404
0.454 0.159 0.185 0.250 0.396 0.340
0.287 0.338 0.111 0.115 0.132 0.137
0.443 0.208 0.268 0.132 0.109 0.079
0.285 0.484 0.259 0.167 0.095 0.057
0.589 0.200 0.268 0.176 0.091 0.051
0.695 0.158 0.180 0.359 0.219 0.157
0.625 0.095 0.152 0.369 0.227 0.069
0.398 0.098 0.127 0.383 0.372 0.081
0.698 0.111 0.128 0.720 0.902 0.193
0.297 0.124 0.138 0.447 0.843 0.187
0.104 0.146 0.180 0.392 0.960 0.291
0.096 0.132 0.158 0.319 0.945 0.299
0.094 0.128 0.152 0.376 0.935 0.249
0.084 0.114 0.144 0.710 0.716 0.143
0.065 0.095 0.567 0.617 0.149 0.157
0.061 0.298 0.331 0.096 0.109 0.117
0.275 0.239 0.102 0.112 0.144 0.170
3.5.2. Basic Line Follower Controller¶
The simplest way to make the GoPiGo3 follow a line with the Line Follower Sensor is by having a controller that can output 3 different states: one for the left, one for the center when it’s properly aligned and another one for the right. When the line that the robot is following is on the left side, then command the robot to go to the left, otherwise go to the right.
That simple! Now, let’s see how that code looks like. The following example was done for the GoPiGo3 but it can easily be adapted for any other robot that has motors.
import easygopigo3 as easy
from di_sensors.easy_line_follower import EasyLineFollower
gpg = easy.EasyGoPiGo3()
my_linefollower = EasyLineFollower()
gpg.forward()
while not my_linefollower.position() == "black":
line_pos = my_linefollower.position()
if line_pos == 'center':
gpg.forward()
elif line_pos == 'left':
gpg.left()
elif line_pos == 'right':
gpg.right()
gpg.stop()
When running this in reality, the result is going to be choppy, so if you need a better control of the robot’s trajectory, go on and check the next section.
3.5.3. PID-Based Controller¶
Also, doing something more advanced with the line follower is possible by using the EasyLineFollower
class. With an object of such type,
a estimated position of the black line can be returned and then feed this estimate into a PID controller. Consequently, a robot (such as the GoPiGo3) can be precisely controlled.
from di_sensors.easy_line_follower import EasyLineFollower
from time import time, sleep
setpoint = 0.5
integralArea = 0.0
previousError = 0.0
motorBaseSpeed = 300
loopTime = 1.0 / 100
Kp = 0.0 # a value suitable for this component
Ki = 0.0 # ditto
Kd = 0.0 # ditto as above
lf = EasyLineFollower()
while True:
start = time()
pos, out_of_line = lf.read_sensors('weighted-avg')
error = pos - setpoint
integralArea += error
correction = Kp * error + Ki * integralArea + Kd * (previousError - error)
previousError = error
motorA = motorBaseSpeed + correction
motorB = motorBaseSpeed - correction
# code for actuating the robot to follow the line
# using the previously computed values for each motor
# to make it run at certain frequency
end = time()
alreadySpent = end - start
remainingTime = loopTime - alreadySpent
if remainingTime > 0:
sleep(remainingTime)
With something like the above code, you can make a pretty reliable control system for the line follower.