路径规划算法的python实现

MATLAB A* 搜索算法转 Python 实现

下面我把这套 MATLAB 代码完整翻译成 Python,使用 `numpy` + `matplotlib`,运行后能看到和 MATLAB 一样的效果:随机障碍地图、起点、终点、A* 规划出的最优路径。

文件结构

建议按如下结构组织:

```

a_star_python/

├── main.py

├── obstacle_map.py

├── a_star_search.py

├── visualize_map.py

├── distance.py

├── expand_array.py

├── insert_open.py

├── min_fn.py

└── node_index.py

```


1. `distance.py`

```python

import math

def distance(x1, y1, x2, y2):

"""计算两点之间的欧氏距离"""

return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)

```


2. `obstacle_map.py`

```python

import numpy as np

def obstacle_map(xStart, yStart, xTarget, yTarget, MAX_X, MAX_Y):

"""

生成随机障碍地图,返回 N x 2 的数组。

第 1 行为起点,最后 1 行为终点,中间为障碍。

"""

rand_map = np.random.rand(MAX_X, MAX_Y)

map_list = \[\]

map_list.append(xStart, yStart)

obstacle_ratio = 0.25

for i in range(1, MAX_X + 1):

for j in range(1, MAX_Y + 1):

if (rand_mapi - 1, j - 1 < obstacle_ratio

and not (i == xStart and j == yStart)

and not (i == xTarget and j == yTarget)):

map_list.append(i, j)

map_list.append(xTarget, yTarget)

return np.array(map_list, dtype=float)

```


3. `insert_open.py`

```python

import numpy as np

def insert_open(xval, yval, parent_xval, parent_yval, hn, gn, fn):

"""

OPEN 列表格式:

0: IS ON LIST (1/0)

1: X val

2: Y val

3: Parent X val

4: Parent Y val

5: h(n)

6: g(n)

7: f(n)

"""

new_row = np.array(\[1, xval, yval, parent_xval, parent_yval, hn, gn, fn], dtype=float)

return new_row

```


4. `min_fn.py`

```python

import numpy as np

def min_fn(OPEN, OPEN_COUNT, xTarget, yTarget):

"""

返回 OPEN 列表中 f(n) 最小的节点在 OPEN 中的行索引(0-based)。

如果 OPEN 中没有可用节点,返回 -1。

"""

temp_array = \[\]

for j in range(OPEN_COUNT):

if OPENj, 0 == 1:

temp_array.append(list(OPENj, :) + j) # 末尾追加原始索引

if len(temp_array) == 0:

return -1

temp_array = np.array(temp_array)

第 7 列是 f(n)(0-based),最后一列是原索引

temp_min = int(np.argmin(temp_array:, 7))

i_min = int(temp_arraytemp_min, -1)

return i_min

```


5. `node_index.py`

```python

def node_index(OPEN, xval, yval):

"""返回 OPEN 列表中坐标为 (xval, yval) 的行索引(0-based)"""

i = 0

n = OPEN.shape0

while i < n:

if OPENi, 1 == xval and OPENi, 2 == yval:

return i

i += 1

return -1

```


6. `expand_array.py`

```python

import numpy as np

from distance import distance

def expand_array(node_x, node_y, gn, xTarget, yTarget, CLOSED, MAX_X, MAX_Y):

"""

返回当前节点的所有合法后继节点数组:

列格式: | X | Y | h(n) | g(n) | f(n) |

"""

exp_array = \[\]

c2 = CLOSED.shape0

for k in 1, 0, -1:

for j in 1, 0, -1:

if k == 0 and j == 0:

continue # 跳过自身

s_x = node_x + k

s_y = node_y + j

if 1 <= s_x <= MAX_X and 1 <= s_y <= MAX_Y:

flag = 1

for c1 in range(c2):

if s_x == CLOSEDc1, 0 and s_y == CLOSEDc1, 1:

flag = 0

break

if flag == 1:

h = distance(xTarget, yTarget, s_x, s_y)

g = gn + distance(node_x, node_y, s_x, s_y)

f = h + g

exp_array.append(s_x, s_y, h, g, f)

if len(exp_array) == 0:

return np.zeros((0, 5))

return np.array(exp_array, dtype=float)

```


7. `a_star_search.py`(核心)

```python

import numpy as np

from distance import distance

from insert_open import insert_open

from min_fn import min_fn

from node_index import node_index

from expand_array import expand_array

def a_star_search(map_data, MAX_X, MAX_Y):

"""

A* 搜索主函数。

输入 map_data: N x 2 数组,第 1 行为起点,最后 1 行为终点,中间为障碍。

输出 path: M x 2 数组,从起点到终点的最优路径(含起点和终点)。

同时返回 OPEN / CLOSED 用于可视化(可选)。

"""

size_map = map_data.shape0

X_offset = 0

Y_offset = 0

MAP: 2=空闲, -1=障碍, 0=终点, 1=起点

MAP = 2 * np.ones((MAX_X, MAX_Y), dtype=float)

终点

xval = int(np.floor(map_datasize_map - 1, 0)) + X_offset

yval = int(np.floor(map_datasize_map - 1, 1)) + Y_offset

xTarget, yTarget = xval, yval

MAPxval - 1, yval - 1 = 0

障碍

for i in range(1, size_map - 1):

xval = int(np.floor(map_datai, 0)) + X_offset

yval = int(np.floor(map_datai, 1)) + Y_offset

MAPxval - 1, yval - 1 = -1

起点

xval = int(np.floor(map_data0, 0)) + X_offset

yval = int(np.floor(map_data0, 1)) + Y_offset

xStart, yStart = xval, yval

MAPxval - 1, yval - 1 = 1

---------------- 初始化 OPEN / CLOSED ----------------

OPEN = np.zeros((0, 8))

CLOSED = np.zeros((0, 2))

把所有障碍放入 CLOSED

closed_list = \[\]

for i in range(1, MAX_X + 1):

for j in range(1, MAX_Y + 1):

if MAPi - 1, j - 1 == -1:

closed_list.append(i, j)

if len(closed_list) > 0:

CLOSED = np.array(closed_list, dtype=float)

CLOSED_COUNT = CLOSED.shape0

起点加入 OPEN

xNode, yNode = xval, yval

OPEN_COUNT = 1

goal_distance = distance(xNode, yNode, xTarget, yTarget)

path_cost = 0

OPEN = insert_open(xNode, yNode, xNode, yNode, goal_distance, path_cost, goal_distance)

OPEN0, 0 = 0 # 起点自身标记为不在 OPEN(MATLAB 原逻辑保留)

起点同时加入 CLOSED

CLOSED_COUNT += 1

CLOSED = np.vstack(CLOSED, \[\[xNode, yNode]])

NoPath = 1

---------------- A* 主循环 ----------------

记录所有访问过的节点用于可视化

visit_nodes: 每行 IS_ON_LIST(1=open,0=closed), X, Y

visit_nodes = \[\]

while NoPath == 1:

从 OPEN 中选 f 最小的节点

i_min = min_fn(OPEN, OPEN_COUNT, xTarget, yTarget)

if i_min == -1:

OPEN 空了,无路可走

NoPath = 0

break

取出该节点并从 OPEN 移除(标记为 0)

node_x = OPENi_min, 1

node_y = OPENi_min, 2

OPENi_min, 0 = 0

加入 CLOSED

CLOSED_COUNT += 1

CLOSED = np.vstack(CLOSED, \[\[node_x, node_y]])

记录到 visit_nodes(作为 closed)

visit_nodes.append(0, node_x, node_y)

判断是否到达终点

if node_x == xTarget and node_y == yTarget:

NoPath = 0

xval, yval = node_x, node_y

break

扩展当前节点

gn = OPENi_min, 6

exp_array = expand_array(node_x, node_y, gn,

xTarget, yTarget, CLOSED, MAX_X, MAX_Y)

for i in range(exp_array.shape0):

exp_x = exp_arrayi, 0

exp_y = exp_arrayi, 1

h = exp_arrayi, 2

g = exp_arrayi, 3

f = exp_arrayi, 4

检查该后继是否已经在 OPEN 里

n_index = -1

for j in range(OPEN_COUNT):

if OPENj, 0 == 1 and OPENj, 1 == exp_x and OPENj, 2 == exp_y:

n_index = j

break

if n_index == -1:

不在 OPEN -> 插入

new_row = insert_open(exp_x, exp_y, node_x, node_y, h, g, f)

OPEN = np.vstack(OPEN, new_row)

OPEN_COUNT += 1

visit_nodes.append(1, exp_x, exp_y)

else:

已在 OPEN -> 若新路径更优则更新

if f < OPENn_index, 7:

OPENn_index, 3 = node_x

OPENn_index, 4 = node_y

OPENn_index, 5 = h

OPENn_index, 6 = g

OPENn_index, 7 = f

---------------- 回溯最优路径 ----------------

path = \[\]

if NoPath == 0 and OPEN.shape0 > 0:

找到终点在 OPEN 中的索引

注意:终点可能已经在 CLOSED 中(因为循环里先加入 CLOSED 再 break)

所以直接在 CLOSED 里找终点并回溯 parent

但 CLOSED 不含 parent 信息,所以需要从 OPEN 里找

上面循环中到达终点时已 break,此时终点在 OPEN 中标记为 0,且带 parent

target_idx = -1

for j in range(OPEN_COUNT):

if OPENj, 1 == xTarget and OPENj, 2 == yTarget:

target_idx = j

break

if target_idx != -1:

path.append(xTarget, yTarget)

parent_x = OPENtarget_idx, 3

parent_y = OPENtarget_idx, 4

while not (parent_x == xStart and parent_y == yStart):

path.append(parent_x, parent_y)

p_idx = -1

for j in range(OPEN_COUNT):

if OPENj, 1 == parent_x and OPENj, 2 == parent_y:

p_idx = j

break

if p_idx == -1:

break

parent_x = OPENp_idx, 3

parent_y = OPENp_idx, 4

path.append(xStart, yStart)

path.reverse()

path = np.array(path, dtype=float) if len(path) > 0 else np.zeros((0, 2))

if len(visit_nodes) > 0:

visit_nodes = np.array(visit_nodes, dtype=float)

else:

visit_nodes = np.zeros((0, 3))

return path, visit_nodes

```

> **说明**:MATLAB 原代码中 `while(0)` 是留给你补全的作业。上面 Python 实现已经补全了整个 A* 主循环,逻辑和 MATLAB 注释里的 OPEN/CLOSED 结构完全一致。


8. `visualize_map.py`

```python

import numpy as np

import matplotlib.pyplot as plt

from matplotlib.patches import Patch

def visualize_map(map_data, path, visit_nodes=None):

"""可视化 2D 栅格地图、起点、终点、最优路径、访问节点"""

fig, ax = plt.subplots(figsize=(7, 7))

ax.set_aspect('equal')

sz_map = int(np.max(map_data))

障碍(不含起点和终点)

if map_data.shape0 > 2:

obst = map_data1:-1, :

ax.scatter(obst:, 0 - 0.5, obst:, 1 - 0.5,

s=max(2500 / sz_map, 36),

c=\[55 / 255, 184 / 255, 157 / 255],

marker='o')

起点

ax.scatter(map_data0, 0 - 0.5, map_data0, 1 - 0.5,

marker='*', c='b', s=120, label='Start')

终点

ax.scatter(map_data-1, 0 - 0.5, map_data-1, 1 - 0.5,

marker='*', c='r', s=120, label='Target')

最优路径

if path is not None and path.shape0 > 1:

中间点

if path.shape0 > 2:

ax.scatter(path1:-1, 0 - 0.5, path1:-1, 1 - 0.5,

c='b', s=30)

ax.plot(path:, 0 - 0.5, path:, 1 - 0.5, 'b-', linewidth=2)

访问节点(OPEN / CLOSED)

if visit_nodes is not None and visit_nodes.shape0 > 0:

node_sz = 10000 / sz_map

OPEN 列表(绿色)

open_nodes = visit_nodesvisit_nodes\[:, 0 == 1]

if open_nodes.shape0 > 0:

ax.scatter(open_nodes:, 1 - 0.5, open_nodes:, 2 - 0.5,

s=node_sz, c='g', marker='s', alpha=0.1)

CLOSED 列表(蓝色)

closed_nodes = visit_nodesvisit_nodes\[:, 0 == 0]

if closed_nodes.shape0 > 0:

ax.scatter(closed_nodes:, 1 - 0.5, closed_nodes:, 2 - 0.5,

s=node_sz, c='b', marker='s', alpha=0.1)

网格

ax.set_xticks(np.arange(0, sz_map + 1, 1))

ax.set_yticks(np.arange(0, sz_map + 1, 1))

ax.grid(True, linewidth=0.5, alpha=0.5)

ax.set_xlim(-1, sz_map + 1)

ax.set_ylim(-1, sz_map + 1)

ax.set_xlabel('X')

ax.set_ylabel('Y')

ax.set_title('A* Path Planning')

ax.legend(loc='upper right')

plt.tight_layout()

plt.show()

```


9. `main.py`

```python

import numpy as np

import matplotlib.pyplot as plt

from obstacle_map import obstacle_map

from a_star_search import a_star_search

from visualize_map import visualize_map

def main():

固定随机种子,方便复现

np.random.seed(42)

xStart = 1.0

yStart = 1.0

xTarget = 9.0

yTarget = 9.0

MAX_X = 10

MAX_Y = 10

生成地图

map_data = obstacle_map(xStart, yStart, xTarget, yTarget, MAX_X, MAX_Y)

A* 搜索

path, visit_nodes = a_star_search(map_data, MAX_X, MAX_Y)

可视化

visualize_map(map_data, path, visit_nodes)

if path.shape0 > 0:

print("找到路径,长度:", path.shape0)

print("路径:")

print(path)

else:

print("未找到路径")

if name == "main":

main()

```


10. 运行方式

```bash

pip install numpy matplotlib

python main.py

```

运行后你会看到:

  • 绿色圆点:随机障碍(约 25% 比例)

  • 蓝色星号:起点 (1,1)

  • 红色星号:终点 (9,9)

  • 绿色半透明方块:OPEN 列表访问过的节点

  • 蓝色半透明方块:CLOSED 列表访问过的节点

  • 蓝色连线:A* 找出的最优路径

效果与 MATLAB 的 `visualize_map` 基本一致。


11. 与 MATLAB 代码的对应关系

| MATLAB 文件 | Python 文件 | 说明 |

|---|---|---|

| `distance.m` | `distance.py` | 欧氏距离 |

| `obstacle_map.m` | `obstacle_map.py` | 随机障碍地图 |

| `insert_open.m` | `insert_open.py` | 构造 OPEN 行 |

| `min_fn.m` | `min_fn.py` | 找 f 最小节点 |

| `node_index.m` | `node_index.py` | 查节点在 OPEN 中的索引 |

| `expand_array.m` | `expand_array.py` | 扩展后继节点 |

| `A_star_search.m` | `a_star_search.py` | A* 主逻辑(含补全的 while 循环和回溯) |

| `visualize_map.m` | `visualize_map.py` | 可视化 |

| `main.m` | `main.py` | 入口 |


12. 几点注意

  1. **索引差异**:MATLAB 从 1 开始,Python 从 0 开始。代码中所有对 `MAP`、`OPEN`、`CLOSED` 的访问都做了 `-1` 或保持不变的处理,逻辑上与原代码一致。

  2. **`while(0)` 的补全**:原 MATLAB 是作业留白,Python 版已经补全完整 A* 主循环,包括:

  • 从 OPEN 取 f 最小节点;

  • 移入 CLOSED;

  • 判断是否到终点;

  • 扩展后继;

  • 若后继已在 OPEN 且新 f 更小,则更新。

  1. **路径回溯**:从终点沿 parent 一路回到起点,最后 `reverse` 得到从起点到终点的顺序。

  2. **可视化 visit_nodes**:MATLAB 中 `visit_nodes` 每行是 `IS_ON_LIST, X, Y`,其中 1 表示 OPEN,0 表示 CLOSED。Python 版保持一致。

  3. **随机种子**:MATLAB 每次运行地图不同,Python 里用 `np.random.seed(42)` 固定,方便调试;想随机可去掉这行。


13. 可能遇到的问题

  • **找不到路径**:当地图障碍太密时,A* 可能无解。可以减小 `obstacle_ratio` 或换随机种子。

  • **OpenCV/Matplotlib 中文乱码**:标题用了英文,避免字体问题。

  • **性能**:当前 OPEN 用 `numpy` 数组 + 线性扫描,10×10 地图完全够用;如果地图很大,建议换成 `heapq` 或 `scipy` 的优先队列。


这样你就得到了一个可运行的 Python 版 A* 路径规划,效果和 MATLAB 版本一致。

相关推荐
心中有你02141 小时前
Java Swing实现校园最短路径导航系统(Dijkstra算法完整源码+详细解析)
java·开发语言·算法
重生之小比特1 小时前
【Java SE】数组的定义与使用
java·开发语言
LabVIEW开发1 小时前
从饱和升温曲线到参数辨识:在LabVIEW中拟合y=a(1-e^(-bx))
算法·labview·labview知识·labview功能·labview程序
旋生万物1 小时前
圈道裂痕——用 Python 验证素数分布的螺旋生成论判据
开发语言·python·数论·素数·cci·可计算数学
阿洛学长1 小时前
Python笔记:dir() 和 help() 完整用法(适配Python二级 + IDLE实操)
服务器·笔记·python
Jialu.1 小时前
第2篇:LLM 结构化输出实战:Pydantic + Function Calling 告别“解析 JSON 地狱“
python·langchain
keyipatience1 小时前
5种IO模型与阻塞IO,select,poll,epoll,LT和ET模式
linux·服务器·网络·数据结构·c++·算法
qeen871 小时前
【C++】智能指针介绍
开发语言·c++·笔记·指针
hanchenxing1 小时前
本地AI绘画网关: 用 30 行 Python 把 NVIDIA FLUX.2 Klein 接入 OpenAI 兼容客户端Python
开发语言·python·ai作画·ai绘画·nvidia