Skip to content

Commit 1d1287f

Browse files
authored
Merge pull request AtsushiSakai#66 from karanchawla/master
Adding Informed RRT Star Path Planner
2 parents 9bc4b3b + 2bafca3 commit 1d1287f

File tree

2 files changed

+289
-0
lines changed

2 files changed

+289
-0
lines changed
55 KB
Loading
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
'''
2+
Author: Karan Chawla
3+
7th June, '18
4+
Reference: https://arxiv.org/pdf/1404.2334.pdf
5+
'''
6+
import random
7+
import numpy as np
8+
import math
9+
import copy
10+
import matplotlib.pyplot as plt
11+
12+
show_animation = True
13+
14+
class InformedRRTStar():
15+
16+
def __init__(self, start, goal, obstacleList, randArea, expandDis=0.5, goalSampleRate=10, maxIter=200):
17+
18+
self.start = Node(start[0], start[1])
19+
self.goal = Node(goal[0], goal[1])
20+
self.minrand = randArea[0]
21+
self.maxrand = randArea[1]
22+
self.expandDis = expandDis
23+
self.goalSampleRate = goalSampleRate
24+
self.maxIter = maxIter
25+
self.obstacleList = obstacleList
26+
27+
def InformedRRTStarSearch(self, animation=True):
28+
29+
self.nodeList = [self.start]
30+
# max length we expect to find in our 'informed' sample space, starts as infinite
31+
cBest = float('inf')
32+
pathLen = float('inf')
33+
treeSize = 0
34+
pathSize = 0
35+
solutionSet = set()
36+
path = None
37+
38+
# Computing the sampling space
39+
cMin = math.sqrt(pow(self.start.x - self.goal.x, 2) + pow(self.start.y - self.goal.y, 2))
40+
xCenter = np.matrix([[(self.start.x + self.goal.x) / 2.0], [(self.start.y + self.goal.y) / 2.0], [0]])
41+
a1 = np.matrix([[(self.goal.x - self.start.x) / cMin], [(self.goal.y - self.start.y) / cMin], [0]])
42+
id1_t = np.matrix([1.0, 0.0, 0.0]) # first column of idenity matrix transposed
43+
M = np.dot(a1 , id1_t)
44+
U, S, Vh = np.linalg.svd(M, 1, 1)
45+
C = np.dot(np.dot(U, np.diag([1.0, 1.0, np.linalg.det(U) * np.linalg.det(np.transpose(Vh))])), Vh)
46+
47+
for i in range(self.maxIter):
48+
# Sample space is defined by cBest
49+
# cMin is the minimum distance between the start point and the goal
50+
# xCenter is the midpoint between the start and the goal
51+
# cBest changes when a new path is found
52+
53+
rnd = self.sample(cBest, cMin, xCenter, C)
54+
nind = self.getNearestListIndex(self.nodeList, rnd)
55+
nearestNode = self.nodeList[nind]
56+
# steer
57+
theta = math.atan2(rnd[1] - nearestNode.y, rnd[0] - nearestNode.x)
58+
newNode = self.getNewNode(theta, nind, nearestNode)
59+
d = self.lineCost(nearestNode, newNode)
60+
if self.__CollisionCheck(newNode, self.obstacleList) and self.check_collision_extend(nearestNode, theta, d):
61+
nearInds = self.findNearNodes(newNode)
62+
newNode = self.chooseParent(newNode, nearInds)
63+
64+
self.nodeList.append(newNode)
65+
self.rewire(newNode, nearInds)
66+
67+
if self.isNearGoal(newNode):
68+
solutionSet.add(newNode)
69+
lastIndex = len(self.nodeList) -1
70+
tempPath = self.getFinalCourse(lastIndex)
71+
tempPathLen = self.getPathLen(tempPath)
72+
if tempPathLen < pathLen:
73+
path = tempPath
74+
cBest = tempPathLen
75+
76+
if animation:
77+
self.drawGraph(rnd)
78+
79+
return path
80+
81+
def chooseParent(self, newNode, nearInds):
82+
if len(nearInds) == 0:
83+
return newNode
84+
85+
dList = []
86+
for i in nearInds:
87+
dx = newNode.x - self.nodeList[i].x
88+
dy = newNode.y - self.nodeList[i].y
89+
d = math.sqrt(dx ** 2 + dy ** 2)
90+
theta = math.atan2(dy, dx)
91+
if self.check_collision_extend(self.nodeList[i], theta, d):
92+
dList.append(self.nodeList[i].cost + d)
93+
else:
94+
dList.append(float('inf'))
95+
96+
minCost = min(dList)
97+
minInd = nearInds[dList.index(minCost)]
98+
99+
if minCost == float('inf'):
100+
print("mincost is inf")
101+
return newNode
102+
103+
newNode.cost = minCost
104+
newNode.parent = minInd
105+
106+
return newNode
107+
108+
def findNearNodes(self, newNode):
109+
nnode = len(self.nodeList)
110+
r = 50.0 * math.sqrt((math.log(nnode) / nnode))
111+
dlist = [(node.x - newNode.x) ** 2 +
112+
(node.y - newNode.y) ** 2 for node in self.nodeList]
113+
nearinds = [dlist.index(i) for i in dlist if i <= r ** 2]
114+
return nearinds
115+
116+
def sample(self, cMax, cMin, xCenter, C):
117+
if cMax < float('inf'):
118+
r = [cMax /2.0, math.sqrt(cMax**2 - cMin**2)/2.0,
119+
math.sqrt(cMax**2 - cMin**2)/2.0]
120+
L = np.diag(r)
121+
xBall = self.sampleUnitBall()
122+
rnd = np.dot(np.dot(C, L), xBall) + xCenter
123+
rnd = [rnd[(0,0)], rnd[(1,0)]]
124+
else:
125+
rnd = self.sampleFreeSpace()
126+
127+
return rnd
128+
129+
def sampleUnitBall(self):
130+
a = random.random()
131+
b = random.random()
132+
133+
if b < a:
134+
a, b = b, a
135+
136+
sample = (b * math.cos(2 * math.pi * a / b),
137+
b * math.sin(2 * math.pi * a / b))
138+
return np.array([[sample[0]], [sample[1]], [0]])
139+
140+
def sampleFreeSpace(self):
141+
if random.randint(0,100) > self.goalSampleRate:
142+
rnd = [random.uniform(self.minrand, self.maxrand),
143+
random.uniform(self.minrand, self.maxrand)]
144+
else:
145+
rnd = [self.goal.x, self.goal.y]
146+
147+
return rnd
148+
149+
def getPathLen(self, path):
150+
pathLen = 0
151+
for i in range(1, len(path)):
152+
node1_x = path[i][0]
153+
node1_y = path[i][1]
154+
node2_x = path[i-1][0]
155+
node2_y = path[i-1][1]
156+
pathLen += math.sqrt((node1_x - node2_x)**2 + (node1_y - node2_y)**2)
157+
158+
return pathLen
159+
160+
def lineCost(self, node1, node2):
161+
return math.sqrt((node1.x - node2.x)**2 + (node1.y - node2.y)**2)
162+
163+
def getNearestListIndex(self, nodes, rnd):
164+
dList = [(node.x - rnd[0])**2 +
165+
(node.y - rnd[1])**2 for node in nodes]
166+
minIndex = dList.index(min(dList))
167+
return minIndex
168+
169+
def __CollisionCheck(self, newNode, obstacleList):
170+
for (ox, oy, size) in obstacleList:
171+
dx = ox - newNode.x
172+
dy = oy - newNode.y
173+
d = dx * dx + dy * dy
174+
if d <= 1.1 * size**2:
175+
return False #collision
176+
177+
return True # safe
178+
179+
def getNewNode(self, theta, nind, nearestNode):
180+
newNode = copy.deepcopy(nearestNode)
181+
182+
newNode.x += self.expandDis * math.cos(theta)
183+
newNode.y += self.expandDis * math.sin(theta)
184+
185+
newNode.cost += self.expandDis
186+
newNode.parent = nind
187+
return newNode
188+
189+
def isNearGoal(self, node):
190+
d = self.lineCost(node, self.goal)
191+
if d < self.expandDis:
192+
return True
193+
return False
194+
195+
def rewire(self, newNode, nearInds):
196+
nnode = len(self.nodeList)
197+
for i in nearInds:
198+
nearNode = self.nodeList[i]
199+
200+
d = math.sqrt((nearNode.x - newNode.x)**2 +
201+
(nearNode.y - newNode.y)**2)
202+
203+
scost = newNode.cost + d
204+
205+
if nearNode.cost > scost:
206+
theta = math.atan2(newNode.y - nearNode.y ,
207+
newNode.x - nearNode.x)
208+
if self.check_collision_extend(nearNode, theta, d):
209+
nearNode.parent = nnode - 1
210+
nearNode.cost = scost
211+
212+
def check_collision_extend(self, nearNode, theta, d):
213+
tmpNode = copy.deepcopy(nearNode)
214+
215+
for i in range(int(d / self.expandDis)):
216+
tmpNode.x += self.expandDis * math.cos(theta)
217+
tmpNode.y += self.expandDis * math.sin(theta)
218+
if not self.__CollisionCheck(tmpNode, self.obstacleList):
219+
return False
220+
221+
return True
222+
223+
def getFinalCourse(self, lastIndex):
224+
path = [[self.goal.x, self.goal.y]]
225+
while self.nodeList[lastIndex].parent is not None:
226+
node = self.nodeList[lastIndex]
227+
path.append([node.x, node.y])
228+
lastIndex = node.parent
229+
path.append([self.start.x, self.start.y])
230+
return path
231+
##################################################################################
232+
def drawGraph(self, rnd=None):
233+
234+
plt.clf()
235+
if rnd is not None:
236+
plt.plot(rnd[0], rnd[1], "^k")
237+
for node in self.nodeList:
238+
if node.parent is not None:
239+
if node.x or node.y is not None:
240+
plt.plot([node.x, self.nodeList[node.parent].x], [
241+
node.y, self.nodeList[node.parent].y], "-g")
242+
243+
for (ox, oy, size) in self.obstacleList:
244+
plt.plot(ox, oy, "ok", ms = 30 * size)
245+
246+
plt.plot(self.start.x, self.start.y, "xr")
247+
plt.plot(self.goal.x, self.goal.y, "xr")
248+
plt.axis([-2, 15, -2, 15])
249+
plt.grid(True)
250+
plt.pause(0.01)
251+
252+
class Node():
253+
254+
def __init__(self, x, y):
255+
self.x = x
256+
self.y = y
257+
self.cost = 0.0
258+
self.parent = None
259+
260+
261+
def main():
262+
print("Start informed rrt star planning")
263+
264+
# create obstacles
265+
obstacleList = [
266+
(5, 5, 0.5),
267+
(9, 6, 1),
268+
(7, 5, 1),
269+
(1, 5, 1),
270+
(3, 6, 1),
271+
(7, 9, 1)
272+
]
273+
274+
# Set params
275+
rrt = InformedRRTStar(start = [0, 0], goal = [5, 10],
276+
randArea = [-2, 15], obstacleList = obstacleList)
277+
path = rrt.InformedRRTStarSearch(animation = show_animation)
278+
279+
# Plot path
280+
if show_animation:
281+
rrt.drawGraph()
282+
plt.plot([x for (x, y) in path], [y for (x, y) in path], '-r')
283+
plt.grid(True)
284+
plt.pause(0.01)
285+
plt.show()
286+
287+
288+
if __name__ == '__main__':
289+
main()

0 commit comments

Comments
 (0)