44import random
55import math
66
7- DEBUG = False
8-
97class Obstacle :
108 def __init__ (self ,x = 0 ,y = 0 ,r = 0.2 ):
119 self .x = x
@@ -20,52 +18,100 @@ def __init__(self, x=0, y=0, heading = None):
2018 self .parent = None
2119 self .cost = float ('inf' ) # Cost to reach this node
2220
21+ OFFSET = 0.8 # meter
22+
23+ OBSTACLE_LIST = []
24+
25+ # return euclidean distance between two point/obstacle
26+ def distance (a ,b ):
27+ return math .sqrt (math .pow (a .x - b .x ,2 ) + math .pow (a .y - b .y ,2 ))
28+
29+ # return angel within -pi to pi
30+ def angle_norm (angle ):
31+ return (angle + math .pi ) % (2 * math .pi ) - math .pi
32+
33+ # return absolute value of angle difference within 0 to pi
34+ def angle_diff (a ,b ):
35+ a = angle_norm (a )
36+ b = angle_norm (b )
37+ diff = a - b
38+ return abs (angle_norm (diff ))
39+
40+ # return the angle in oppsite direction
41+ def angle_inverse (angle ):
42+ angle = angle_norm (angle )
43+ if angle < 0 :
44+ return angle + math .pi
45+ return angle - math .pi
46+
47+ def is_valid (point ):
48+ for obstacle in OBSTACLE_LIST :
49+ if distance (point , obstacle ) < obstacle .radius + OFFSET :
50+ return False
51+ return True
52+
53+ # return the nearest point in the tree
54+ def Nearest (tree ,sample_p ):
55+ min = 10000000
56+ nearest_p = None
57+ for point in tree :
58+ d = distance (point ,sample_p )
59+ if d < min :
60+ min = d
61+ nearest_p = point
62+ return nearest_p
63+
64+ # calculate the point heading based on parent point
65+ def heading (parent ,p2 ):
66+ delta = np .array ([p2 .x ,p2 .y ]) - np .array ([parent .x ,parent .y ])
67+ return math .atan2 (delta [1 ],delta [0 ])
68+
69+ # create a point that is one step size away from nearest point toward the sample point
70+ def LocalPlanner (nearest_p ,sample_p , step_size = 0.5 ):
71+ dist = distance (nearest_p ,sample_p )
72+ if dist < step_size :
73+ return sample_p
74+ direction = np .array ([sample_p .x ,sample_p .y ]) - np .array ([nearest_p .x ,nearest_p .y ])
75+ delta = (direction / dist ) * step_size
76+ new_p = Point ()
77+ new_p .x = nearest_p .x + delta [0 ]
78+ new_p .y = nearest_p .y + delta [1 ]
79+ new_p .parent = nearest_p
80+ new_p .cost = dist
81+ return new_p
82+
2383class BiRRT :
24- def __init__ (self , start : list , goal : list , obstacles : list , MAP_SIZE : list ):
84+ def __init__ (self , start : list , goal : list , obstacles : list , map : list ):
2585
2686 self .path = []
2787 self .tree_from_start = []
2888 self .tree_from_end = []
2989
30- # start position (current vehicle position)
3190 self .start_point = Point (x = start [0 ],y = start [1 ],heading = start [2 ])
91+ self .end_point = Point (x = goal [0 ],y = goal [1 ],heading = goal [2 ])
3292
33- # desire position (reverse heading for building tree)
34- # (will revert back after route found)
35- self .end_point = Point (x = goal [0 ],y = goal [1 ],heading = self .angle_inverse (goal [2 ]))
36-
37- # obstacles and map boundary
38- self .OBSTACLE_LIST = []
93+ OBSTACLE_LIST = []
3994 for i in range (len (obstacles )):
40- self .OBSTACLE_LIST .append (Obstacle (obstacles [i ][0 ], obstacles [i ][1 ], r = 0.2 ))
41-
42- # min distace of vehicle center to obstacle
43- # should be roughly 1/2 of vehicle width
44- self .OFFSET = 0.8 # meter
95+ OBSTACLE_LIST .append (Obstacle (obstacles [i ][0 ], obstacles [i ][1 ], r = 0.2 ))
4596
46- # max iteration size for performing route search
4797 self .MAX_Iteration = 20000
48-
49- # step size for local planner
5098 self .step_size = 0.5 # meter
51-
52- # radius for determine neighbor node
5399 self .search_r = 1.3 # meter
54-
55- # angle limit for vehicle turning per step size
56100 self .heading_limit = math .pi / 6 # limit the heading change in route
101+ self .goal_sample_rate = 0.1 # rate to check area near end-goal
57102
58103 # Map boundary
59- self .MAP_X_LOW = MAP_SIZE [0 ] # meter
60- self .MAP_X_HIGH = MAP_SIZE [1 ] # meter
61- self .MAP_Y_LOW = MAP_SIZE [2 ] # meter
62- self .MAP_Y_HIGH = MAP_SIZE [3 ] # meter
104+ self .MAP_X_LOW = map [0 ] # meter
105+ self .MAP_X_HIGH = map [1 ] # meter
106+ self .MAP_Y_LOW = map [2 ] # meter
107+ self .MAP_Y_HIGH = map [3 ] # meter
63108
64109 def search (self ):
65110 # initialize two tree
66111 self .tree_from_start .append (self .start_point )
67112 self .tree_from_end .append (self .end_point )
68113
114+ # self.start_time = time.time()
69115 # perform search within max number of iterration
70116 for iterration in range (self .MAX_Iteration ):
71117 # uniformly sample a point within in the map
@@ -81,34 +127,33 @@ def search(self):
81127 tree_a = self .tree_from_end
82128 tree_b = self .tree_from_start
83129 Direction = "backward"
84-
85- if DEBUG :
86- print (Direction + " AT {} Interation" .format (iterration ))
130+
131+ print (Direction + " AT {} Interation" .format (iterration ))
87132 # find nearest point in the tree
88- nearest_point_a = self . Nearest (tree_a , sample_p )
133+ nearest_point_a = Nearest (tree_a , sample_p )
89134 # use local planner to move one step size
90- new_p = self . LocalPlanner (nearest_point_a , sample_p , self .step_size )
135+ new_p = LocalPlanner (nearest_point_a , sample_p , self .step_size )
91136 # check collision
92- if not self . is_valid (new_p ):
137+ if not is_valid (new_p ):
93138 continue
94139 # check if there exist previous point with less cost to new point
95140 neighbor_points = self .Neighbors (new_p ,tree_a )
96- min_cost = nearest_point_a .cost + self . distance (new_p ,nearest_point_a )
141+ min_cost = nearest_point_a .cost + distance (new_p ,nearest_point_a )
97142 parent_p = nearest_point_a
98143 for point in neighbor_points :
99- curr_cost = point .cost + self . distance (point , new_p )
144+ curr_cost = point .cost + distance (point , new_p )
100145 if curr_cost < min_cost :
101146 min_cost = curr_cost
102147 parent_p = point
103148 # update point's paraent
104149 new_p .cost = min_cost
105150 new_p .parent = parent_p
106- new_p .heading = self . heading (parent_p ,new_p )
151+ new_p .heading = heading (parent_p ,new_p )
107152
108153 # check heading limit and collision
109- if self . angle_diff (new_p .heading ,new_p .parent .heading ) > (self .heading_limit ):
154+ if angle_diff (new_p .heading ,new_p .parent .heading ) > (self .heading_limit ):
110155 continue
111- if not self . is_valid (new_p ):
156+ if not is_valid (new_p ):
112157 continue
113158
114159 # point is valid, add to tree
@@ -118,43 +163,43 @@ def search(self):
118163 for point in neighbor_points :
119164 if point == parent_p :
120165 continue
121- if new_p .cost + self . distance (new_p ,point ) < point .cost :
166+ if new_p .cost + distance (new_p ,point ) < point .cost :
122167 # check heading limit
123- if self . angle_diff (new_p .heading ,point .heading ) > (self .heading_limit ):
168+ if angle_diff (new_p .heading ,point .heading ) > (self .heading_limit ):
124169 continue
125- if self . angle_diff (new_p .heading ,self . heading (new_p ,point )) > (self .heading_limit ):
170+ if angle_diff (new_p .heading ,heading (new_p ,point )) > (self .heading_limit ):
126171 continue
127- if self . angle_diff (point .heading ,self . heading (new_p ,point )) > (self .heading_limit ):
172+ if angle_diff (point .heading ,heading (new_p ,point )) > (self .heading_limit ):
128173 continue
129174 point .parent = new_p
130- point .cost = new_p .cost + self . distance (new_p , point )
131- point .heading = self . heading (new_p ,point )
175+ point .cost = new_p .cost + distance (new_p , point )
176+ point .heading = heading (new_p ,point )
132177
133178 # find nearest point in another tree
134- nearest_point_b = self . Nearest (tree_b , new_p )
179+ nearest_point_b = Nearest (tree_b , new_p )
135180
136181 # check if two tree can be connected
137- if self . distance (new_p ,nearest_point_b ) > self .step_size :
182+ if distance (new_p ,nearest_point_b ) > self .step_size :
138183 continue
139184 # check heading limit
140- if self . angle_diff (new_p .heading ,self . angle_inverse (nearest_point_b .heading )) > (self .heading_limit ):
185+ if angle_diff (new_p .heading ,angle_inverse (nearest_point_b .heading )) > (self .heading_limit ):
141186 continue
142- if self . angle_diff (new_p .heading ,self . heading (new_p ,nearest_point_b )) > (self .heading_limit ):
187+ if angle_diff (new_p .heading ,heading (new_p ,nearest_point_b )) > (self .heading_limit ):
143188 continue
144- if self . angle_diff (nearest_point_b .heading ,self . heading (nearest_point_b ,new_p )) > (self .heading_limit ):
189+ if angle_diff (nearest_point_b .heading ,heading (nearest_point_b ,new_p )) > (self .heading_limit ):
145190 continue
146191
147192 # check if there exist another point that can connect two tree with less cost
148193 neighbor_points = self .Neighbors (new_p ,tree_b )
149- min_cost = new_p .cost + nearest_point_b .cost + self . distance (new_p ,nearest_point_a )
194+ min_cost = new_p .cost + nearest_point_b .cost + distance (new_p ,nearest_point_a )
150195 for point in neighbor_points :
151- curr_cost = new_p .cost + point .cost + self . distance (point , new_p )
196+ curr_cost = new_p .cost + point .cost + distance (point , new_p )
152197 if curr_cost < min_cost :
153- if self . angle_diff (new_p .heading ,self . angle_inverse (point .heading )) > (self .heading_limit ):
198+ if angle_diff (new_p .heading ,angle_inverse (point .heading )) > (self .heading_limit ):
154199 continue
155- if self . angle_diff (new_p .heading ,self . heading (new_p ,point )) > (self .heading_limit ):
200+ if angle_diff (new_p .heading ,heading (new_p ,point )) > (self .heading_limit ):
156201 continue
157- if self . angle_diff (point .heading ,self . heading (point ,new_p )) > (self .heading_limit ):
202+ if angle_diff (point .heading ,heading (point ,new_p )) > (self .heading_limit ):
158203 continue
159204 min_cost = curr_cost
160205 nearest_point_b = point
@@ -170,7 +215,7 @@ def search(self):
170215 def Neighbors (self ,sample_p ,tree ):
171216 neighbor_points = []
172217 for point in tree :
173- if self . distance (point , sample_p ) <= self .search_r :
218+ if distance (point , sample_p ) <= self .search_r :
174219 neighbor_points .append (point )
175220 return neighbor_points
176221
@@ -185,70 +230,11 @@ def trace_path(self,point_a,point_b):
185230 point_b = point_temp
186231
187232 while point_a is not None :
188- path_start .append ([ point_a . x , point_a . y , point_a . heading ] )
233+ path_start .append (point_a )
189234 point_a = point_a .parent
190235 while point_b is not None :
191- point_b .heading = self . angle_inverse (point_b .heading )
192- path_end .append ([ point_b . x , point_b . y , point_b . heading ] )
236+ point_b .heading = angle_inverse (point_b .heading )
237+ path_end .append (point_b )
193238 point_b = point_b .parent
194239
195240 self .path = path_start [::- 1 ] + path_end
196-
197- # return euclidean distance between two point/obstacle
198- def distance (self ,a ,b ):
199- return math .sqrt (math .pow (a .x - b .x ,2 ) + math .pow (a .y - b .y ,2 ))
200-
201- # return angel within -pi to pi
202- def angle_norm (self ,angle ):
203- return (angle + math .pi ) % (2 * math .pi ) - math .pi
204-
205- # return absolute value of angle difference within 0 to pi
206- def angle_diff (self ,a ,b ):
207- a = self .angle_norm (a )
208- b = self .angle_norm (b )
209- diff = a - b
210- return abs (self .angle_norm (diff ))
211-
212- # return the angle in oppsite direction
213- def angle_inverse (self ,angle ):
214- angle = self .angle_norm (angle )
215- if angle < 0 :
216- return angle + math .pi
217- return angle - math .pi
218-
219- # check if the point/move is collision free
220- def is_valid (self ,point ):
221- for obstacle in self .OBSTACLE_LIST :
222- if self .distance (point , obstacle ) < obstacle .radius + self .OFFSET :
223- return False
224- return True
225-
226- # return the nearest point in the tree
227- def Nearest (self ,tree ,sample_p ):
228- min = 10000000
229- nearest_p = None
230- for point in tree :
231- d = self .distance (point ,sample_p )
232- if d < min :
233- min = d
234- nearest_p = point
235- return nearest_p
236-
237- # calculate the point heading based on parent point
238- def heading (self ,parent ,p2 ):
239- delta = np .array ([p2 .x ,p2 .y ]) - np .array ([parent .x ,parent .y ])
240- return math .atan2 (delta [1 ],delta [0 ])
241-
242- # create a point that is one step size away from nearest point toward the sample point
243- def LocalPlanner (self ,nearest_p ,sample_p , step_size = 0.5 ):
244- dist = self .distance (nearest_p ,sample_p )
245- if dist < step_size :
246- return sample_p
247- direction = np .array ([sample_p .x ,sample_p .y ]) - np .array ([nearest_p .x ,nearest_p .y ])
248- delta = (direction / dist ) * step_size
249- new_p = Point ()
250- new_p .x = nearest_p .x + delta [0 ]
251- new_p .y = nearest_p .y + delta [1 ]
252- new_p .parent = nearest_p
253- new_p .cost = dist
254- return new_p
0 commit comments