Skip to content

Commit aa02bb0

Browse files
Merge branch 's2025_teamA' of https://github.com/krishauser/GEMstack into s2025_teamA
2 parents a5af802 + 868325e commit aa02bb0

1 file changed

Lines changed: 1 addition & 206 deletions

File tree

testing/test_collision_detection.py

Lines changed: 1 addition & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -9,212 +9,7 @@
99
import matplotlib.patches as patches
1010
import math
1111

12-
13-
################################################################################
14-
########## Collisiong Detection ################################################
15-
################################################################################
16-
class CollisionDetector:
17-
"""
18-
Simulation class to update positions of two rectangles (vehicle and pedestrian)
19-
with velocities v1 and v2, performing collision detection at each time step.
20-
All functions remain within the class, and variables defined in __init__ remain unchanged;
21-
local copies are used during simulation.
22-
"""
23-
def __init__(self, x1, y1, t1, x2, y2, t2, v1, v2, total_time=10.0, desired_speed=2.0, acceleration=0.5):
24-
25-
self.vehicle_x = x1
26-
self.vehicle_y = y1
27-
self.pedestrian_x = x2
28-
self.pedestrian_y = y2
29-
30-
# Vehicle parameters with buffer adjustments
31-
self.vehicle_size_x = 3.2
32-
self.vehicle_size_y = 1.7
33-
self.vehicle_buffer_x = 3.0 + 1.0
34-
self.vehicle_buffer_y = 2.0
35-
36-
# Vehicle rectangle
37-
self.x1 = self.vehicle_x + (self.vehicle_size_x + self.vehicle_buffer_x)*0.5 # Offset for buffer (remains constant)
38-
self.y1 = self.vehicle_y
39-
self.w1 = self.vehicle_size_x + self.vehicle_buffer_x # Increase width with buffer
40-
self.h1 = self.vehicle_size_y + self.vehicle_buffer_y # Increase height with buffer
41-
self.t1 = t1
42-
43-
# Pedestrian rectangle
44-
self.x2 = x2
45-
self.y2 = y2
46-
self.w2 = 0.5
47-
self.h2 = 0.5
48-
self.t2 = t2
49-
50-
# Velocities are expected as [vx, vy]
51-
self.v1 = v1
52-
self.v2 = v2
53-
54-
self.dt = 0.1 # seconds
55-
self.total_time = total_time # seconds
56-
57-
self.desired_speed = desired_speed
58-
self.acceleration = acceleration
59-
60-
def set_params(self, speed, acceleration):
61-
self.desired_speed = speed
62-
self.acceleration = acceleration
63-
64-
def get_corners(self, x, y, w, h, theta):
65-
"""
66-
Returns the 4 corners of a rotated rectangle.
67-
(x, y): center of rectangle
68-
w, h: width and height of rectangle
69-
theta: rotation angle in radians
70-
"""
71-
cos_t = math.cos(theta)
72-
sin_t = math.sin(theta)
73-
hw, hh = w / 2.0, h / 2.0
74-
75-
corners = np.array([
76-
[ hw * cos_t - hh * sin_t, hw * sin_t + hh * cos_t],
77-
[-hw * cos_t - hh * sin_t, -hw * sin_t + hh * cos_t],
78-
[-hw * cos_t + hh * sin_t, -hw * sin_t - hh * cos_t],
79-
[ hw * cos_t + hh * sin_t, hw * sin_t - hh * cos_t]
80-
])
81-
82-
corners[:, 0] += x
83-
corners[:, 1] += y
84-
85-
return corners
86-
87-
def get_axes(self, rect):
88-
axes = []
89-
for i in range(len(rect)):
90-
p1 = rect[i]
91-
p2 = rect[(i + 1) % len(rect)]
92-
edge = p2 - p1
93-
normal = np.array([-edge[1], edge[0]])
94-
norm = np.linalg.norm(normal)
95-
if norm:
96-
normal /= norm
97-
axes.append(normal)
98-
return axes
99-
100-
def project(self, rect, axis):
101-
dots = np.dot(rect, axis)
102-
return np.min(dots), np.max(dots)
103-
104-
def sat_collision(self, rect1, rect2):
105-
"""
106-
Determines if two convex polygons (rectangles) collide using the
107-
Separating Axis Theorem (SAT).
108-
rect1 and rect2 are numpy arrays of shape (4,2) representing their corners.
109-
"""
110-
axes1 = self.get_axes(rect1)
111-
axes2 = self.get_axes(rect2)
112-
113-
for axis in axes1 + axes2:
114-
min1, max1 = self.project(rect1, axis)
115-
min2, max2 = self.project(rect2, axis)
116-
if max1 < min2 or max2 < min1:
117-
return False
118-
return True
119-
120-
def plot_rectangles(self, rect1, rect2, collision, ax):
121-
"""
122-
Plot two rectangles on a provided axis and indicate collision by color.
123-
"""
124-
color = 'red' if collision else 'blue'
125-
for rect in [rect1, rect2]:
126-
polygon = patches.Polygon(rect, closed=True, edgecolor=color, fill=False, linewidth=2)
127-
ax.add_patch(polygon)
128-
ax.scatter(rect[:, 0], rect[:, 1], color=color, zorder=3)
129-
ax.set_title(f"Collision: {'Yes' if collision else 'No'}")
130-
131-
def run(self, is_displayed=False):
132-
collision_distance = -1
133-
steps = int(self.total_time / self.dt) + 1
134-
135-
# Create local variables for positions; these will be updated
136-
# without modifying the __init__ attributes.
137-
current_x1 = self.x1
138-
current_y1 = self.y1
139-
current_x2 = self.x2
140-
current_y2 = self.y2
141-
current_v1 = self.v1[0]
142-
143-
if is_displayed:
144-
plt.ion() # Turn on interactive mode
145-
fig, ax = plt.subplots(figsize=(10,5))
146-
147-
for i in range(steps):
148-
t_sim = i * self.dt
149-
150-
# Compute rectangle corners using the local positional variables.
151-
rect1 = self.get_corners(current_x1, current_y1, self.w1, self.h1, self.t1)
152-
rect2 = self.get_corners(current_x2, current_y2, self.w2, self.h2, self.t2)
153-
154-
# Perform collision detection.
155-
collision = self.sat_collision(rect1, rect2)
156-
157-
if is_displayed:
158-
# Plot the current step.
159-
ax.clear()
160-
self.plot_rectangles(rect1, rect2, collision, ax)
161-
ax.set_aspect('equal')
162-
ax.grid(True, linestyle='--', alpha=0.5)
163-
ax.set_xlim(self.vehicle_x - 5, self.vehicle_x + 20)
164-
ax.set_ylim(self.vehicle_y -5, self.vehicle_y +5)
165-
166-
# Draw an additional rectangle (vehicle body) at the vehicle's center.
167-
rect_vehiclebody = patches.Rectangle(
168-
(current_x1 - (self.vehicle_size_x + self.vehicle_buffer_x)*0.5, current_y1 - self.vehicle_size_y * 0.5),
169-
self.w1 - self.vehicle_buffer_x,
170-
self.h1 - self.vehicle_buffer_y,
171-
edgecolor='green',
172-
fill=False,
173-
linewidth=2,
174-
linestyle='--'
175-
)
176-
ax.add_patch(rect_vehiclebody)
177-
178-
ax.text(0.01, 0.99, f"t = {t_sim:.1f}s", fontsize=12, transform=ax.transAxes, verticalalignment='top')
179-
plt.draw()
180-
181-
# Pause briefly to simulate real-time updating.
182-
plt.pause(self.dt * 0.05)
183-
184-
# Stop simulation if collision is detected.
185-
if collision:
186-
current_vehicle_x = current_x1 - (self.vehicle_size_x + self.vehicle_buffer_x) * 0.5
187-
current_vehicle_y = current_y1
188-
collision_distance = current_vehicle_x - self.vehicle_x
189-
break
190-
191-
# Update the vehicle's speed if it is not at the desired speed.
192-
next_v = current_v1 + self.acceleration * self.dt
193-
# Accelerating: Check if the vehicle is about to exceed the desired speed.
194-
if next_v > self.desired_speed and current_v1 <= self.desired_speed:
195-
current_v1 = self.desired_speed
196-
# Decelerating: Check if the vehicle is about to fall below the desired speed.
197-
elif next_v < self.desired_speed and current_v1 >= self.desired_speed:
198-
current_v1 = self.desired_speed
199-
else:
200-
current_v1 = next_v
201-
202-
current_v1 = 0.0 if current_v1 < 0.0 else current_v1
203-
204-
# Update local positions based on velocities.
205-
current_x1 += current_v1 * self.dt
206-
current_y1 += self.v1[1] * self.dt
207-
current_x2 += self.v2[0] * self.dt
208-
current_y2 += self.v2[1] * self.dt
209-
210-
if is_displayed:
211-
plt.ioff()
212-
plt.show(block=True)
213-
214-
# print("Collision distance:", collision_distance)
215-
216-
return collision_distance
217-
12+
from GEMstack.onboard.planning.pedestrian_yield_logic import CollisionDetector
21813

21914
if __name__ == "__main__":
22015
# Vehicle parameters. x, y, theta (angle in radians)

0 commit comments

Comments
 (0)