Exercise: micro:Maqueen PID

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#

  1. Prepare the hardware. You will need:

    • micro:Maqueen and batteries

    • ultrasonic sensor installed

  2. Navigate to the MicroPython editor https://python.microbit.org/v/3. If you already have it open, then use that existing window or tab.

  3. Connect your micro:bit using the instructions on the previous page.

  4. 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)
    
  5. 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