Skip to content

Commit 87b4973

Browse files
committed
Computed a search axis based on the curb position, which defines the parking lot boundary. Project the current vehicle pose onto this axis and perform a search along it. The curb and cone inputs are expected as (x, y) points only—yaw and size are not required."
1 parent 3b7ff89 commit 87b4973

2 files changed

Lines changed: 258 additions & 37 deletions

File tree

GEMstack/onboard/planning/parallel_parking.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from typing import List
21
from ..component import Component
32
from ...utils import serialization
43
from ...state import Route,ObjectFrameEnum, AllState, VehicleState, Roadgraph, MissionObjective
@@ -7,9 +6,6 @@
76
import os
87
import numpy as np
98
from typing import List
10-
import reeds_shepp
11-
from typing import List, Tuple
12-
139

1410
class SummoningParkingRoutePlanner(Component):
1511
def __init__(self, routefn: str, vehicle_interface: GEMInterface, frame: str = 'start'):
@@ -38,11 +34,13 @@ def __init__(self, routefn: str, vehicle_interface: GEMInterface, frame: str = '
3834
# SLOT 5 (22.11, -2.44, 0.0, (1.7, 3.2))
3935
self.parked_cars = [
4036

41-
(17.33, -2.44, 0.0, (1.7, 3.2)),
42-
(22.11, -2.44, 0.0, (1.7, 3.2))
37+
(17.33, -2.44),
38+
(22.11, -2.44)
4339
]
4440
self.parking_utils = ReedsSheppParking()
4541
self.parking_utils.closest = False
42+
self.parking_utils.find_collision_free_trajectory(self.parked_cars)
43+
4644

4745

4846
def state_inputs(self):
@@ -56,7 +54,7 @@ def rate(self):
5654

5755
def update(self, vehicle: VehicleState, x=0.0):
5856
self.current_pose = vehicle.pose
59-
self.parking_utils.find_collision_free_trajectory()
6057
self.waypoints_to_go = self.parking_utils.waypoints_to_go
58+
self.parking_utils.find_collision_free_trajectory(self.parked_cars)
6159
self.route = Route(frame=ObjectFrameEnum.START, points=self.waypoints_to_go.tolist())
6260
return self.route

GEMstack/onboard/planning/reeds_shepp_parking.py

Lines changed: 253 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,49 @@
66
from typing import List, Tuple
77
import math
88

9-
109
Pose = Tuple[float, float, float] # (x, y, yaw)
1110
Dims = Tuple[float, float] # (width, length)
1211
Obstacle = Tuple[float, float, float, Dims] # (x, y, yaw, (width, length))
1312

13+
class ReedsSheppParking:
14+
def __init__(self, vehicle_pose = (0.0, 0.0, 0.0), vehicle_dims = (1.7, 3.2), compact_parking_spot_size = (2.44, 4.88),
15+
shift_from_center_to_rear_axis = 1.25, search_step_size = 0.1, closest = False, parking_lot_axis_shift_margin = 2.44,
16+
static_horizontal_curb_size = (2.44, 0.5),
17+
static_horizontal_curb_xy_coordinates = [(0.0, -2.44),(24.9, -2.44)],
18+
add_static_vertical_curb_as_obstacle = True,
19+
static_vertical_curb_xy_coordinates = [(12.45, -4.88)],
20+
static_vertical_curb_size = (2.44, 24.9),
21+
add_static_horizontal_curb_as_obstacle = True,
22+
detected_cones = []):
23+
24+
25+
self.detected_cones = detected_cones
26+
self.parked_cars = []
27+
self.objects_to_avoid_collisions = []
1428

29+
self.static_horizontal_curb_xy_coordinates = static_horizontal_curb_xy_coordinates
30+
self.static_horizontal_curb_size = static_horizontal_curb_size
31+
self.add_static_vertical_curb_as_obstacle = add_static_vertical_curb_as_obstacle
1532

16-
class ReedsSheppParking:
17-
def __init__(self,initial_pose_of_vehicle = (0.0, 0.0, 0.0), vehicle_dims = (1.7, 3.2), compact_parking_spot_size = (2.44, 4.88),
18-
shift_from_center_to_rear_axis = 1.25, search_step_size = 0.1, closest = False,
19-
static_horizontal_curb = [(0.0, -2.44, 0.0, (1.77, 0.5)),(24.9, -2.44, 0.0, (1.77, 0.5))],
20-
static_vertical_curb = [(12.45, -4.88, 0.0, (2.44, 24.9))],
21-
parked_cars = []):
22-
23-
self.initial_pose_of_vehicle = initial_pose_of_vehicle
24-
self.x_axis_of_search = self.initial_pose_of_vehicle[0]
25-
self.static_horizontal_curb = static_horizontal_curb
26-
self.static_vertical_curb = static_vertical_curb
27-
self.parked_cars = parked_cars
33+
self.static_vertical_curb_size = static_vertical_curb_size
34+
self.static_vertical_curb_xy_coordinates = static_vertical_curb_xy_coordinates
35+
self.add_static_horizontal_curb_as_obstacle = add_static_horizontal_curb_as_obstacle
36+
37+
self.vehicle_pose = vehicle_pose
38+
self.x_axis_of_search = self.vehicle_pose[0]
39+
2840

2941
self.vehicle_dims = vehicle_dims
3042
self.compact_parking_spot_size = compact_parking_spot_size # US Compact Space for parking (2.44, 4.88)
3143
self.shift_from_center_to_rear_axis = shift_from_center_to_rear_axis # TODO: Check
3244
self.search_step_size = search_step_size
45+
self.parking_lot_axis_shift_margin = parking_lot_axis_shift_margin
3346
# TODO: Add thrid option: park in the middle
3447
self.closest = closest # If True, the closest parking spot will be selected, otherwise the farthest one will be selected
3548

49+
50+
51+
3652

3753

3854
def reeds_shepp_path(start_pose, final_pose, step_size=0.1, rho=3.657):# 3.657
@@ -231,37 +247,244 @@ def available_parking_spots(all_parking_spots_in_parking_lot, parked_cars, compa
231247
available_parking_spots.append(spot)
232248

233249
return available_parking_spots
250+
251+
def yaw_of_parked_cars(curb_0, curb_1):
252+
# Compute the vector v from p1 to p2
253+
# v_x = x2 - x1, v_y = y2 - y1
254+
v = (curb_1[0] - curb_0[0], curb_1[1] - curb_0[1])
255+
angle_rad = math.atan2(v[1], v[0]) # TODO: Double check if CCW is the positive direction
256+
return angle_rad
257+
258+
def shift_points_perpendicular_ccw(p1, p2, shift_amount):
259+
"""
260+
Shift points p1 and p2 by a given amount perpendicular (to the left)
261+
of the vector from p1 to p2.
262+
263+
Args:
264+
p1 (tuple of float): First point (x1, y1)
265+
p2 (tuple of float): Second point (x2, y2)
266+
shift_amount (float): Distance to shift perpendicular to the left of vector v
267+
268+
Returns:
269+
p1_shifted (tuple of float): Shifted first point
270+
p2_shifted (tuple of float): Shifted second point
271+
dir_unit (tuple of float): Normalized direction vector v̂ = (v_x, v_y) / |v|
272+
shift_vec (tuple of float): Actual shift vector applied = perp_unit * shift_amount
273+
"""
274+
x1, y1 = p1
275+
x2, y2 = p2
276+
277+
# 1) Compute connecting vector v = p2 - p1
278+
v_x = x2 - x1
279+
v_y = y2 - y1
280+
281+
# 2) Compute its magnitude |v|
282+
length = math.hypot(v_x, v_y)
283+
if length == 0:
284+
raise ValueError("p1 and p2 must be distinct points to define a direction.")
285+
286+
# 3) Normalize v to get unit direction v̂ = (v_x, v_y) / |v|
287+
dir_x = v_x / length
288+
dir_y = v_y / length
289+
290+
v_norm = (dir_x, dir_y)
291+
292+
# 4) Compute the left-perpendicular unit vector: perp = (-dir_y, dir_x)
293+
perp_x = -dir_y
294+
perp_y = dir_x
295+
296+
# 5) Scale this perpendicular by the desired shift_amount
297+
shift_x = perp_x * shift_amount
298+
shift_y = perp_y * shift_amount
299+
300+
# 6) Apply shift to both points
301+
p1_shifted = (x1 + shift_x, y1 + shift_y)
302+
p2_shifted = (x2 + shift_x, y2 + shift_y)
303+
304+
# Return shifted points
305+
return p1_shifted, p2_shifted, v_norm
306+
307+
308+
309+
def project_point_on_axis(p1, p2, p3):
310+
"""
311+
Project point p3 orthogonally onto the line (axis) defined by p1 -> p2.
312+
313+
Args:
314+
p1 (tuple of float): First point on the axis (x1, y1)
315+
p2 (tuple of float): Second point on the axis (x2, y2)
316+
p3 (tuple of float): The point to be projected (x3, y3)
317+
318+
Returns:
319+
p_proj (tuple of float): Coordinates of the projection of p3 onto the line p1–p2
320+
t (float): The parameter along the line (0 at p1, 1 at p2, can be outside [0,1])
321+
"""
322+
x1, y1 = p1
323+
x2, y2 = p2
324+
x3, y3 = p3
325+
326+
# Compute vector along the axis v = p2 - p1
327+
v_x = x2 - x1
328+
v_y = y2 - y1
329+
330+
# Compute vector from p1 to p3: u = p3 - p1
331+
u_x = x3 - x1
332+
u_y = y3 - y1
333+
334+
# Compute dot products
335+
dot_uv = u_x * v_x + u_y * v_y # u · v
336+
dot_vv = v_x * v_x + v_y * v_y # v · v
337+
338+
if dot_vv == 0:
339+
raise ValueError("p1 and p2 must be distinct to define an axis.")
340+
341+
# Parameter t gives the position along the line: p_proj = p1 + t * v
342+
t = dot_uv / dot_vv
343+
344+
# Compute projected point coordinates
345+
proj_x = x1 + t * v_x
346+
proj_y = y1 + t * v_y
347+
348+
return (proj_x, proj_y)
349+
350+
351+
352+
def move_point_along_vector(p0, direction, step=0.1, positive_direction=True):
353+
"""
354+
Move the point p0 by a fixed step along the given direction vector.
355+
356+
Args:
357+
p0 (tuple of float): The starting point (x0, y0).
358+
direction (tuple of float): The direction vector (dx, dy).
359+
step (float): Distance to move along the direction (default 0.1).
360+
361+
Returns:
362+
tuple of float: The new point (x_new, y_new) after moving.
363+
Raises:
364+
ValueError: if the direction vector has zero length.
365+
"""
366+
x0, y0 = p0
367+
dx, dy = direction
368+
369+
# Compute the length of the direction vector
370+
length = math.hypot(dx, dy)
371+
if length == 0:
372+
raise ValueError("Direction vector must be non-zero to define a movement direction.")
373+
374+
# Normalize the direction vector to unit length
375+
ux = dx / length
376+
uy = dy / length
377+
378+
# Move the point by 'step' along the unit direction
379+
if positive_direction:
380+
x_new = x0 + ux * step
381+
y_new = y0 + uy * step
382+
else:
383+
x_new = x0 - ux * step
384+
y_new = y0 - uy * step
385+
386+
return (x_new, y_new)
387+
388+
389+
390+
391+
392+
393+
394+
395+
396+
def find_collision_free_trajectory(self, detected_cones = [], vehicle_pose = (0.0, 0.0, 0.0), update_pose = False):
234397

235-
def find_collision_free_trajectory(self, parked_cars = []):
398+
# Update detected cones
399+
self.detected_cones = detected_cones
400+
401+
# Update vehicle pose
402+
if update_pose:
403+
self.vehicle_pose = vehicle_pose
404+
405+
# Compute the angel between the vector v (connecting start horizontal curb_0 to end horizontal curb_1)
406+
# and the x-axis. This value is used to compute the orientation of static horizontal curbs,
407+
# static vertical curb and the yaw of parked cars (we set this since cones do not have orientaion).
408+
self.yaw_of_parked_cars = ReedsSheppParking.yaw_of_parked_cars(self.static_horizontal_curb_xy_coordinates[0], self.static_horizontal_curb_xy_coordinates[1])
409+
self.static_horizontal_curb = [
410+
(self.static_horizontal_curb_xy_coordinates[0][0], self.static_horizontal_curb_xy_coordinates[0][1], self.yaw_of_parked_cars, self.static_horizontal_curb_size),
411+
(self.static_horizontal_curb_xy_coordinates[1][0], self.static_horizontal_curb_xy_coordinates[1][1], self.yaw_of_parked_cars, self.static_horizontal_curb_size)
412+
]
413+
self.static_vertical_curb = [
414+
(self.static_vertical_curb_xy_coordinates[0][0], self.static_vertical_curb_xy_coordinates[0][1], self.yaw_of_parked_cars, self.static_vertical_curb_size),
415+
]
416+
417+
# Adding obstacles to the list of objects to avoid collisions
418+
if self.detected_cones != []:
419+
for cone in self.detected_cones:
420+
self.parked_cars.append((cone[0], cone[1], self.yaw_of_parked_cars, self.vehicle_dims))
421+
422+
else:
423+
self.parked_cars = self.detected_cones
236424

237-
self.parked_cars = parked_cars
238-
self.objects_to_avoid_collisions = self.static_horizontal_curb + self.parked_cars + self.static_vertical_curb
425+
# Adding all the parked cars and static curbs to the list of objects to avoid collisions
426+
self.objects_to_avoid_collisions += self.parked_cars
427+
if self.add_static_horizontal_curb_as_obstacle:
428+
self.objects_to_avoid_collisions += self.static_horizontal_curb
429+
if self.add_static_vertical_curb_as_obstacle:
430+
self.objects_to_avoid_collisions += self.static_vertical_curb
431+
432+
# Compute coordinates of parking spots in the parking lot defined by the static horizontal curb
239433
self.all_parking_spots_in_parking_lot = ReedsSheppParking.all_parking_spots_in_parking_lot(
240-
self.static_horizontal_curb, self.compact_parking_spot_size, yaw_of_parked_cars=0.0,
434+
self.static_horizontal_curb, self.compact_parking_spot_size, yaw_of_parked_cars = self.yaw_of_parked_cars,
241435
shift_from_center_to_rear_axis=self.shift_from_center_to_rear_axis
242436
)
437+
438+
# Compute the available parking spots in the parking lot
243439
self.available_parking_spots = ReedsSheppParking.available_parking_spots(
244440
self.all_parking_spots_in_parking_lot, self.parked_cars, self.compact_parking_spot_size,
245441
yaw_of_parked_cars=0.0, shift_from_center_to_rear_axis=self.shift_from_center_to_rear_axis
246442
)
443+
444+
445+
247446
self.parking_spot_to_go = [ReedsSheppParking.pick_parking_spot(self.available_parking_spots, 0.0, 0.0, yaw=0.0, closest=self.closest)]
248447
x_shift = self.parking_spot_to_go[0][0] - self.shift_from_center_to_rear_axis
249448
self.parking_spot_to_go[0] = (x_shift, self.parking_spot_to_go[0][1], self.parking_spot_to_go[0][2])
250-
self.x_axis_of_search_direction_positive = ReedsSheppParking.search_axis_direction(self.parking_spot_to_go, self.initial_pose_of_vehicle)
449+
self.x_axis_of_search_direction_positive = ReedsSheppParking.search_axis_direction(self.parking_spot_to_go, self.vehicle_pose)
450+
451+
452+
453+
# Compute axis of search
454+
curb_0_xy_shifted, curb_1_xy_shifted, new_axis_direction = ReedsSheppParking.shift_points_perpendicular_ccw(self.static_horizontal_curb_xy_coordinates[0],
455+
self.static_horizontal_curb_xy_coordinates[1],
456+
self.parking_lot_axis_shift_margin)
457+
458+
# Compute the projected point on the axis of search
459+
vehicle_pose_proj = ReedsSheppParking.project_point_on_axis(curb_0_xy_shifted, curb_1_xy_shifted, self.vehicle_pose[0:2])
460+
251461

252462
while True:
253-
waypoints = ReedsSheppParking.reeds_shepp_path((0.0, 0.0, 0.0), (self.x_axis_of_search, 0.0, 0.0))
254-
waypoints2 = ReedsSheppParking.reeds_shepp_path((self.x_axis_of_search, 0.0, 0.0), self.parking_spot_to_go[0])
463+
464+
vehicle_pose_proj = ReedsSheppParking.move_point_along_vector(vehicle_pose_proj, new_axis_direction,
465+
step = self.search_step_size,
466+
positive_direction = self.x_axis_of_search_direction_positive)
467+
468+
waypoints = ReedsSheppParking.reeds_shepp_path(self.vehicle_pose , (vehicle_pose_proj[0], vehicle_pose_proj[1], self.yaw_of_parked_cars))
469+
waypoints2 = ReedsSheppParking.reeds_shepp_path((vehicle_pose_proj[0], vehicle_pose_proj[1], self.yaw_of_parked_cars), self.parking_spot_to_go[0])
470+
255471
self.waypoints_for_obstacles_check = waypoints + waypoints2
256472
self.waypoints_to_go = np.array(self.waypoints_for_obstacles_check)[:, :2]
257-
if ReedsSheppParking.is_trajectory_collision_free(self.waypoints_for_obstacles_check, self.vehicle_dims,
258-
self.objects_to_avoid_collisions):
473+
474+
# Exit if the trajectory is collision free
475+
if ReedsSheppParking.is_trajectory_collision_free(self.waypoints_for_obstacles_check,
476+
self.vehicle_dims,
477+
self.objects_to_avoid_collisions):
259478
break
260-
if self.x_axis_of_search_direction_positive:
261-
self.x_axis_of_search += self.search_step_size
262-
if self.x_axis_of_search > self.static_horizontal_curb[1][0] + self.compact_parking_spot_size[1]:
263-
raise ValueError("No parking spot available.")
264-
else:
265-
self.x_axis_of_search -= self.search_step_size
266-
if self.x_axis_of_search < self.static_horizontal_curb[0][0] - self.compact_parking_spot_size[1]:
267-
raise ValueError("No parking spot available.")
479+
480+
481+
482+
483+
# if self.x_axis_of_search_direction_positive:
484+
# self.x_axis_of_search += self.search_step_size
485+
# if self.x_axis_of_search > self.static_horizontal_curb[1][0] + self.compact_parking_spot_size[1]:
486+
# raise ValueError("No parking spot available.")
487+
# else:
488+
# self.x_axis_of_search -= self.search_step_size
489+
# if self.x_axis_of_search < self.static_horizontal_curb[0][0] - self.compact_parking_spot_size[1]:
490+
# raise ValueError("No parking spot available.")

0 commit comments

Comments
 (0)