Skip to content

Commit 2a50a59

Browse files
...
1 parent 070c1b1 commit 2a50a59

2 files changed

Lines changed: 173 additions & 28 deletions

File tree

obstacle_avoidance.py

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from gpiozero import DistanceSensor
22
import time
33

4-
54
class ObstacleAvoidance:
65
def __init__(self, trigger_pin=27, echo_pin=22, motors=None, threshold=0.5):
76
# Faster response (no smoothing delay)
@@ -11,80 +10,136 @@ def __init__(self, trigger_pin=27, echo_pin=22, motors=None, threshold=0.5):
1110
max_distance=2.0,
1211
queue_len=1
1312
)
14-
1513
self.motors = motors
1614
self.threshold = threshold
1715
self.turn_left_next = True
18-
16+
1917
# More realistic speeds for a Pi 3 robot
2018
self.min_speed = 0.3
2119
self.max_speed = 0.6
22-
20+
21+
# Stuck detection
22+
self.obstacle_count = 0
23+
self.last_obstacle_time = 0
24+
self.stuck_threshold = 3 # Number of obstacles in quick succession
25+
self.stuck_time_window = 2.0 # Within 2 seconds
26+
self.committed_direction = None # Will be 'left' or 'right'
27+
self.commitment_timeout = 5.0 # Stay committed for 5 seconds
28+
self.commitment_start_time = 0
29+
2330
def get_distance(self):
2431
try:
2532
return self.sensor.distance
2633
except Exception:
2734
return None
28-
35+
2936
def _map_speed(self, distance):
3037
if distance >= self.threshold:
3138
return self.max_speed
32-
3339
if distance <= 0:
3440
return self.min_speed
35-
3641
ratio = distance / self.threshold
3742
speed = self.min_speed + (self.max_speed - self.min_speed) * ratio
3843
return max(self.min_speed, min(self.max_speed, speed))
39-
44+
45+
def _detect_stuck(self):
46+
"""Check if robot is repeatedly hitting obstacles (stuck pattern)"""
47+
current_time = time.time()
48+
49+
# Check if we're still in the time window
50+
if current_time - self.last_obstacle_time < self.stuck_time_window:
51+
self.obstacle_count += 1
52+
else:
53+
# Reset counter if too much time has passed
54+
self.obstacle_count = 1
55+
56+
self.last_obstacle_time = current_time
57+
58+
# If we hit stuck threshold, commit to a direction
59+
if self.obstacle_count >= self.stuck_threshold:
60+
if self.committed_direction is None:
61+
# Choose direction based on current preference
62+
self.committed_direction = 'left' if self.turn_left_next else 'right'
63+
self.commitment_start_time = current_time
64+
print(f"🔄 STUCK DETECTED! Committing to turn {self.committed_direction.upper()}")
65+
return True
66+
return False
67+
68+
def _should_stay_committed(self):
69+
"""Check if we should still follow committed direction"""
70+
if self.committed_direction is None:
71+
return False
72+
73+
# Release commitment after timeout
74+
if time.time() - self.commitment_start_time > self.commitment_timeout:
75+
print(f"✅ Commitment timeout - resuming normal operation")
76+
self.committed_direction = None
77+
self.obstacle_count = 0
78+
return False
79+
80+
return True
81+
4082
def check_and_avoid(self):
4183
if self.motors is None:
4284
return False
43-
85+
4486
distance = self.get_distance()
4587
if distance is None:
4688
return False
47-
48-
# Debug (optional)
49-
# print(f"Distance: {distance:.2f} m")
50-
89+
5190
# 🚨 OBSTACLE DETECTED
5291
if distance < self.threshold:
53-
92+
# Check if we're stuck
93+
is_stuck = self._detect_stuck()
94+
5495
self.motors.stop()
5596
time.sleep(0.05)
56-
57-
# Reverse briefly (shorter, faster reaction)
97+
98+
# Reverse briefly
5899
self.motors.backward(self.min_speed)
59100
start_time = time.time()
60101
while time.time() - start_time < 0.3:
61102
pass
62-
63103
self.motors.stop()
64104
time.sleep(0.05)
65-
66-
# Turn (short controlled turn)
67-
if self.turn_left_next:
105+
106+
# Decide turn direction
107+
if self._should_stay_committed():
108+
# Stuck mode: always turn the same way
109+
turn_direction = self.committed_direction
110+
# Longer turn when stuck
111+
turn_duration = 0.7
112+
else:
113+
# Normal mode: alternate
114+
turn_direction = 'left' if self.turn_left_next else 'right'
115+
turn_duration = 0.55
116+
self.turn_left_next = not self.turn_left_next
117+
118+
# Execute turn
119+
if turn_direction == 'left':
68120
self.motors.turn_left(self.min_speed)
69121
else:
70122
self.motors.turn_right(self.min_speed)
71-
72-
self.turn_left_next = not self.turn_left_next
73-
123+
74124
start_time = time.time()
75-
while time.time() - start_time < 0.55:
125+
while time.time() - start_time < turn_duration:
76126
pass
77-
127+
78128
self.motors.stop()
79129
return True
80-
130+
81131
# ✅ PATH CLEAR
82132
else:
133+
# Reset stuck detection if we have clear path
134+
if distance > self.threshold * 1.5: # Good clearance
135+
if self.obstacle_count > 0:
136+
self.obstacle_count = max(0, self.obstacle_count - 1)
137+
83138
speed = self._map_speed(distance)
84139
self.motors.forward(speed)
85140
return False
86-
141+
87142
def cleanup(self):
88143
if self.motors:
89144
self.motors.stop()
90-
self.sensor.close()
145+
self.sensor.close()

obstacle_avoidance_2.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
from gpiozero import DistanceSensor
2+
import time
3+
4+
5+
class ObstacleAvoidance:
6+
def __init__(self, trigger_pin=27, echo_pin=22, motors=None, threshold=0.5):
7+
# Faster response (no smoothing delay)
8+
self.sensor = DistanceSensor(
9+
echo=echo_pin,
10+
trigger=trigger_pin,
11+
max_distance=2.0,
12+
queue_len=1
13+
)
14+
15+
self.motors = motors
16+
self.threshold = threshold
17+
self.turn_left_next = True
18+
19+
# More realistic speeds for a Pi 3 robot
20+
self.min_speed = 0.3
21+
self.max_speed = 0.6
22+
23+
def get_distance(self):
24+
try:
25+
return self.sensor.distance
26+
except Exception:
27+
return None
28+
29+
def _map_speed(self, distance):
30+
if distance >= self.threshold:
31+
return self.max_speed
32+
33+
if distance <= 0:
34+
return self.min_speed
35+
36+
ratio = distance / self.threshold
37+
speed = self.min_speed + (self.max_speed - self.min_speed) * ratio
38+
return max(self.min_speed, min(self.max_speed, speed))
39+
40+
def check_and_avoid(self):
41+
if self.motors is None:
42+
return False
43+
44+
distance = self.get_distance()
45+
if distance is None:
46+
return False
47+
48+
# Debug (optional)
49+
# print(f"Distance: {distance:.2f} m")
50+
51+
# 🚨 OBSTACLE DETECTED
52+
if distance < self.threshold:
53+
54+
self.motors.stop()
55+
time.sleep(0.05)
56+
57+
# Reverse briefly (shorter, faster reaction)
58+
self.motors.backward(self.min_speed)
59+
start_time = time.time()
60+
while time.time() - start_time < 0.3:
61+
pass
62+
63+
self.motors.stop()
64+
time.sleep(0.05)
65+
66+
# Turn (short controlled turn)
67+
if self.turn_left_next:
68+
self.motors.turn_left(self.min_speed)
69+
else:
70+
self.motors.turn_right(self.min_speed)
71+
72+
self.turn_left_next = not self.turn_left_next
73+
74+
start_time = time.time()
75+
while time.time() - start_time < 0.55:
76+
pass
77+
78+
self.motors.stop()
79+
return True
80+
81+
# ✅ PATH CLEAR
82+
else:
83+
speed = self._map_speed(distance)
84+
self.motors.forward(speed)
85+
return False
86+
87+
def cleanup(self):
88+
if self.motors:
89+
self.motors.stop()
90+
self.sensor.close()

0 commit comments

Comments
 (0)