Skip to content

Commit 2cf4f6f

Browse files
authored
Added D* Search to path planning folder (AtsushiSakai#490)
* changes * updated docs and readme * Update a_star.py * Update a_star.py * Create test_dstar.py * trailing loc * Update dstar.py * Update dstar.py * Update dstar.py * Update dstar.py * Update dstar.py * newline * corrected changes requested * 13, five, 21 * corrected changes * latest * linted * lint * removed diff
1 parent 0e23203 commit 2cf4f6f

File tree

4 files changed

+282
-0
lines changed

4 files changed

+282
-0
lines changed

PathPlanning/DStar/dstar.py

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
"""
2+
3+
D* grid planning
4+
5+
author: Nirnay Roy
6+
7+
See Wikipedia article (https://en.wikipedia.org/wiki/D*)
8+
9+
"""
10+
import math
11+
12+
from sys import maxsize
13+
14+
import matplotlib.pyplot as plt
15+
16+
show_animation = True
17+
18+
19+
class State:
20+
21+
def __init__(self, x, y):
22+
self.x = x
23+
self.y = y
24+
self.parent = None
25+
self.state = "."
26+
self.t = "new" # tag for state
27+
self.h = 0
28+
self.k = 0
29+
30+
def cost(self, state):
31+
if self.state == "#" or state.state == "#":
32+
return maxsize
33+
34+
return math.sqrt(math.pow((self.x - state.x), 2) +
35+
math.pow((self.y - state.y), 2))
36+
37+
def set_state(self, state):
38+
"""
39+
.: new
40+
#: obstacle
41+
e: oparent of current state
42+
*: closed state
43+
s: current state
44+
"""
45+
if state not in ["s", ".", "#", "e", "*"]:
46+
return
47+
self.state = state
48+
49+
50+
class Map:
51+
52+
def __init__(self, row, col):
53+
self.row = row
54+
self.col = col
55+
self.map = self.init_map()
56+
57+
def init_map(self):
58+
map_list = []
59+
for i in range(self.row):
60+
tmp = []
61+
for j in range(self.col):
62+
tmp.append(State(i, j))
63+
map_list.append(tmp)
64+
return map_list
65+
66+
def get_neighbers(self, state):
67+
state_list = []
68+
for i in [-1, 0, 1]:
69+
for j in [-1, 0, 1]:
70+
if i == 0 and j == 0:
71+
continue
72+
if state.x + i < 0 or state.x + i >= self.row:
73+
continue
74+
if state.y + j < 0 or state.y + j >= self.col:
75+
continue
76+
state_list.append(self.map[state.x + i][state.y + j])
77+
return state_list
78+
79+
def set_obstacle(self, point_list):
80+
for x, y in point_list:
81+
if x < 0 or x >= self.row or y < 0 or y >= self.col:
82+
continue
83+
84+
self.map[x][y].set_state("#")
85+
86+
87+
class Dstar:
88+
def __init__(self, maps):
89+
self.map = maps
90+
self.open_list = set()
91+
92+
def process_state(self):
93+
x = self.min_state()
94+
95+
if x is None:
96+
return -1
97+
98+
k_old = self.get_kmin()
99+
self.remove(x)
100+
101+
if k_old < x.h:
102+
for y in self.map.get_neighbers(x):
103+
if y.h <= k_old and x.h > y.h + x.cost(y):
104+
x.parent = y
105+
x.h = y.h + x.cost(y)
106+
elif k_old == x.h:
107+
for y in self.map.get_neighbers(x):
108+
if y.t == "new" or y.parent == x and y.h != x.h + x.cost(y) \
109+
or y.parent != x and y.h > x.h + x.cost(y):
110+
y.parent = x
111+
self.insert(y, x.h + x.cost(y))
112+
else:
113+
for y in self.map.get_neighbers(x):
114+
if y.t == "new" or y.parent == x and y.h != x.h + x.cost(y):
115+
y.parent = x
116+
self.insert(y, x.h + x.cost(y))
117+
else:
118+
if y.parent != x and y.h > x.h + x.cost(y):
119+
self.insert(y, x.h)
120+
else:
121+
if y.parent != x and x.h > y.h + x.cost(y) \
122+
and y.t == "close" and y.h > k_old:
123+
self.insert(y, y.h)
124+
return self.get_kmin()
125+
126+
def min_state(self):
127+
if not self.open_list:
128+
return None
129+
min_state = min(self.open_list, key=lambda x: x.k)
130+
return min_state
131+
132+
def get_kmin(self):
133+
if not self.open_list:
134+
return -1
135+
k_min = min([x.k for x in self.open_list])
136+
return k_min
137+
138+
def insert(self, state, h_new):
139+
if state.t == "new":
140+
state.k = h_new
141+
elif state.t == "open":
142+
state.k = min(state.k, h_new)
143+
elif state.t == "close":
144+
state.k = min(state.h, h_new)
145+
state.h = h_new
146+
state.t = "open"
147+
self.open_list.add(state)
148+
149+
def remove(self, state):
150+
if state.t == "open":
151+
state.t = "close"
152+
self.open_list.remove(state)
153+
154+
def modify_cost(self, x):
155+
if x.t == "close":
156+
self.insert(x, x.parent.h + x.cost(x.parent))
157+
158+
def run(self, start, end):
159+
160+
rx = []
161+
ry = []
162+
163+
self.open_list.add(end)
164+
165+
while True:
166+
self.process_state()
167+
if start.t == "close":
168+
break
169+
170+
start.set_state("s")
171+
s = start
172+
s = s.parent
173+
s.set_state("e")
174+
tmp = start
175+
176+
while tmp != end:
177+
tmp.set_state("*")
178+
rx.append(tmp.x)
179+
ry.append(tmp.y)
180+
if show_animation:
181+
plt.plot(rx, ry)
182+
plt.pause(0.01)
183+
if tmp.parent.state == "#":
184+
self.modify(tmp)
185+
continue
186+
tmp = tmp.parent
187+
tmp.set_state("e")
188+
189+
return rx, ry
190+
191+
def modify(self, state):
192+
self.modify_cost(state)
193+
while True:
194+
k_min = self.process_state()
195+
if k_min >= state.h:
196+
break
197+
198+
199+
def main():
200+
m = Map(100, 100)
201+
ox, oy = [], []
202+
for i in range(-10, 60):
203+
ox.append(i)
204+
oy.append(-10)
205+
for i in range(-10, 60):
206+
ox.append(60)
207+
oy.append(i)
208+
for i in range(-10, 61):
209+
ox.append(i)
210+
oy.append(60)
211+
for i in range(-10, 61):
212+
ox.append(-10)
213+
oy.append(i)
214+
for i in range(-10, 40):
215+
ox.append(20)
216+
oy.append(i)
217+
for i in range(0, 40):
218+
ox.append(40)
219+
oy.append(60 - i)
220+
print([(i, j) for i, j in zip(ox, oy)])
221+
m.set_obstacle([(i, j) for i, j in zip(ox, oy)])
222+
223+
start = [10, 10]
224+
goal = [50, 50]
225+
if show_animation:
226+
plt.plot(ox, oy, ".k")
227+
plt.plot(start[0], start[1], "og")
228+
plt.plot(goal[0], goal[1], "xb")
229+
230+
start = m.map[start[0]][start[1]]
231+
end = m.map[goal[0]][goal[1]]
232+
dstar = Dstar(m)
233+
rx, ry = dstar.run(start, end)
234+
235+
if show_animation:
236+
plt.plot(rx, ry)
237+
plt.show()
238+
239+
240+
if __name__ == '__main__':
241+
main()

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Python codes for robotics algorithm.
3737
* [Grid based search](#grid-based-search)
3838
* [Dijkstra algorithm](#dijkstra-algorithm)
3939
* [A* algorithm](#a-algorithm)
40+
* [D* algorithm](#d-algorithm)
4041
* [Potential Field algorithm](#potential-field-algorithm)
4142
* [Grid based coverage path planning](#grid-based-coverage-path-planning)
4243
* [State Lattice Planning](#state-lattice-planning)
@@ -301,6 +302,19 @@ In the animation, cyan points are searched nodes.
301302

302303
Its heuristic is 2D Euclid distance.
303304

305+
### D\* algorithm
306+
307+
This is a 2D grid based the shortest path planning with D star algorithm.
308+
309+
![figure at master · nirnayroy/intelligentrobotics](https://github.com/nirnayroy/intelligent-robotics/blob/main/dstar.gif)
310+
311+
The animation shows a robot finding its path avoiding an obstacle using the D* search algorithm.
312+
313+
Ref:
314+
315+
- [D* Algorithm Wikipedia](https://en.wikipedia.org/wiki/D*)
316+
317+
304318
### Potential Field algorithm
305319

306320
This is a 2D grid based path planning with Potential Field algorithm.

docs/modules/path_planning.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ In the animation, cyan points are searched nodes.
3939

4040
Its heuristic is 2D Euclid distance.
4141

42+
.. _a*-algorithm:
43+
44+
D\* algorithm
45+
~~~~~~~~~~~~~
46+
47+
This is a 2D grid based shortest path planning with D star algorithm.
48+
49+
|dstar|
50+
51+
The animation shows a robot finding its path avoiding an obstacle using the D* search algorithm.
52+
53+
Ref:
54+
55+
- `D* search Wikipedia <https://en.wikipedia.org/wiki/D*>`__
56+
4257
Potential Field algorithm
4358
~~~~~~~~~~~~~~~~~~~~~~~~~
4459

@@ -427,6 +442,7 @@ Ref:
427442
.. |DWA| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathPlanning/DynamicWindowApproach/animation.gif
428443
.. |Dijkstra| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathPlanning/Dijkstra/animation.gif
429444
.. |astar| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathPlanning/AStar/animation.gif
445+
.. |dstar| image:: https://github.com/nirnayroy/intelligent-robotics/blob/main/dstar.gif
430446
.. |PotentialField| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathPlanning/PotentialFieldPlanning/animation.gif
431447
.. |4| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathPlanning/ModelPredictiveTrajectoryGenerator/kn05animation.gif
432448
.. |5| image:: https://github.com/AtsushiSakai/PythonRobotics/raw/master/PathPlanning/ModelPredictiveTrajectoryGenerator/lookuptable.png?raw=True

tests/test_dstar.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import conftest
2+
from PathPlanning.DStar import dstar as m
3+
4+
5+
def test_1():
6+
m.show_animation = False
7+
m.main()
8+
9+
10+
if __name__ == '__main__':
11+
conftest.run_this_test(__file__)

0 commit comments

Comments
 (0)