7.10. Exercise: micro:Maqueen PID#
In this exercise you will program your micro:Maqueen to use a PID controller to maintain a constant distance to an object in front of it using the ultrasonic sensor.
7.10.1. Instructions#
Prepare the hardware. You will need:
micro:Maqueen and batteries
ultrasonic sensor installed
Navigate to the MicroPython editor https://python.microbit.org/v/3. If you already have it open, then use that existing window or tab.
Connect your micro:bit using the instructions on the previous page.
Enter the following code into the editor.
Starting Code
from microbit import * import utime def ultrasound_measure(): pin1.write_digital(1) utime.sleep_us(10) pin1.write_digital(0) timeout = utime.ticks_us() while True: pulseBegin = utime.ticks_us() if 1 == pin2.read_digital(): break if (pulseBegin-timeout) > 5000: return -1 while True: pulseEnd = utime.ticks_us() if 0 == pin2.read_digital(): break if (pulseEnd-pulseBegin) > 5000: return -2 x = pulseEnd - pulseBegin d = x / 58 return int(d) def motor_left(speed, direction): buf = bytearray(3) buf[0] = 0x00 buf[1] = direction buf[2] = speed i2c.write(0x10, buf) def motor_right(speed, direction): buf = bytearray(3) buf[0] = 0x02 buf[1] = direction buf[2] = speed i2c.write(0x10, buf) i2c.init() target_distance_cm = 10 ALPHA = 0.1 # smoothing factor: 0 < ALPHA < 1 smoothed_distance_cm = 0 while True: dist = ultrasound_measure() if dist >= 0: # Smooth the distance reading smoothed_distance_cm = ALPHA * dist + (1 - ALPHA) * smoothed # TODO: Add PID control logic here sleep(10)
Flash the code to your micro:bit.
If you get stuck ask a peer or your teacher for help!
Question 1
Extend the code so that:
the maqueen uses proportional only control to maintain the distance
You can achieve this by the following steps:
Create a variable before the loop to hold the proportional gain
Inside the loop
Calculate the distance error
Calculate the P value by multiplying the proportional gain with the error
Set the motor speed to this value (optionally use a dead band)
Solution
from microbit import *
import utime
def ultrasound_measure():
pin1.write_digital(1)
utime.sleep_us(10)
pin1.write_digital(0)
timeout = utime.ticks_us()
while True:
pulseBegin = utime.ticks_us()
if 1 == pin2.read_digital():
break
if (pulseBegin-timeout) > 5000:
return -1
while True:
pulseEnd = utime.ticks_us()
if 0 == pin2.read_digital():
break
if (pulseEnd-pulseBegin) > 5000:
return -2
x = pulseEnd - pulseBegin
d = x / 58
return int(d)
def motor_left(speed, direction):
buf = bytearray(3)
buf[0] = 0x00
buf[1] = direction
buf[2] = speed
i2c.write(0x10, buf)
def motor_right(speed, direction):
buf = bytearray(3)
buf[0] = 0x02
buf[1] = direction
buf[2] = speed
i2c.write(0x10, buf)
i2c.init()
target_distance_cm = 10
ALPHA = 0.1 # smoothing factor: 0 < ALPHA < 1
smoothed_distance_cm = 0
kp = 10
while True:
dist = ultrasound_measure()
if dist >= 0:
# Smooth the distance reading
smoothed_distance_cm = ALPHA * dist + (1 - ALPHA) * smoothed
error = smoothed - target
# Proportional term
p = int(kp * math.fabs(error))
# Deadband
if math.fabs(error) > 3:
direction = int(error < 0)
speed = int(p)
else:
speed = 0
direction = 0
motor_left(speed, direction)
motor_right(speed, direction)
sleep(10)
Question 2
Extend the code so that:
the maqueen uses proportional-derivative control to smoothly maintain the distance and prevent overshoot
You can achieve this by the following steps:
Create variable before the loop to hold:
the previous time the loop ran i.e.
utime.ticks_us()the previous distance error
the derivative gain
Inside the loop:
Record the current time in a variable
Calculate the time difference using
utime.ticks_diff(new_time, last_time)Calculate the D value by multiplying the proportional gain with the
(error - previous error)/(time difference)Set the motor speed to
(p+d)
Solution
Solution is locked
Question 3
Tip
For this question remove the rubber tyres from the maqueen to leave the plastic wheels. Then place the maqueen on a slippery surface like a desk or polished floor. The smooth floor will cause the wheels to slip and the maqueen will accumulate distance errors over time.
Extend the code so that:
the maqueen uses full PID control to smoothly maintain the distance
You can achieve this by the following steps:
Create variable before the loop to hold the:
integral gain
accumulated error
Inside the loop:
Update the accumulated error by adding the
(error * time difference)Calculate the integral (I) value by multiplying the integral gain with
(error * time difference)Set the motor speed to
(p+i+d)
Solution
Solution is locked