🌐 Level 6 – Wi-Fi Robotics

Project 6.3: "Advanced Line Follower"

 

What you’ll learn

  • ✅ Basic line tracking: Read three TCRT5000 sensors and follow a dark line on a light surface (or the inverse).
  • ✅ Multi‑sensor logic: Decide turns using left, center, and right readings together.
  • ✅ Handling curves & intersections: Slow down in tight curves and choose paths at splits.
  • ✅ Adaptive speed: Change motor speed based on how “off center” you are.
  • ✅ Recovery search: If the line is lost, run a safe search pattern to find it again fast.

Blocks glossary (used in this project)

  • Analog input (ADC): Read each TCRT5000 reflectance sensor as 0–4095.
  • Thresholding: Convert ADC values into boolean “ON_LINE / OFF_LINE.”
  • Digital outputs (motor driver): Control L298N IN pins for left/right motors.
  • Speed variable: Use simple duty duration or step timing to simulate speed scaling.
  • Serial println: Print “L/C/R”, “STATE:…”, “TURN:…”, and “SPEED:…” for visibility.
  • If / else + patterns: Choose forward/turn/slow/search behaviors from sensor states.

What you need

PartHow many?Pin connection (suggested)
D1 R321USB cable (30 cm)
TCRT5000 reflectance3Left → ADC Pin 32, Center → ADC Pin 33, Right → ADC Pin 34
L298N motor driver1Left IN1→18, IN2→19; Right IN3→5, IN4→23
TT motors + wheels2L298N OUT1/OUT2 (left), OUT3/OUT4 (right)
Power for motors1External motor supply; shared ground with R32

Notes

  • Aim sensors downward a few mm from the floor.
  • Share ground between the sensor boards, L298N, and the D1 R32.
  • Calibrate thresholds for your floor/line colors.

Before you start

  • USB cable is plugged in and stable
  • Serial monitor is open
  • Quick test prints:
print("Ready!")  # Confirm serial is working so you can see messages

Microprojects 1–5

Microproject 6.3.1 – Basic line tracking

Goal: Read L/C/R sensors, threshold them, and choose forward/left/right.
Blocks used:

  • ADC read: Pins 32, 33, 34.
  • Thresholding: Make booleans for “line detected.”
  • Motor control: Forward/turn with simple prints.

MicroPython code:

import machine  # Import machine to access ADC and motor pins
import time  # Import time for small delays

# Sensors: Left, Center, Right on ADC pins
adcL = machine.ADC(machine.Pin(32))  # Create ADC for Left sensor on Pin 32
adcC = machine.ADC(machine.Pin(33))  # Create ADC for Center sensor on Pin 33
adcR = machine.ADC(machine.Pin(34))  # Create ADC for Right sensor on Pin 34 (input-only pin)
adcL.atten(machine.ADC.ATTN_11DB)  # Set attenuation for full range on Left
adcC.atten(machine.ADC.ATTN_11DB)  # Set attenuation for full range on Center
adcR.atten(machine.ADC.ATTN_11DB)  # Set attenuation for full range on Right
adcL.width(machine.ADC.WIDTH_12BIT)  # Use 12-bit resolution (0–4095) for Left
adcC.width(machine.ADC.WIDTH_12BIT)  # Use 12-bit resolution (0–4095) for Center
adcR.width(machine.ADC.WIDTH_12BIT)  # Use 12-bit resolution (0–4095) for Right
print("Microproject 6.3.1: Sensors ready (L=32, C=33, R=34)")  # Confirm sensor setup

# Motors: L298N IN pins
L_IN1 = machine.Pin(18, machine.Pin.OUT)  # Left motor IN1
L_IN2 = machine.Pin(19, machine.Pin.OUT)  # Left motor IN2
R_IN3 = machine.Pin(5, machine.Pin.OUT)   # Right motor IN3
R_IN4 = machine.Pin(23, machine.Pin.OUT)  # Right motor IN4
print("Motors ready (Left 18/19, Right 5/23)")  # Confirm motor setup

def motors_stop():  # Helper to stop both motors
    L_IN1.value(0)  # Left IN1 LOW
    L_IN2.value(0)  # Left IN2 LOW
    R_IN3.value(0)  # Right IN3 LOW
    R_IN4.value(0)  # Right IN4 LOW
    print("MOTORS:STOP")  # Print stop status

def motors_forward():  # Helper to move forward
    L_IN1.value(1)  # Left forward HIGH
    L_IN2.value(0)  # Left forward LOW
    R_IN3.value(1)  # Right forward HIGH
    R_IN4.value(0)  # Right forward LOW
    print("MOVE:FWD")  # Print forward status

def motors_left():  # Helper to turn left (pivot)
    L_IN1.value(0)  # Left backward LOW (stop/pivot)
    L_IN2.value(1)  # Left backward HIGH
    R_IN3.value(1)  # Right forward HIGH
    R_IN4.value(0)  # Right forward LOW
    print("TURN:LEFT")  # Print left turn

def motors_right():  # Helper to turn right (pivot)
    L_IN1.value(1)  # Left forward HIGH
    L_IN2.value(0)  # Left forward LOW
    R_IN3.value(0)  # Right backward LOW
    R_IN4.value(1)  # Right backward HIGH
    print("TURN:RIGHT")  # Print right turn

threshold = 2000  # Initial reflectance threshold (tune for your surface)
print("Threshold:", threshold)  # Show threshold value

# Read once and act (basic behavior demo)
L_raw = adcL.read()  # Read Left sensor raw value
C_raw = adcC.read()  # Read Center sensor raw value
R_raw = adcR.read()  # Read Right sensor raw value
print("RAW L/C/R:", L_raw, C_raw, R_raw)  # Print raw readings

L_on = L_raw > threshold  # True if Left detects dark line (if higher=dark)
C_on = C_raw > threshold  # True if Center detects dark line
R_on = R_raw > threshold  # True if Right detects dark line
print("ON_LINE L/C/R:", L_on, C_on, R_on)  # Print boolean detection states

if C_on and not L_on and not R_on:  # If only Center sees the line
    motors_forward()  # Move forward to stay centered
elif L_on and not C_on:  # If Left sees line and Center does not
    motors_left()  # Turn left to re-center
elif R_on and not C_on:  # If Right sees line and Center does not
    motors_right()  # Turn right to re-center
else:  # For other combinations (e.g., intersections or none)
    motors_stop()  # Stop as a safe default

Reflection: Thresholds turn analog light into clear “on line” decisions—center forward, side turn.
Challenge:

  • Easy: Invert logic if your sensor gives lower values on dark lines.
  • Harder: Add a tiny forward pulse before stopping to avoid jitter at edges.

Microproject 6.3.2 – Multi‑sensor tracking

Goal: Use all three sensors together to handle typical patterns like slight drift or dual hits.
Blocks used:

  • Combined rules: Handle L+C and C+R patterns smoothly.
  • Serial println: Print “RULE:LC/CR/ONLY_L/ONLY_R/CENTER”.

MicroPython code:

import time  # Import time for small delays

def read_states(threshold):  # Helper to read and threshold all sensors
    L_raw = adcL.read()  # Read Left ADC
    C_raw = adcC.read()  # Read Center ADC
    R_raw = adcR.read()  # Read Right ADC
    L_on = L_raw > threshold  # Threshold Left
    C_on = C_raw > threshold  # Threshold Center
    R_on = R_raw > threshold  # Threshold Right
    print("RAW:", L_raw, C_raw, R_raw)  # Print raw readings
    print("STATE:", L_on, C_on, R_on)  # Print boolean states
    return L_on, C_on, R_on  # Return booleans

def step_multi(threshold):  # One decision step using multi-sensor rules
    L_on, C_on, R_on = read_states(threshold)  # Read states
    if C_on and not L_on and not R_on:  # Center only → go forward
        print("RULE:CENTER")  # Print rule
        motors_forward()  # Forward
    elif L_on and C_on and not R_on:  # Left+Center → gentle left bias forward
        print("RULE:LC")  # Print rule
        motors_left()  # Turn left
        time.sleep(0.1)  # Short correction pulse
        motors_forward()  # Then forward
    elif R_on and C_on and not L_on:  # Right+Center → gentle right bias forward
        print("RULE:CR")  # Print rule
        motors_right()  # Turn right
        time.sleep(0.1)  # Short correction pulse
        motors_forward()  # Then forward
    elif L_on and not C_on and not R_on:  # Only Left → stronger left correction
        print("RULE:ONLY_L")  # Print rule
        motors_left()  # Turn left
    elif R_on and not C_on and not L_on:  # Only Right → stronger right correction
        print("RULE:ONLY_R")  # Print rule
        motors_right()  # Turn right
    else:  # Ambiguous (all off or all on at an intersection)
        print("RULE:AMBIG")  # Print rule
        motors_stop()  # Stop to think

# Demo calls (two steps)
step_multi(threshold)  # Run one multi-sensor decision
time.sleep(0.2)  # Small delay
step_multi(threshold)  # Run again

Reflection: Combining center with a side makes turns gentle—less zig‑zag, more stable motion.
Challenge:

  • Easy: Increase correction pulse to 150 ms for stronger steering.
  • Harder: Add a “both sides on, center off” rule to slow down before deciding.

Microproject 6.3.3 – Tight curves and intersections

Goal: Detect sharp curves (side sensor only for several reads) and intersections (all three on).
Blocks used:

  • Counters/windows: Confirm a state over time.
  • Speed scaling: Slow down on tight curves and at intersections.

MicroPython code:

curve_window = 5  # Number of consecutive steps to confirm a tight curve
left_hits = 0  # Counter for consecutive left-only detections
right_hits = 0  # Counter for consecutive right-only detections

def curve_intersection_step(threshold):  # One step with curve/intersection logic
    global left_hits  # Use global counter for left-only
    global right_hits  # Use global counter for right-only
    L_on, C_on, R_on = read_states(threshold)  # Read sensor states
    if L_on and not C_on and not R_on:  # Left-only (possible tight curve)
        left_hits += 1  # Increase left curve counter
        right_hits = 0  # Reset right curve counter
        print("CURVE_LEFT_HITS:", left_hits)  # Print counter
        motors_left()  # Turn left
        time.sleep(0.08)  # Brief slow movement to handle curve
    elif R_on and not C_on and not L_on:  # Right-only (possible tight curve)
        right_hits += 1  # Increase right curve counter
        left_hits = 0  # Reset left curve counter
        print("CURVE_RIGHT_HITS:", right_hits)  # Print counter
        motors_right()  # Turn right
        time.sleep(0.08)  # Brief slow movement to handle curve
    elif L_on and C_on and R_on:  # All three on (intersection or wide line)
        print("INTERSECTION:DETECTED")  # Print intersection message
        motors_stop()  # Stop briefly to choose path
        time.sleep(0.2)  # Short pause
        motors_forward()  # Default choice: go straight through
        time.sleep(0.2)  # Move forward a little
        left_hits = 0  # Reset curve counters
        right_hits = 0  # Reset curve counters
    else:  # Regular tracking
        left_hits = 0  # Reset left curve counter
        right_hits = 0  # Reset right curve counter
        step_multi(threshold)  # Use normal multi-sensor rules

# Demo: run several steps
for i in range(10):  # Run 10 decision steps
    curve_intersection_step(threshold)  # Handle curve/intersection logic
    time.sleep(0.05)  # Small delay between steps

Reflection: Windows and pauses make curves and intersections smooth instead of chaotic.
Challenge:

  • Easy: At intersections, add “TURN:LEFT” choice sometimes (e.g., every other intersection).
  • Harder: Use a “mode” variable (LEFT_BIAS/RIGHT_BIAS) to choose intersection behavior consistently.

Microproject 6.3.4 – Speed adjustment on curves

Goal: Adapt speed using a simple error value: side sensors increase error, center reduces it.
Blocks used:

  • Error metric: error = (R_on − L_on)*k with center bonus.
  • Speed mapping: Faster when centered, slower when off center.

MicroPython code:

def compute_error(L_on, C_on, R_on):  # Compute a simple lateral error
    base = 0  # Start error at zero
    if L_on and not R_on:  # If left sees line and right does not
        base -= 1  # Bias left (negative)
    if R_on and not L_on:  # If right sees line and left does not
        base += 1  # Bias right (positive)
    if C_on:  # If center sees line
        base *= 0.5  # Reduce error when center is on line
    print("ERROR:", base)  # Print computed error
    return base  # Return error value

def set_speed(error):  # Simulate speed by controlling move pulse length
    if abs(error) < 0.5:  # If error is small (well centered)
        pulse = 0.12  # Longer forward pulse (faster)
        print("SPEED:FAST")  # Print speed label
    else:  # If error is larger (off center)
        pulse = 0.06  # Shorter forward pulse (slower)
        print("SPEED:SLOW")  # Print speed label
    return pulse  # Return pulse duration

def speed_step(threshold):  # One step that adapts speed
    L_on, C_on, R_on = read_states(threshold)  # Read states
    err = compute_error(L_on, C_on, R_on)  # Compute error
    pulse = set_speed(err)  # Choose pulse based on error
    if C_on:  # If center sees line
        motors_forward()  # Move forward
        time.sleep(pulse)  # Move for chosen pulse
        motors_stop()  # Stop briefly
        time.sleep(0.03)  # Small pause
    elif L_on and not R_on:  # If only left sees the line
        motors_left()  # Turn left
        time.sleep(pulse)  # Turn for chosen pulse
        motors_stop()  # Stop briefly
        time.sleep(0.03)  # Small pause
    elif R_on and not L_on:  # If only right sees the line
        motors_right()  # Turn right
        time.sleep(pulse)  # Turn for chosen pulse
        motors_stop()  # Stop briefly
        time.sleep(0.03)  # Small pause
    else:  # If none see the line
        motors_stop()  # Stop for safety
        print("STATE:LOST")  # Print lost state

# Demo: run a few adaptive-speed steps
for i in range(8):  # Run 8 adaptive steps
    speed_step(threshold)  # Perform speed-adjusted move

Reflection: Even a tiny speed change reduces overshoot—smooth movement feels smarter.
Challenge:

  • Easy: Add “SPEED:MID” as a third level.
  • Harder: Scale pulse by a small table mapping |error| to pulse times.

Microproject 6.3.5 – Searching for a lost line

Goal: If no sensors see the line, run a safe search pattern (wiggle left/right, then forward).
Blocks used:

  • State check: Detect NONE_ON_LINE.
  • Pattern: Left wiggle → Right wiggle → Short forward.

MicroPython code:

def lost_line(threshold):  # Handle a lost line condition
    L_on, C_on, R_on = read_states(threshold)  # Read states
    if not L_on and not C_on and not R_on:  # If all sensors are off line
        print("SEARCH:START")  # Announce search start
        motors_left()  # Wiggle left
        time.sleep(0.15)  # Short left wiggle
        motors_right()  # Wiggle right
        time.sleep(0.15)  # Short right wiggle
        motors_forward()  # Small forward nudge
        time.sleep(0.12)  # Forward nudge duration
        motors_stop()  # Stop
        print("SEARCH:END")  # Announce search end
    else:  # If some sensor sees the line
        print("SEARCH:SKIP")  # Skip search
        step_multi(threshold)  # Continue normal tracking

# Demo: run lost-line handler a few times
for i in range(5):  # Attempt search steps
    lost_line(threshold)  # Call lost-line handler
    time.sleep(0.1)  # Small delay

Reflection: A gentle wiggle saves time; big spinning wastes battery and can lose orientation.
Challenge:

  • Easy: Add a second search pass with longer wiggles if the first fails.
  • Harder: Remember the last seen side (LEFT or RIGHT) and bias the search toward it.

Main project – Advanced line follower

Blocks steps (with glossary)

  • Sensor reads + thresholds: Convert ADC to clear booleans for L/C/R.
  • Multi‑sensor rules: Forward when centered, corrective turns for side hits.
  • Curves/intersections: Confirm with counters; slow down and decide.
  • Adaptive speed: Map simple error to pulse length.
  • Recovery: Run a safe search pattern when line is lost.

MicroPython code (mirroring blocks)

# Project 6.3 – Advanced Line Follower

import machine  # Import machine for ADC and motor control
import time  # Import time for delays and timing

# Sensors setup: ADC Left, Center, Right
adcL = machine.ADC(machine.Pin(32))  # Left sensor on Pin 32
adcC = machine.ADC(machine.Pin(33))  # Center sensor on Pin 33
adcR = machine.ADC(machine.Pin(34))  # Right sensor on Pin 34
for adc in (adcL, adcC, adcR):  # Configure each ADC similarly
    adc.atten(machine.ADC.ATTN_11DB)  # Full-scale attenuation
    adc.width(machine.ADC.WIDTH_12BIT)  # 12-bit resolution (0–4095)
print("INIT: Sensors L=32, C=33, R=34")  # Confirm sensor pins

# Motors setup: L298N IN pins
L_IN1 = machine.Pin(18, machine.Pin.OUT)  # Left IN1
L_IN2 = machine.Pin(19, machine.Pin.OUT)  # Left IN2
R_IN3 = machine.Pin(5, machine.Pin.OUT)   # Right IN3
R_IN4 = machine.Pin(23, machine.Pin.OUT)  # Right IN4
print("INIT: Motors Left(18,19) Right(5,23)")  # Confirm motor pins

def motors_stop():  # Stop both motors
    L_IN1.value(0)  # Left IN1 LOW
    L_IN2.value(0)  # Left IN2 LOW
    R_IN3.value(0)  # Right IN3 LOW
    R_IN4.value(0)  # Right IN4 LOW
    print("MOTORS:STOP")  # Print stop

def motors_forward():  # Move forward
    L_IN1.value(1)  # Left forward HIGH
    L_IN2.value(0)  # Left forward LOW
    R_IN3.value(1)  # Right forward HIGH
    R_IN4.value(0)  # Right forward LOW
    print("MOVE:FWD")  # Print forward

def motors_left():  # Pivot left
    L_IN1.value(0)  # Left backward LOW
    L_IN2.value(1)  # Left backward HIGH
    R_IN3.value(1)  # Right forward HIGH
    R_IN4.value(0)  # Right forward LOW
    print("TURN:LEFT")  # Print left turn

def motors_right():  # Pivot right
    L_IN1.value(1)  # Left forward HIGH
    L_IN2.value(0)  # Left forward LOW
    R_IN3.value(0)  # Right backward LOW
    R_IN4.value(1)  # Right backward HIGH
    print("TURN:RIGHT")  # Print right turn

threshold = 2000  # Reflectance threshold (tune for your surface)
print("THRESHOLD:", threshold)  # Show threshold

curve_window = 5  # Steps to confirm tight curve
left_hits = 0  # Consecutive left-only counter
right_hits = 0  # Consecutive right-only counter

def read_states(th):  # Read and threshold sensors
    L_raw = adcL.read()  # Read Left raw
    C_raw = adcC.read()  # Read Center raw
    R_raw = adcR.read()  # Read Right raw
    L_on = L_raw > th  # Threshold Left
    C_on = C_raw > th  # Threshold Center
    R_on = R_raw > th  # Threshold Right
    print("RAW L/C/R:", L_raw, C_raw, R_raw)  # Print raw
    print("ON_LINE L/C/R:", L_on, C_on, R_on)  # Print booleans
    return L_on, C_on, R_on  # Return states

def compute_error(L_on, C_on, R_on):  # Simple lateral error
    base = 0  # Start at zero
    if L_on and not R_on:  # Left bias
        base -= 1  # Negative error
    if R_on and not L_on:  # Right bias
        base += 1  # Positive error
    if C_on:  # Center helps stability
        base *= 0.5  # Reduce error magnitude
    print("ERROR:", base)  # Print error
    return base  # Return error

def set_speed(error):  # Map error to speed pulse
    if abs(error) < 0.5:  # Well centered
        pulse = 0.12  # Faster pulse
        print("SPEED:FAST")  # Print speed
    else:  # Off center
        pulse = 0.06  # Slower pulse
        print("SPEED:SLOW")  # Print speed
    return pulse  # Return pulse length

def step_multi(th):  # Multi-sensor rule step
    L_on, C_on, R_on = read_states(th)  # Get states
    if C_on and not L_on and not R_on:  # Center only
        print("RULE:CENTER")  # Print rule
        motors_forward()  # Forward
    elif L_on and C_on and not R_on:  # Left+Center
        print("RULE:LC")  # Print rule
        motors_left()  # Turn left
        time.sleep(0.1)  # Correction pulse
        motors_forward()  # Forward
    elif R_on and C_on and not L_on:  # Right+Center
        print("RULE:CR")  # Print rule
        motors_right()  # Turn right
        time.sleep(0.1)  # Correction pulse
        motors_forward()  # Forward
    elif L_on and not C_on and not R_on:  # Only Left
        print("RULE:ONLY_L")  # Print rule
        motors_left()  # Turn left
    elif R_on and not C_on and not L_on:  # Only Right
        print("RULE:ONLY_R")  # Print rule
        motors_right()  # Turn right
    else:  # Ambiguous
        print("RULE:AMBIG")  # Print rule
        motors_stop()  # Stop

def curve_intersection_step(th):  # Handle curves/intersections
    global left_hits  # Use global counters
    global right_hits  # Use global counters
    L_on, C_on, R_on = read_states(th)  # Read states
    if L_on and not C_on and not R_on:  # Left-only
        left_hits += 1  # Count left curve
        right_hits = 0  # Reset right
        print("CURVE_LEFT_HITS:", left_hits)  # Print counter
        motors_left()  # Turn left
        time.sleep(0.08)  # Slow turn
    elif R_on and not C_on and not L_on:  # Right-only
        right_hits += 1  # Count right curve
        left_hits = 0  # Reset left
        print("CURVE_RIGHT_HITS:", right_hits)  # Print counter
        motors_right()  # Turn right
        time.sleep(0.08)  # Slow turn
    elif L_on and C_on and R_on:  # Intersection
        print("INTERSECTION")  # Print intersection
        motors_stop()  # Pause
        time.sleep(0.2)  # Decide time
        motors_forward()  # Default straight
        time.sleep(0.2)  # Move a bit
        left_hits = 0  # Reset counters
        right_hits = 0  # Reset counters
    else:  # Regular tracking
        step_multi(th)  # Use multi-sensor rules

def speed_step(th):  # Adaptive speed step
    L_on, C_on, R_on = read_states(th)  # Read states
    err = compute_error(L_on, C_on, R_on)  # Compute error
    pulse = set_speed(err)  # Speed mapping
    if C_on:  # Center on line
        motors_forward()  # Forward
        time.sleep(pulse)  # Move pulse
        motors_stop()  # Stop briefly
        time.sleep(0.03)  # Pause
    elif L_on and not R_on:  # Left-only
        motors_left()  # Turn left
        time.sleep(pulse)  # Move pulse
        motors_stop()  # Stop briefly
        time.sleep(0.03)  # Pause
    elif R_on and not L_on:  # Right-only
        motors_right()  # Turn right
        time.sleep(pulse)  # Move pulse
        motors_stop()  # Stop briefly
        time.sleep(0.03)  # Pause
    else:  # Lost
        motors_stop()  # Stop
        print("STATE:LOST")  # Print lost

def lost_line(th):  # Recovery search
    L_on, C_on, R_on = read_states(th)  # Read states
    if not L_on and not C_on and not R_on:  # All off
        print("SEARCH:START")  # Announce search
        motors_left()  # Wiggle left
        time.sleep(0.15)  # Wiggle
        motors_right()  # Wiggle right
        time.sleep(0.15)  # Wiggle
        motors_forward()  # Nudge forward
        time.sleep(0.12)  # Nudge duration
        motors_stop()  # Stop
        print("SEARCH:END")  # Announce end
    else:  # Some see line
        step_multi(th)  # Normal tracking

print("RUN: Advanced line follower")  # Announce start
while True:  # Continuous control loop
    curve_intersection_step(threshold)  # Handle curves/intersections first
    speed_step(threshold)  # Adapt speed using error
    lost_line(threshold)  # Try recovery if needed
    time.sleep(0.05)  # Small loop delay

External explanation

  • What it teaches: You turned analog reflectance into decisions, handled tricky corners and intersections, adjusted speed with a simple error, and added a recovery search.
  • Why it works: Multi‑sensor rules reduce zig‑zagging, counters confirm curves, speed scaling reduces overshoot, and search patterns quickly re‑acquire the line.
  • Key concept: “Sense → decide → adjust → recover.”

Story time

Your robot glides along the tape trail, slows for a tight bend, pauses at a crossroads, and then shoots straight with confidence. When it slips, it wiggles, finds the track, and keeps going—like it’s stubborn in a good way.


Debugging (2)

Debugging 6.3.1 – Loses the line on curves

Problem: Robot shoots past tight bends and ends up off the track.
Clues: Side‑only states appear briefly, robot keeps full speed.
Broken code:

time.sleep(0.12)  # Long forward pulse even when off center

Fixed code:

pulse = set_speed(err)  # Choose pulse based on error
# Smaller pulse when |error| is large prevents overshoot on curves

Why it works: Shorter pulses when off center reduce drift and let corrections happen quickly.
Avoid next time: Tie speed to error and add pauses between corrective moves.

Debugging 6.3.2 – Non‑adaptive speed

Problem: Robot moves at one speed all the time, zig‑zagging or stalling.
Clues: “SPEED:FAST” never changes or “SPEED:SLOW” always prints.
Broken code:

def set_speed(error):
    return 0.1  # Fixed pulse (ignores error)

Fixed code:

def set_speed(error):
    if abs(error) < 0.5:
        return 0.12  # FAST when centered
    else:
        return 0.06  # SLOW when off center

Why it works: Adaptive speed matches motion to sensor confidence, keeping movement smooth.
Avoid next time: Never ignore error—use it to guide speed and turning.


Final checklist

  • L/C/R ADC values print and threshold to clear booleans
  • Multi‑sensor rules produce forward and gentle corrections
  • Curves confirmed over windows; intersections handled with short pauses
  • Speed adapts to error (FAST when centered, SLOW on edges)
  • Lost‑line search pattern wiggles safely and re‑acquires the track

Extras

  • 🧠 Student tip: Mark your track with consistent tape color and width; re‑calibrate threshold after lighting changes.
  • 🧑‍🏫 Instructor tip: Have students log RAW L/C/R while driving different segments to pick good thresholds.
  • 📖 Glossary:
    • Threshold: A cut‑off value separating “on” vs “off” detection.
    • Window: Consecutive detections used to confirm a state.
    • Error: A simple measure of how far off center you are.
  • 💡 Mini tips:
    • Keep sensors at the same height; uneven mounting causes false turns.
    • Insert brief stops before changing direction to prevent stalling.
    • Share ground across sensors, driver, and MCU for stable readings.
On this page