Skip to content

Commit 4fa3b1c

Browse files
author
Eric Chen
committed
first commit
0 parents  commit 4fa3b1c

File tree

1 file changed

+231
-0
lines changed

1 file changed

+231
-0
lines changed

D*_lite.py

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
import matplotlib.pyplot as plt
2+
import numpy as np
3+
import heapq
4+
5+
class Node:
6+
def __init__(self, x, y):
7+
self.pos = (x, y)
8+
self.g = float('inf')
9+
self.rhs = float('inf')
10+
self.is_obstacle = False
11+
12+
def __eq__(self, other):
13+
return isinstance(other, Node) and self.pos == other.pos
14+
15+
def __hash__(self):
16+
return hash(self.pos)
17+
18+
def __lt__(self, other):
19+
return self.pos < other.pos
20+
21+
grid_size = 20
22+
grid = np.zeros((grid_size, grid_size)) # 0: free, 1: obstacle
23+
node_dict = {}
24+
for x in range(grid_size):
25+
for y in range(grid_size):
26+
node_dict[(x, y)] = Node(x, y)
27+
28+
# 障礙物設定
29+
grid[5:10, 8] = 1
30+
grid[15, 5:15] = 1
31+
grid[12, 2:7] = 1
32+
grid[8, 12:16] = 1
33+
grid[1:4, 10] = 1
34+
for (x, y), node in node_dict.items():
35+
if grid[x][y] == 1:
36+
node.is_obstacle = True
37+
38+
start = node_dict[(2, 3)]
39+
goal = node_dict[(17, 14)]
40+
41+
def h(a, b):
42+
return abs(a.pos[0] - b.pos[0]) + abs(a.pos[1] - b.pos[1])
43+
44+
def cost(u, v):
45+
return float('inf') if v.is_obstacle else 1
46+
47+
def key(s):
48+
return (min(s.g, s.rhs) + h(start, s), min(s.g, s.rhs))
49+
50+
open_list = []
51+
open_set = set()
52+
visited_nodes = set() # 記錄所有試圖拜訪的節點
53+
54+
def push_to_open_list(node):
55+
if node not in open_set:
56+
heapq.heappush(open_list, (key(node), node))
57+
open_set.add(node)
58+
visited_nodes.add(node)
59+
60+
def pop_from_open_list():
61+
while open_list:
62+
k, node = heapq.heappop(open_list)
63+
if node in open_set:
64+
open_set.remove(node)
65+
visited_nodes.add(node)
66+
return node
67+
return None
68+
69+
def neighbor(u):
70+
dirs = [[0, 1], [1, 0], [-1, 0], [0, -1]]
71+
ns = []
72+
for dx, dy in dirs:
73+
nx, ny = u.pos[0] + dx, u.pos[1] + dy
74+
if 0 <= nx < grid_size and 0 <= ny < grid_size:
75+
ns.append(node_dict[(nx, ny)])
76+
return ns
77+
78+
def update_vertex(u):
79+
if u != goal:
80+
u.rhs = min(cost(u, s) + s.g for s in neighbor(u))
81+
if u in open_set:
82+
open_set.remove(u)
83+
if u.g != u.rhs:
84+
push_to_open_list(u)
85+
86+
def compute_shortest_path():
87+
while open_list and (key(start) > open_list[0][0] or start.rhs != start.g):
88+
u = pop_from_open_list()
89+
if u is None:
90+
break
91+
if u.g > u.rhs:
92+
u.g = u.rhs
93+
for s in neighbor(u):
94+
update_vertex(s)
95+
else:
96+
u.g = float('inf')
97+
update_vertex(u)
98+
for s in neighbor(u):
99+
update_vertex(s)
100+
101+
def get_next_move(current):
102+
candidates = []
103+
for s in neighbor(current):
104+
if not s.is_obstacle:
105+
candidates.append((cost(current, s) + s.g, s))
106+
if not candidates:
107+
return None
108+
return min(candidates)[1]
109+
110+
def detect_new_obstacles(current):
111+
new_obs = []
112+
for s in neighbor(current):
113+
if grid[s.pos[0]][s.pos[1]] == 1 and not s.is_obstacle:
114+
s.is_obstacle = True
115+
new_obs.append(s)
116+
return new_obs
117+
118+
fig, ax = plt.subplots(figsize=(6, 6))
119+
plt.ion() # 開啟交互模式
120+
121+
def update_plot(current_path, visited_nodes, new_obstacles=None):
122+
ax.clear()
123+
display_grid = np.zeros((grid_size, grid_size))
124+
# 繪製試圖拜訪的節點
125+
max_g = max([n.g for n in visited_nodes if n.g != float('inf')] or [1])
126+
for node in visited_nodes:
127+
g_value = node.g if node.g != float('inf') else 0
128+
normalized_g = 0.3 + 0.5 * (g_value / max_g)
129+
display_grid[node.pos[0]][node.pos[1]] = normalized_g
130+
131+
# 標記起點、終點和障礙物
132+
display_grid[start.pos[0]][start.pos[1]] = 0.2
133+
display_grid[goal.pos[0]][goal.pos[1]] = 0.1
134+
display_grid[grid == 1] = 1.0
135+
136+
# 繪製地圖
137+
ax.imshow(display_grid, cmap='Greens')
138+
ax.set_title("D* Lite Dynamic Path Planning")
139+
ax.set_xticks(np.arange(0, grid_size, 1))
140+
ax.set_yticks(np.arange(0, grid_size, 1))
141+
ax.set_xticks(np.arange(-0.5, grid_size, 1), minor=True)
142+
ax.set_yticks(np.arange(-0.5, grid_size, 1), minor=True)
143+
ax.grid(which='minor', color='black', linewidth=0.5)
144+
ax.invert_yaxis()
145+
146+
# 繪製當前路徑(紅線)
147+
path_x = [node.pos[1] for node in current_path]
148+
path_y = [node.pos[0] for node in current_path]
149+
ax.plot(path_x, path_y, 'r-', linewidth=2, label='current path')
150+
ax.scatter(path_x[0], path_y[0], c='red', s=100, marker='o', label='start')
151+
ax.scatter(path_x[-1], path_y[-1], c='blue', s=100, marker='*', label='goal')
152+
153+
# 標記新障礙物(如果有)
154+
if new_obstacles:
155+
for obs in new_obstacles:
156+
ax.scatter(obs.pos[1], obs.pos[0], c='black', s=100, marker='x', label='new obstacle')
157+
158+
ax.legend()
159+
plt.draw()
160+
plt.pause(0.5) # 暫停以顯示動畫效果
161+
162+
new_obstacles = []
163+
def on_click(event):
164+
if event.inaxes == ax and event.button == 1: # 左鍵點擊
165+
x, y = int(event.ydata + 0.5), int(event.xdata + 0.5)
166+
if (x, y) != start.pos and (x, y) != goal.pos and grid[x, y] == 0:
167+
grid[x, y] = 1
168+
node_dict[(x, y)].is_obstacle = True
169+
new_obstacles.append(node_dict[(x, y)])
170+
print(f"新增障礙物: ({x}, {y})")
171+
172+
fig.canvas.mpl_connect('button_press_event', on_click)
173+
174+
goal.rhs = 0
175+
push_to_open_list(goal)
176+
compute_shortest_path()
177+
178+
current = start
179+
path = [current]
180+
step_count = 0
181+
182+
print("按左鍵點擊地圖添加新障礙物,按任意鍵繼續下一步,關閉窗口結束模擬。")
183+
while current != goal:
184+
# 計算當前路徑
185+
next_node = get_next_move(current)
186+
if next_node is None:
187+
print("無路徑到達終點!")
188+
break
189+
current = next_node
190+
path.append(current)
191+
step_count += 1
192+
193+
# 檢查新障礙物(包括點擊添加的)
194+
detected_obs = detect_new_obstacles(current) + new_obstacles
195+
if detected_obs:
196+
for u in detected_obs:
197+
for s in neighbor(u):
198+
update_vertex(s)
199+
update_vertex(u)
200+
compute_shortest_path()
201+
new_obstacles = [] # 清空點擊添加的障礙物
202+
203+
# 更新路徑顯示
204+
temp_path = [current]
205+
temp_node = current
206+
while temp_node != goal:
207+
next_node = get_next_move(temp_node)
208+
if next_node is None:
209+
break
210+
temp_path.append(next_node)
211+
temp_node = next_node
212+
213+
# 顯示當前進度
214+
print(f"\n步驟 {step_count}: 當前位置 {current.pos}")
215+
print("當前路徑節點:")
216+
for node in temp_path:
217+
print(f"節點: {node.pos}, g 分數: {node.g}")
218+
update_plot(temp_path, visited_nodes, detected_obs)
219+
220+
# 等待用戶輸入以繼續
221+
plt.waitforbuttonpress()
222+
223+
print("\n最終走過的路徑節點(從起點到終點):")
224+
for node in path:
225+
print(f"節點: {node.pos}, g 分數: {node.g}")
226+
print("\n所有試圖拜訪的節點:")
227+
for node in visited_nodes:
228+
print(f"節點: {node.pos}, g 分數: {node.g}")
229+
230+
plt.ioff()
231+
plt.show()

0 commit comments

Comments
 (0)