66from typing import List , Tuple
77import math
88
9-
109Pose = Tuple [float , float , float ] # (x, y, yaw)
1110Dims = Tuple [float , float ] # (width, length)
1211Obstacle = 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