Note
Hello, welcome to the SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts Community on Facebook! Dive deeper into Raspberry Pi, Arduino, and ESP32 with fellow enthusiasts.
Why Join?
Expert Support: Solve post-sale issues and technical challenges with help from our community and team.
Learn & Share: Exchange tips and tutorials to enhance your skills.
Exclusive Previews: Get early access to new product announcements and sneak peeks.
Special Discounts: Enjoy exclusive discounts on our newest products.
Festive Promotions and Giveaways: Take part in giveaways and holiday promotions.
👉 Ready to explore and create with us? Click [here] and join today!
4. Obstacle Avoidance
In this project, picrawler will use an ultrasonic module to detect obstacles in front. When PiCrawler detects an obstacle, it will send a signal and look for another direction to move forward.
Run the Code
cd ~/picrawler/examples
sudo python3 4_avoid.py
When the program starts, PiCrawler stands up.
It continuously measures the distance using the ultrasonic sensor and prints the value in the terminal.
If an obstacle is detected within 15 cm: - A warning sound is played. - The robot performs a small left turn.
If the path is clear: - The robot moves forward.
The robot keeps avoiding obstacles automatically until you press Ctrl+C.
Before exiting, it safely sits down.
Code
Note
You can Modify/Reset/Copy/Run/Stop the code below. But before that, you need to go to source code path like picrawler\examples. After modifying the code, you can run it directly to see the effect.
from picrawler import Picrawler
from robot_hat import Music, Ultrasonic, Pin
import time
import signal
music = Music()
crawler = Picrawler()
sonar = Ultrasonic(Pin("D2"), Pin("D3")) # Ultrasonic trigger/echo pins
music.music_set_volume(100) # Set speaker volume
alert_distance = 15 # Obstacle warning distance (cm)
speed = 80 # Movement speed
# ----------------------------
# Add hardware timeout to sonar.read()
# Prevent program from freezing
# ----------------------------
class Timeout(Exception):
pass
def _alarm_handler(signum, frame):
raise Timeout()
signal.signal(signal.SIGALRM, _alarm_handler)
# Read distance once with timeout protection
def safe_read_once(timeout_s=1):
try:
signal.alarm(timeout_s)
d = sonar.read()
signal.alarm(0)
return d
except Timeout:
signal.alarm(0)
return None
except Exception:
signal.alarm(0)
return None
# Read multiple times and return median value (anti-noise)
def read_distance_filtered(n=5, gap=0.03, timeout_s=1):
vals = []
for _ in range(n):
d = safe_read_once(timeout_s=timeout_s)
if d is not None and d > 0:
vals.append(d)
time.sleep(gap)
if not vals:
return None
vals.sort()
return vals[len(vals)//2] # Median filter
def main():
distance = read_distance_filtered(n=5, gap=0.03, timeout_s=1)
print("distance:", distance)
if distance is None:
time.sleep(0.15) # Wait if read failed
return
if distance <= alert_distance:
# Obstacle detected → play sound and turn
try:
music.sound_play_threading('./sounds/sign.wav', volume=100)
except Exception as e:
print("sound error:", e)
crawler.do_action('turn left angle', 1, speed)
time.sleep(0.5) # Quiet window after movement
else:
# Path clear → move forward
crawler.do_action('forward', 1, speed)
time.sleep(0.4)
if __name__ == "__main__":
try:
crawler.do_step('stand', 40) # Stand before starting
time.sleep(1.0)
while True:
main()
except KeyboardInterrupt:
print("\nStop.")
finally:
try:
crawler.do_step('sit', 40) # Sit before exit
time.sleep(1.0)
except Exception:
pass
How it works?
Initialization Block
music = Music() crawler = Picrawler() sonar = Ultrasonic(Pin("D2"), Pin("D3")) music.music_set_volume(100) alert_distance = 15 speed = 80
This block initializes the three main modules: -
music: controls sound playback. -crawler: controls PiCrawler movement. -sonar: reads distance using the ultrasonic sensor.It also sets the speaker volume, the obstacle threshold (cm), and the movement speed.
Timeout Setup Block (prevents sonar.read() from freezing)
class Timeout(Exception): pass def _alarm_handler(signum, frame): raise Timeout() signal.signal(signal.SIGALRM, _alarm_handler)
The ultrasonic driver may block while waiting for the echo signal. This block installs a signal handler so the program can interrupt a stuck
sonar.read()call and keep running.Function: safe_read_once()
def safe_read_once(timeout_s=1): try: signal.alarm(timeout_s) d = sonar.read() signal.alarm(0) return d except Timeout: signal.alarm(0) return None except Exception: signal.alarm(0) return None
This function reads the ultrasonic distance once with timeout protection. - If the read succeeds, it returns the distance value. - If it times out or fails, it returns
Noneinstead of freezing.Function: read_distance_filtered()
def read_distance_filtered(n=5, gap=0.03, timeout_s=1): vals = [] for _ in range(n): d = safe_read_once(timeout_s=timeout_s) if d is not None and d > 0: vals.append(d) time.sleep(gap) if not vals: return None vals.sort() return vals[len(vals)//2]
This function improves reliability by reading multiple samples: - Invalid values (
Noneor<= 0) are ignored. - The remaining values are sorted. - The median value is returned to reduce noise.Function: main() (core decision and action)
def main(): distance = read_distance_filtered(...) if distance is None: return if distance <= alert_distance: music.sound_play_threading(...) crawler.do_action('turn left angle', 1, speed) else: crawler.do_action('forward', 1, speed)
This is the main control logic:
Reads a filtered distance value.
If reading fails, it skips this cycle.
If an obstacle is closer than
alert_distance, it plays a warning sound and turns left.Otherwise, it moves forward.
Program Entry Block (continuous loop + safe exit)
if __name__ == "__main__": try: crawler.do_step('stand', 40) while True: main() except KeyboardInterrupt: print("\nStop.") finally: crawler.do_step('sit', 40)
This block controls the overall program flow: - The crawler stands up before starting. - The program runs
main()repeatedly in an infinite loop. - Pressing Ctrl+C stops the loop. - The crawler sits down before the program exits.