Skip to content

Commit d4d45e7

Browse files
author
Karan
committed
final working version TODO: performance improvements
1 parent 3f0d2b3 commit d4d45e7

File tree

1 file changed

+88
-88
lines changed

1 file changed

+88
-88
lines changed

PathPlanning/BatchInformedRRTStar/batch_informed_rrtstar.py

Lines changed: 88 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
"""
2-
Batch Informed Trees based path planning
2+
Batch Informed Trees based path planning:
3+
Uses a heuristic to efficiently search increasingly dense
4+
RGGs while reusing previous information. Provides faster
5+
convergence that RRT*, Informed RRT* and other sampling based
6+
methods.
7+
8+
Uses lazy connecting by combining sampling based methods and A*
9+
like incremental graph search algorithms.
310
411
author: Karan Chawla(@karanchawla)
512
@@ -15,6 +22,9 @@
1522

1623
show_animation = True
1724

25+
# Class to represent the explicit tree created
26+
# while sampling through the state space
27+
1828

1929
class RTree(object):
2030

@@ -113,28 +123,19 @@ def nodeIdToRealWorldCoord(self, nid):
113123
# in the full configuration space
114124
return self.gridCoordToRealWorldCoord(self.nodeIdToGridCoord(nid))
115125

116-
117-
class Node():
118-
119-
def __init__(self, x, y):
120-
self.x = x
121-
self.y = y
122-
self.cost = 0.0
123-
self.parent = None
126+
# Uses Batch Informed Trees to find a path from start to goal
124127

125128

126129
class BITStar(object):
127130

128131
def __init__(self, start, goal,
129132
obstacleList, randArea, eta=2.0,
130-
expandDis=0.5, goalSampleRate=10, maxIter=200):
133+
maxIter=80):
131134
self.start = start
132135
self.goal = goal
133136

134137
self.minrand = randArea[0]
135138
self.maxrand = randArea[1]
136-
self.expandDis = expandDis
137-
self.goalSampleRate = goalSampleRate
138139
self.maxIter = maxIter
139140
self.obstacleList = obstacleList
140141

@@ -153,14 +154,12 @@ def __init__(self, start, goal,
153154
lowerLimit = [randArea[0], randArea[0]]
154155
upperLimit = [randArea[1], randArea[1]]
155156
self.tree = RTree(start=start, lowerLimit=lowerLimit,
156-
upperLimit=upperLimit, resolution=0.1)
157+
upperLimit=upperLimit, resolution=0.01)
157158

158159
def plan(self, animation=True):
159160

160161
self.startId = self.tree.realWorldToNodeId(self.start)
161-
print("startId: ", self.startId)
162162
self.goalId = self.tree.realWorldToNodeId(self.goal)
163-
print("goalId: ", self.goalId)
164163

165164
# add goal to the samples
166165
self.samples[self.goalId] = self.goal
@@ -182,7 +181,7 @@ def plan(self, animation=True):
182181

183182
# Computing the sampling space
184183
cMin = math.sqrt(pow(self.start[0] - self.goal[1], 2) +
185-
pow(self.start[0] - self.goal[1], 2))
184+
pow(self.start[0] - self.goal[1], 2)) / 1.5
186185
xCenter = np.matrix([[(self.start[0] + self.goal[0]) / 2.0],
187186
[(self.goal[1] - self.start[1]) / 2.0], [0]])
188187
a1 = np.matrix([[(self.goal[0] - self.start[0]) / cMin],
@@ -201,14 +200,21 @@ def plan(self, animation=True):
201200
# run until done
202201
while (iterations < self.maxIter):
203202
if len(self.vertex_queue) == 0 and len(self.edge_queue) == 0:
203+
print("Batch: ", iterations)
204204
# Using informed rrt star way of computing the samples
205-
self.samples.update(self.informedSample(
206-
50, cBest, cMin, xCenter, C))
207-
# prune the tree
208205
self.r = 2.0
209206
if iterations != 0:
207+
if foundGoal:
208+
# a better way to do this would be to make number of samples
209+
# a function of cMin
210+
m = 200
211+
self.samples = dict()
212+
self.samples[self.goalId] = self.goal
213+
else:
214+
m = 100
215+
cBest = self.g_scores[self.goalId]
210216
self.samples.update(self.informedSample(
211-
200, cBest, cMin, xCenter, C))
217+
m, cBest, cMin, xCenter, C))
212218

213219
# make the old vertices the new vertices
214220
self.old_vertices += self.tree.vertices.keys()
@@ -231,7 +237,7 @@ def plan(self, animation=True):
231237
bestEdge[0], bestEdge[1]) + self.computeHeuristicCost(bestEdge[1], self.goalId)
232238
estimatedCostOfEdge = self.computeDistanceCost(self.startId, bestEdge[0]) + self.computeHeuristicCost(
233239
bestEdge[0], bestEdge[1]) + self.computeHeuristicCost(bestEdge[1], self.goalId)
234-
actualCostOfEdge = self.g_scores[bestEdge[0]] + + \
240+
actualCostOfEdge = self.g_scores[bestEdge[0]] + \
235241
self.computeDistanceCost(bestEdge[0], bestEdge[1])
236242

237243
if(estimatedCostOfVertex < self.g_scores[self.goalId]):
@@ -243,18 +249,22 @@ def plan(self, animation=True):
243249
secondCoord = self.tree.nodeIdToRealWorldCoord(
244250
bestEdge[1])
245251
path = self.connect(firstCoord, secondCoord)
252+
lastEdge = self.tree.realWorldToNodeId(secondCoord)
246253
if path is None or len(path) == 0:
247254
continue
248255
nextCoord = path[len(path) - 1, :]
249256
nextCoordPathId = self.tree.realWorldToNodeId(
250257
nextCoord)
251258
bestEdge = (bestEdge[0], nextCoordPathId)
252-
try:
253-
del self.samples[bestEdge[1]]
254-
except(KeyError):
255-
pass
256-
eid = self.tree.addVertex(nextCoord)
257-
self.vertex_queue.append(eid)
259+
if(bestEdge[1] in self.tree.vertices.keys()):
260+
continue
261+
else:
262+
try:
263+
del self.samples[bestEdge[1]]
264+
except(KeyError):
265+
pass
266+
eid = self.tree.addVertex(nextCoord)
267+
self.vertex_queue.append(eid)
258268
if eid == self.goalId or bestEdge[0] == self.goalId or bestEdge[1] == self.goalId:
259269
print("Goal found")
260270
foundGoal = True
@@ -273,7 +283,7 @@ def plan(self, animation=True):
273283
if animation:
274284
self.drawGraph(xCenter=xCenter, cBest=cBest,
275285
cMin=cMin, etheta=etheta, samples=self.samples.values(),
276-
start=firstCoord, end=secondCoord, tree=self.nodes)
286+
start=firstCoord, end=secondCoord, tree=self.tree.edges)
277287

278288
for edge in self.edge_queue:
279289
if(edge[0] == bestEdge[1]):
@@ -283,29 +293,31 @@ def plan(self, animation=True):
283293
(edge[0], bestEdge[1]))
284294
if(edge[1] == bestEdge[1]):
285295
if self.g_scores[edge[1]] + self.computeDistanceCost(edge[1], bestEdge[1]) >= self.g_scores[self.goalId]:
286-
if(edge[1], bestEdge[1]) in self.edge_queue:
296+
if(lastEdge, bestEdge[1]) in self.edge_queue:
287297
self.edge_queue.remove(
288-
(edge[1], bestEdge[1]))
298+
(lastEdge, bestEdge[1]))
289299
else:
300+
print("Nothing good")
290301
self.edge_queue = []
291302
self.vertex_queue = []
292303
iterations += 1
293304

305+
print("Finding the path")
294306
plan.append(self.goal)
295307
currId = self.goalId
296308
while (currId != self.startId):
297309
plan.append(self.tree.nodeIdToRealWorldCoord(currId))
298310
currId = self.nodes[currId]
299311

300-
plan.append(self.startId)
312+
plan.append(self.start)
301313
plan = plan[::-1] # reverse the plan
302-
return np.array(plan)
314+
return plan
303315

304316
def connect(self, start, end):
305317
# A function which attempts to extend from a start coordinates
306318
# to goal coordinates
307319
steps = self.computeDistanceCost(self.tree.realWorldToNodeId(
308-
start), self.tree.realWorldToNodeId(end)) * 25
320+
start), self.tree.realWorldToNodeId(end)) * 10
309321
x = np.linspace(start[0], end[0], num=steps)
310322
y = np.linspace(start[1], end[1], num=steps)
311323
for i in range(len(x)):
@@ -314,14 +326,15 @@ def connect(self, start, end):
314326
return None
315327
# if collision, send path until collision
316328
return np.vstack((x[0:i], y[0:i])).transpose()
317-
return np.vstack((x, y)).transpose()
329+
330+
return np.vstack((x, y)).transpose()
318331

319332
def _collisionCheck(self, x, y):
320333
for (ox, oy, size) in self.obstacleList:
321334
dx = ox - x
322335
dy = oy - y
323336
d = dx * dx + dy * dy
324-
if d <= 1.1 * size ** 2:
337+
if d <= size ** 2:
325338
return True # collision
326339
return False
327340

@@ -341,20 +354,10 @@ def computeDistanceCost(self, vid, xid):
341354

342355
return np.linalg.norm(stop - start, 2)
343356

344-
def radius(self, q):
345-
dim = len(start) # dimensions
346-
space_measure = self.minrand * self.maxrand # volume of the space
347-
348-
min_radius = self.eta * 2.0 * pow((1.0 + 1.0 / dim) *
349-
(space_measure / self.unit_ball_measure), 1.0 / dim)
350-
return min_radius * pow(numpy.log(q) / q, 1 / dim)
351-
352-
# Return the closest sample
353-
# def getNearestSample(self):
354-
355357
# Sample free space confined in the radius of ball R
356358
def informedSample(self, m, cMax, cMin, xCenter, C):
357359
samples = dict()
360+
print("g_Score goal id: ", self.g_scores[self.goalId])
358361
for i in range(m + 1):
359362
if cMax < float('inf'):
360363
r = [cMax / 2.0,
@@ -436,53 +439,51 @@ def expandVertex(self, vid):
436439
if(np.linalg.norm(scoord - currCoord, 2) <= self.r and sid != vid):
437440
neigbors.append((sid, scoord))
438441

439-
# add the vertex to the edge queue
440-
if vid not in self.old_vertices:
441-
neigbors = []
442-
for v, edges in self.tree.vertices.items():
443-
if v != vid and (v, vid) not in self.edge_queue:
444-
vcoord = self.tree.nodeIdToRealWorldCoord(v)
445-
if(np.linalg.norm(vcoord - currCoord, 2) <= self.r):
446-
neigbors.append((vid, vcoord))
447-
448442
# add an edge to the edge queue is the path might improve the solution
449443
for neighbor in neigbors:
450444
sid = neighbor[0]
445+
scoord = neighbor[1]
451446
estimated_f_score = self.computeDistanceCost(
452447
self.startId, vid) + self.computeHeuristicCost(sid, self.goalId) + self.computeDistanceCost(vid, sid)
453448
if estimated_f_score < self.g_scores[self.goalId]:
454449
self.edge_queue.append((vid, sid))
455450

451+
# add the vertex to the edge queue
452+
if vid not in self.old_vertices:
453+
neigbors = []
454+
for v, edges in self.tree.vertices.items():
455+
if v != vid and (v, vid) not in self.edge_queue and (vid, v) not in self.edge_queue:
456+
vcoord = self.tree.nodeIdToRealWorldCoord(v)
457+
if(np.linalg.norm(vcoord - currCoord, 2) <= self.r and v != vid):
458+
neigbors.append((vid, vcoord))
459+
460+
for neighbor in neigbors:
461+
sid = neighbor[0]
462+
scoord = neighbor[1]
463+
estimated_f_score = self.computeDistanceCost(self.startId, vid) + \
464+
self.computeDistanceCost(
465+
vid, sid) + self.computeHeuristicCost(sid, self.goalId)
466+
if estimated_f_score < self.g_scores[self.goalId] and (self.g_scores[vid] + self.computeDistanceCost(vid, sid)) < self.g_scores[sid]:
467+
self.edge_queue.append((vid, sid))
468+
456469
def updateGraph(self):
457470
closedSet = []
458471
openSet = []
459472
currId = self.startId
460473
openSet.append(currId)
461474

462-
# do some plotting
463-
464475
foundGoal = False
465476

466477
while len(openSet) != 0:
467478
# get the element with lowest f_score
468-
minn = float('inf')
469-
min_node = None
470-
min_idx = 0
471-
for i in range(0, len(openSet)):
472-
try:
473-
f_score = self.f_scores[openSet[i]]
474-
except:
475-
pass
476-
if f_score < minn:
477-
minn = f_score
478-
min_node = openSet[i]
479-
min_idx = i
480-
currId = min_node
481-
482-
openSet.pop(min_idx)
479+
currId = min(openSet, key=lambda x: self.f_scores[x])
480+
481+
# remove element from open set
482+
openSet.remove(currId)
483483

484484
# Check if we're at the goal
485485
if(currId == self.goalId):
486+
self.nodes[self.goalId]
486487
foundGoal = True
487488
break
488489

@@ -507,7 +508,7 @@ def updateGraph(self):
507508

508509
# update g and f scores
509510
self.g_scores[succesor] = g_score
510-
self.f_scores[succesor] = f_score + \
511+
self.f_scores[succesor] = g_score + \
511512
self.computeHeuristicCost(succesor, self.goalId)
512513

513514
# store the parent and child
@@ -526,12 +527,6 @@ def drawGraph(self, xCenter=None, cBest=None, cMin=None, etheta=None,
526527
if start is not None and end is not None:
527528
plt.plot([start[0], start[1]], [end[0], end[1]], "-g")
528529

529-
if tree is not None and len(tree) != 0:
530-
for key, value in tree.items():
531-
keyCoord = self.tree.nodeIdToRealWorldCoord(key)
532-
valueCoord = self.tree.nodeIdToRealWorldCoord(value)
533-
plt.plot(keyCoord, valueCoord, "-r")
534-
535530
for (ox, oy, size) in self.obstacleList:
536531
plt.plot(ox, oy, "ok", ms=30 * size)
537532

@@ -564,18 +559,23 @@ def plot_ellipse(self, xCenter, cBest, cMin, etheta):
564559
def main():
565560
print("Starting Batch Informed Trees Star planning")
566561
obstacleList = [
567-
# (5, 5, 0.5),
568-
# (9, 6, 1),
569-
# (7, 5, 1),
570-
# (1, 5, 1),
571-
# (3, 6, 1),
572-
# (7, 9, 1)
562+
(5, 5, 0.5),
563+
(9, 6, 1),
564+
(7, 5, 1),
565+
(1, 5, 1),
566+
(3, 6, 1),
567+
(7, 9, 1)
573568
]
574569

575-
bitStar = BITStar(start=[0, 0], goal=[2, 4], obstacleList=obstacleList,
576-
randArea=[0, 15])
570+
bitStar = BITStar(start=[-1, 0], goal=[3, 8], obstacleList=obstacleList,
571+
randArea=[-2, 15])
577572
path = bitStar.plan(animation=show_animation)
578-
print("Done")
573+
print(path)
574+
if show_animation:
575+
plt.plot([x for (x, y) in path], [y for (x, y) in path], '-r')
576+
plt.grid(True)
577+
plt.pause(0.05)
578+
plt.show()
579579

580580

581581
if __name__ == '__main__':

0 commit comments

Comments
 (0)