@@ -51,30 +51,93 @@ def get_target_point(path, targetL):
5151 return [x , y , ti ]
5252
5353
54- def line_collision_check (first , second , obstacleList ):
55- # Line Equation
56-
57- x1 = first [0 ]
58- y1 = first [1 ]
59- x2 = second [0 ]
60- y2 = second [1 ]
61-
62- try :
63- a = y2 - y1
64- b = - (x2 - x1 )
65- c = y2 * (x2 - x1 ) - x2 * (y2 - y1 )
66- except ZeroDivisionError :
67- return False
68-
69- for (ox , oy , size ) in obstacleList :
70- d = abs (a * ox + b * oy + c ) / (math .hypot (a , b ))
71- if d <= size :
72- return False
73-
74- return True # OK
75-
76-
77- def path_smoothing (path , max_iter , obstacle_list ):
54+ def is_point_collision (x , y , obstacle_list , robot_radius ):
55+ """
56+ Check whether a single point collides with any obstacle.
57+
58+ This function calculates the Euclidean distance between the given point (x, y)
59+ and each obstacle center. If the distance is less than or equal to the sum of
60+ the obstacle's radius and the robot's radius, a collision is detected.
61+
62+ Args:
63+ x (float): X-coordinate of the point to check.
64+ y (float): Y-coordinate of the point to check.
65+ obstacle_list (List[Tuple[float, float, float]]): List of obstacles defined as (ox, oy, radius).
66+ robot_radius (float): Radius of the robot, used to inflate the obstacles.
67+
68+ Returns:
69+ bool: True if the point is in collision with any obstacle, False otherwise.
70+ """
71+ for (ox , oy , obstacle_radius ) in obstacle_list :
72+ d = math .hypot (ox - x , oy - y )
73+ if d <= obstacle_radius + robot_radius :
74+ return True # Collided
75+ return False
76+
77+
78+ def line_collision_check (first , second , obstacle_list , robot_radius = 0.0 , sample_step = 0.2 ):
79+ """
80+ Check if the line segment between `first` and `second` collides with any obstacle.
81+ Considers the robot_radius by inflating the obstacle size.
82+
83+ Args:
84+ first (List[float]): Start point of the line [x, y]
85+ second (List[float]): End point of the line [x, y]
86+ obstacle_list (List[Tuple[float, float, float]]): Obstacles as (x, y, radius)
87+ robot_radius (float): Radius of robot
88+ sample_step (float): Distance between sampling points along the segment
89+
90+ Returns:
91+ bool: True if collision-free, False otherwise
92+ """
93+ x1 , y1 = first [0 ], first [1 ]
94+ x2 , y2 = second [0 ], second [1 ]
95+
96+ dx = x2 - x1
97+ dy = y2 - y1
98+ length = math .hypot (dx , dy )
99+
100+ if length == 0 :
101+ # Degenerate case: point collision check
102+ return not is_point_collision (x1 , y1 , obstacle_list , robot_radius )
103+
104+ steps = int (length / sample_step ) + 1 # Sampling every sample_step along the segment
105+
106+ for i in range (steps + 1 ):
107+ t = i / steps
108+ x = x1 + t * dx
109+ y = y1 + t * dy
110+
111+ if is_point_collision (x , y , obstacle_list , robot_radius ):
112+ return False # Collision found
113+
114+ return True # Safe
115+
116+
117+ def path_smoothing (path , max_iter , obstacle_list , robot_radius = 0.0 ):
118+ """
119+ Smooths a given path by iteratively replacing segments with shortcut connections,
120+ while ensuring the new segments are collision-free.
121+
122+ The algorithm randomly picks two points along the original path and attempts to
123+ connect them with a straight line. If the line does not collide with any obstacles
124+ (considering the robot's radius), the intermediate path points between them are
125+ replaced with the direct connection.
126+
127+ Args:
128+ path (List[List[float]]): The original path as a list of [x, y] coordinates.
129+ max_iter (int): Number of iterations for smoothing attempts.
130+ obstacle_list (List[Tuple[float, float, float]]): List of obstacles represented as
131+ (x, y, radius).
132+ robot_radius (float, optional): Radius of the robot, used to inflate obstacle size
133+ during collision checking. Defaults to 0.0.
134+
135+ Returns:
136+ List[List[float]]: The smoothed path as a list of [x, y] coordinates.
137+
138+ Example:
139+ >>> smoothed = path_smoothing(path, 1000, obstacle_list, robot_radius=0.5)
140+ """
78141 le = get_path_length (path )
79142
80143 for i in range (max_iter ):
@@ -94,7 +157,7 @@ def path_smoothing(path, max_iter, obstacle_list):
94157 continue
95158
96159 # collision check
97- if not line_collision_check (first , second , obstacle_list ):
160+ if not line_collision_check (first , second , obstacle_list , robot_radius ):
98161 continue
99162
100163 # Create New path
@@ -119,14 +182,16 @@ def main():
119182 (3 , 10 , 2 ),
120183 (7 , 5 , 2 ),
121184 (9 , 5 , 2 )
122- ] # [x,y,size ]
185+ ] # [x,y,radius ]
123186 rrt = RRT (start = [0 , 0 ], goal = [6 , 10 ],
124- rand_area = [- 2 , 15 ], obstacle_list = obstacleList )
187+ rand_area = [- 2 , 15 ], obstacle_list = obstacleList ,
188+ robot_radius = 0.3 )
125189 path = rrt .planning (animation = show_animation )
126190
127191 # Path smoothing
128192 maxIter = 1000
129- smoothedPath = path_smoothing (path , maxIter , obstacleList )
193+ smoothedPath = path_smoothing (path , maxIter , obstacleList ,
194+ robot_radius = rrt .robot_radius )
130195
131196 # Draw final path
132197 if show_animation :
0 commit comments