Skip to content

Obstacle Avoidance

Stopping Distance vs Trigger Distance

if distance < 20:  # cm
    stop()

This logic fails because robots don't stop instantly.

The physics: At speed 100, your robot might travel 5-8cm before actually stopping. If you trigger at 20cm and need 8cm to stop, you collide at 12cm.

                    Trigger        Stop         Obstacle
                    distance       distance
     [Robot]────────────┼────────────┼────────────[Wall]
                       20cm         8cm

     Must trigger at: obstacle_distance + stopping_distance

Additional failure modes: - Sensor timeout: Ultrasonic sensors can hang waiting for echoes that never return - Surface blindness: Soft/angled surfaces absorb sound - sensor reports "no obstacle" - Update rate: Sensor measurement takes time; robot moves during measurement

Robust avoidance requires measuring your actual stopping distance at each speed, adding margins, and handling sensor failures.


This tutorial covers detecting and avoiding obstacles using the ultrasonic sensor and optocouplers.

What You'll Learn

In this tutorial, you'll understand:

  • How ultrasonic sensors measure distance and their limitations
  • The concept of stopping distance and why it depends on speed
  • How to combine multiple sensors for robust obstacle detection
  • The importance of priority ordering in safety-critical systems
  • How to quantify performance through systematic testing

After completing this tutorial, you'll be able to:

  • ✅ Measure ultrasonic sensor response time and update rate
  • ✅ Implement stop-on-obstacle behavior with timeout handling
  • ✅ Calculate required trigger distance for different speeds
  • ✅ Combine ultrasonic and optocoupler sensors for edge detection
  • ✅ Measure and record avoidance success rates

Prerequisites: - [[ultrasonic|Ultrasonic Sensor Tutorial]] - [[optocoupler|Optocoupler Tutorial]] - [[Reference/05-motors/motor-basics|Motor Basics]]


Sensor Comparison

Different sensors have different strengths for obstacle detection:

Sensor Range Best For Limitations
Ultrasonic 2-400 cm Walls, large objects Soft/angled surfaces
Optocoupler 0-2 cm Line/edge detection Very short range
IR proximity 2-30 cm Close objects Affected by ambient light

For most obstacle avoidance, the ultrasonic sensor is the primary tool.


⚡ Task 1: Measure Ultrasonic Response Time

Before implementing avoidance, understand how fast the sensor responds.

measure_response.py
from machine import Pin
import time

trig = Pin(0, Pin.OUT)
echo = Pin(1, Pin.IN)

def measure_distance():
    trig.low()
    time.sleep_us(2)
    trig.high()
    time.sleep_us(10)
    trig.low()

    while echo.value() == 0:
        start = time.ticks_us()
    while echo.value() == 1:
        end = time.ticks_us()

    duration = time.ticks_diff(end, start)
    return duration * 0.0343 / 2  # cm

# Measure timing
times = []
for _ in range(100):
    t1 = time.ticks_us()
    dist = measure_distance()
    t2 = time.ticks_us()
    times.append(time.ticks_diff(t2, t1))

avg_time = sum(times) / len(times)
print(f"Average measurement time: {avg_time:.0f} µs")
print(f"Maximum update rate: {1000000 / avg_time:.0f} Hz")

Record your results: - Average measurement time: _ µs - At 10 cm distance: µs - At 50 cm distance: __ µs


⚡ Task 2: Simple Stop-on-Obstacle

The most basic avoidance: stop when something is close.

simple_stop.py
from machine import Pin
from pico_car import pico_car
import time

Motor = pico_car()
trig = Pin(0, Pin.OUT)
echo = Pin(1, Pin.IN)

STOP_DISTANCE = 15  # cm

def measure_distance():
    trig.low()
    time.sleep_us(2)
    trig.high()
    time.sleep_us(10)
    trig.low()

    timeout = time.ticks_us()
    while echo.value() == 0:
        if time.ticks_diff(time.ticks_us(), timeout) > 30000:
            return 999  # Timeout

    start = time.ticks_us()
    while echo.value() == 1:
        if time.ticks_diff(time.ticks_us(), start) > 30000:
            return 999

    duration = time.ticks_diff(time.ticks_us(), start)
    return duration * 0.0343 / 2

while True:
    dist = measure_distance()

    if dist < STOP_DISTANCE:
        Motor.Car_Stop()
        print(f"STOP! Object at {dist:.1f} cm")
    else:
        Motor.Car_Run(80, 80)

    time.sleep(0.05)

Test and observe: - Does it stop reliably? - What happens with soft objects (pillows, fabric)? - What's the minimum stopping distance at speed 80?


⚡ Task 3: Measure Stopping Distance

Critical question: How far does the robot travel before stopping?

measure_stopping.py
from machine import Pin
from pico_car import pico_car
import time

Motor = pico_car()
trig = Pin(0, Pin.OUT)
echo = Pin(1, Pin.IN)

def measure_distance():
    # ... same as above ...
    pass

# Test at different speeds
for speed in [60, 80, 100, 120]:
    input(f"Press Enter to test speed {speed}...")

    # Drive toward wall
    Motor.Car_Run(speed, speed)

    while True:
        dist = measure_distance()
        if dist < 20:  # Trigger distance
            Motor.Car_Stop()
            time.sleep(0.5)
            final_dist = measure_distance()
            print(f"Speed {speed}: Stopped at {final_dist:.1f} cm")
            break
        time.sleep(0.02)

    time.sleep(2)

Record stopping distances:

Speed Trigger (cm) Final Distance (cm) Stopping Distance (cm)
60 20
80 20
100 20
120 20

Analysis: What trigger distance do you need for each speed to avoid collision?


⚡ Task 4: Turn-and-Continue Avoidance

When an obstacle is detected, turn and continue:

turn_avoid.py
from machine import Pin
from pico_car import pico_car
import time

Motor = pico_car()
trig = Pin(0, Pin.OUT)
echo = Pin(1, Pin.IN)

STOP_DISTANCE = 20
TURN_TIME = 0.5  # seconds

def measure_distance():
    # ... same as above ...
    pass

while True:
    dist = measure_distance()

    if dist < STOP_DISTANCE:
        # Stop
        Motor.Car_Stop()
        time.sleep(0.2)

        # Back up a bit
        Motor.Car_Back(80, 80)
        time.sleep(0.3)

        # Turn right
        Motor.Car_Right(80, 80)
        time.sleep(TURN_TIME)

        # Resume forward
        Motor.Car_Run(80, 80)
    else:
        Motor.Car_Run(80, 80)

    time.sleep(0.05)

Tune the parameters:

Parameter Effect Your Value
STOP_DISTANCE When to trigger
Back-up time How far to reverse
TURN_TIME How much to turn

⚡ Task 5: Smart Direction Choice

Choose turn direction based on which side is clearer:

smart_avoid.py
from machine import Pin
from pico_car import pico_car
import time

Motor = pico_car()
trig = Pin(0, Pin.OUT)
echo = Pin(1, Pin.IN)

def measure_distance():
    # ... same as above ...
    pass

def scan_left():
    """Turn left and measure"""
    Motor.Car_Left(60, 60)
    time.sleep(0.3)
    Motor.Car_Stop()
    time.sleep(0.1)
    return measure_distance()

def scan_right():
    """Turn right and measure"""
    Motor.Car_Right(60, 60)
    time.sleep(0.3)
    Motor.Car_Stop()
    time.sleep(0.1)
    return measure_distance()

while True:
    dist = measure_distance()

    if dist < 25:
        Motor.Car_Stop()

        # Scan both directions
        Motor.Car_Left(60, 60)
        time.sleep(0.3)
        Motor.Car_Stop()
        left_dist = measure_distance()

        Motor.Car_Right(60, 60)
        time.sleep(0.6)  # Return to center and go right
        Motor.Car_Stop()
        right_dist = measure_distance()

        print(f"Left: {left_dist:.0f} cm, Right: {right_dist:.0f} cm")

        # Choose clearer direction
        if left_dist > right_dist:
            Motor.Car_Left(80, 80)
            time.sleep(0.5)
        else:
            # Already facing right, continue
            pass

    Motor.Car_Run(80, 80)
    time.sleep(0.05)

⚡ Task 6: Edge Detection with Optocouplers

Combine ultrasonic (front obstacles) with optocouplers (floor edges):

combined_avoid.py
from machine import Pin
from pico_car import pico_car
import time

Motor = pico_car()

# Ultrasonic
trig = Pin(0, Pin.OUT)
echo = Pin(1, Pin.IN)

# Optocouplers (edge detection)
opto_left = Pin(2, Pin.IN)
opto_right = Pin(5, Pin.IN)

FRONT_DISTANCE = 20

def measure_distance():
    # ... same as above ...
    pass

while True:
    front_dist = measure_distance()
    left_edge = opto_left.value()   # 1 = edge/no floor
    right_edge = opto_right.value()

    # Priority 1: Edge detection (most dangerous)
    if left_edge == 1 or right_edge == 1:
        Motor.Car_Stop()
        print("EDGE DETECTED!")
        Motor.Car_Back(80, 80)
        time.sleep(0.5)
        Motor.Car_Right(80, 80)
        time.sleep(0.5)

    # Priority 2: Front obstacle
    elif front_dist < FRONT_DISTANCE:
        Motor.Car_Stop()
        print(f"Obstacle at {front_dist:.0f} cm")
        Motor.Car_Back(60, 60)
        time.sleep(0.3)
        Motor.Car_Right(80, 80)
        time.sleep(0.4)

    # Normal: Move forward
    else:
        Motor.Car_Run(80, 80)

    time.sleep(0.05)

⚡ Task 7: Measure Avoidance Success Rate

Quantify performance:

test_avoidance.py
# Manual test protocol
# Run robot through obstacle course 10 times
# Record: collisions, edge falls, successful avoidances

test_results = []

for trial in range(10):
    result = input(f"Trial {trial+1}: (s)uccess, (c)ollision, (e)dge fall: ")
    test_results.append(result)

success = test_results.count('s')
collisions = test_results.count('c')
edges = test_results.count('e')

print(f"\nResults:")
print(f"Success rate: {success}/10 ({success*10}%)")
print(f"Collisions: {collisions}")
print(f"Edge falls: {edges}")

Record your test results:

Trial Result Notes
1
2
...
10

Success rate: _____ %


Key Takeaways

  1. Sensor timing affects maximum robot speed
  2. Stopping distance increases with speed
  3. Multiple sensors provide redundancy
  4. Priority ordering (edge > obstacle > normal) prevents dangerous situations
  5. Quantitative testing reveals actual performance

Comparison: Avoidance Strategies

Strategy Complexity Effectiveness Use Case
Simple stop Low Limited Static demo
Turn-and-continue Medium Good General use
Smart direction Higher Better Complex environments
Multi-sensor Highest Best Safety-critical

Self-Assessment

Quick Check – Did You Get It?

🔹 Why does the ultrasonic sensor need a timeout in the measurement code? 🔹 What is the relationship between robot speed and required trigger distance? 🔹 Why do we check edge detection (optocouplers) before front obstacles (ultrasonic)? 🔹 What factors affect the stopping distance of the robot? 🔹 How would you improve the success rate if the robot keeps colliding?

Research Tasks

  1. Stopping distance formula: Plot the relationship between speed and stopping distance. Is it linear? What physical factors explain the relationship?

  2. Sensor fusion: Research how autonomous vehicles combine multiple sensors (LIDAR, cameras, ultrasonic). What principles apply to your robot?

  3. Reaction time analysis: Measure the total delay from obstacle detection to motor stop. Where does the time go (sensor, processing, motor response)?


Next Steps

  • [[Reference/05-motors/line-following|Line Following]] - Use sensors for path following
  • [[Reference/06-software/state-machine|State Machine]] - Structure complex behaviors
  • [[mission-tracks/ultrasonic-sensor|Ultrasonic Mission]] - Complete challenge