使用 Python 实现网格地图 A* 路径规划教程

花韻仙語
发布: 2025-11-07 11:40:41
原创
1002人浏览过

使用 Python 实现网格地图 A* 路径规划教程

本教程详细介绍了如何在 python 中实现网格地图的路径规划。利用类似广度优先搜索的策略,从起点开始,逐步将可通行节点标记为指向起点的方向。一旦到达目标点,即可通过回溯这些方向,高效地重建出从起点到目标的最优路径。文章包含示例代码,帮助读者理解并应用此寻路方法。

1. 简介与问题定义

路径规划是人工智能计算机科学中的一个基本问题,广泛应用于游戏开发、机器人导航和物流优化等领域。本教程将重点介绍如何在二维网格地图中寻找从起点到终点的最短路径。

我们的地图表示为一个嵌套列表(即二维数组),其中包含以下几种值:

  • 0: 代表墙壁或不可通行区域。
  • 1: 代表空地或可通行区域。
  • 2, 3, 4: 代表不同类型的障碍物,但同样不可通行。
  • *: 代表路径的起点。
  • $$$: 代表路径的终点。

目标是编写一个 Python 函数,能够:

  1. 从起点开始,在地图中填充表示回溯方向的符号,直到到达终点。
  2. 从终点开始,沿着这些方向符号回溯,以重建出最短路径。
  3. 返回包含该路径的地图(路径上的节点被特殊符号标记)。

需要注意的是,尽管问题提及 A* 算法,但针对无权图(即所有可通行路径的成本相同,例如本例中每一步移动成本都视为1)寻找最短路径时,广度优先搜索(BFS)是一种简单且有效的算法,它能够保证找到最短路径。本教程将基于 BFS 的思想实现路径填充和回溯。

立即学习Python免费学习笔记(深入)”;

2. 地图表示与基本设置

首先,我们定义一个示例地图和一些常量,包括起点、终点以及用于标记方向的符号。

虎课网
虎课网

虎课网是超过1800万用户信赖的自学平台,拥有海量设计、绘画、摄影、办公软件、职业技能等优质的高清教程视频,用户可以根据行业和兴趣爱好,自主选择学习内容,每天免费学习一个...

虎课网 62
查看详情 虎课网
mapList = [ # 示例地图,你可以根据需要修改
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 2, 2, 1, 1, 2, 2, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 2, 2, 1, 1, 2, 2, 0, 0],
    [0, 0, 1, 1, 2, 2, 3, 3, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 2, 2, 3, 3, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 0, 0],
    [0, 0, 2, 2, 2, 2, 1, 1, 2, 2, 3, 3, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 2, 2, 2, 2, 1, 1, 2, 2, 3, 3, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 2, 2, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0],
    [0, 0, 1, 1, 2, 2, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0],
    [0, 0, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 3, 3, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 3, 3, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0],
    [0, 0, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]

# 定义起点和终点坐标及符号
sourceRow, sourceColumn = 6, 2
destinationRow, destinationColumn = 2, 15
sourceSymbol = "*"
destinationSymbol = "$$$"

# 定义方向符号
up = "^"
down = "v"
right = ">"
left = "<"

# 在地图上标记起点和终点
mapList[sourceRow][sourceColumn] = sourceSymbol
mapList[destinationRow][destinationColumn] = destinationSymbol

def print_map(map_data):
    """辅助函数:打印地图"""
    for row in map_data:
        print(" ".join(map(str, row)))
    print("-" * 40)

print("--- 初始地图 ---")
print_map(mapList)
登录后复制

3. 路径填充:广度优先搜索 (BFS) 策略

这一步的目标是从起点开始,向所有可通行的相邻节点扩散,并用方向符号标记这些节点。每个方向符号都指向其父节点(即扩散过程中上一步到达的节点),这样最终我们可以从终点沿着这些符号回溯到起点。

我们使用一个队列 tempList 来实现 BFS。每次从队列中取出一个点,检查其四个相邻点:

  • 如果相邻点是终点,则停止扩散。
  • 如果相邻点是可通行区域 (1),则将其标记为指向当前点的方向,并加入队列。
# 用于 BFS 队列
tempList = [(sourceRow, sourceColumn)]

def fill_with_directions(current_point: tuple, current_map: list):
    """
    从当前点开始,向四周可通行区域填充方向符号。
    方向符号指示从该点回溯到父节点(即当前点)的方向。
    """
    r, c = current_point

    # 定义四个方向的偏移量:(dr, dc)
    # (1, 0) -> 下, (-1, 0) -> 上, (0, 1) -> 右, (0, -1) -> 左
    neighbor_offsets = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 上,下,左,右

    for dr, dc in neighbor_offsets:
        nr, nc = r + dr, c + dc # 相邻点的坐标

        # 检查边界
        if not (0 <= nr < len(current_map) and 0 <= nc < len(current_map[0])):
            continue

        # 如果到达终点
        if current_map[nr][nc] == destinationSymbol:
            return True # 找到终点,返回True

        # 如果相邻点是可通行区域 (1)
        if current_map[nr][nc] == 1:
            # 根据相对位置填充方向符号
            if dr == -1: # 相邻点在上方,当前点在下方,所以相邻点应指向下
                current_map[nr][nc] = down
            elif dr == 1: # 相邻点在下方,当前点在上方,所以相邻点应指向上
                current_map[nr][nc] = up
            elif dc == -1: # 相邻点在左方,当前点在右方,所以相邻点应指向右
                current_map[nr][nc] = right
            elif dc == 1: # 相邻点在右方,当前点在左方,所以相邻点应指向左
                current_map[nr][nc] = left

            # 将新填充的点加入队列,以便后续探索
            tempList.append((nr, nc))
    return False # 未找到终点

# 执行 BFS 扩散
while tempList:
    current_point = tempList.pop(0) # 取出队列中的第一个点
    if fill_with_directions(current_point, mapList):
        print("--- 填充方向后的地图 (到达终点) ---")
        print_map(mapList)
        break # 找到终点,停止扩散
登录后复制

4. 路径回溯与重建

当 fill_with_directions 函数返回 True 时,表示我们已经从起点扩散到了终点。此时,地图上除了起点和终点,所有路径上的可通行节点都被标记了方向符号。接下来,我们将从终点开始,沿着这些方向符号反向追踪,直到回到起点,并将路径上的节点标记为 *。

# 从终点开始回溯
current_path_point = None

# 首先找到终点周围哪个点被标记了方向,作为回溯的起点
# 定义四个方向的偏移量
neighbor_offsets = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 上,下,左,右
possible_signs = [up, down, left, right]

for dr, dc in neighbor_offsets:
    nr, nc = destinationRow + dr, destinationColumn + dc
    # 检查边界
    if not (0 <= nr < len(mapList) and 0 <= nc < len(mapList[0])):
        continue

    if mapList[nr][nc] in possible_signs:
        current_path_point = (nr, nc)
        break

# 回溯并标记路径
while current_path_point:
    r, c = current_path_point
    current_item = mapList[r][c]

    # 如果回溯到起点,则停止
    if current_item == sourceSymbol:
        current_path_point = None
        break

    # 将当前点标记为路径的一部分
    mapList[r][c] = '*'

    # 根据当前点的方向符号,移动到下一个回溯点(即其父节点)
    if current_item == up: # 当前点指向父节点上方,说明父节点在当前点下方
        current_path_point = (r + 1, c)
    elif current_item == down: # 当前点指向父节点下方,说明父节点在当前点上方
        current_path_point = (r - 1, c)
    elif current_item == left: # 当前点指向父节点左方,说明父节点在当前点右方
        current_path_point = (r, c + 1)
    elif current_item == right: # 当前点指向父节点右方,说明父节点在当前点左方
        current_path_point = (r, c - 1)
    else:
        # 理论上不应该发生,除非路径断裂或遇到非方向符号的可通行区域
        print(f"Error: Encountered unexpected item {current_item} at {current_path_point}")
        break

print("--- 最终路径地图 ---")
print_map(mapList)
登录后复制

5. 完整函数封装

为了方便使用,我们可以将上述逻辑封装到一个函数中。

def find_path_in_map(initial_map: list, start_coords: tuple, end_coords: tuple):
    """
    在网格地图中寻找从起点到终点的最短路径。

    Args:
        initial_map (list): 嵌套列表表示的地图。
                            0: 墙壁, 1: 可通行, 2/3/4: 障碍物。
        start_coords (tuple): 起点坐标 (row, column)。
        end_coords (tuple): 终点坐标 (row, column)。

    Returns:
        list: 包含标记路径的地图,如果找不到路径则返回None。
    """
    # 创建地图的深拷贝,避免修改原始地图
    current_map = [row[:] for row in initial_map]

    sourceRow, sourceColumn = start_coords
    destinationRow, destinationColumn = end
登录后复制

以上就是使用 Python 实现网格地图 A* 路径规划教程的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号