基于A*算法的机器人路径规划 五种地图随意切换, 内涵详细的代码注释

最近在搞机器人路径规划,被A*算法折腾得够呛。这玩意儿就像个傲娇的猫主子,你得顺着它的脾气来。不过折腾明白之后,发现还真香——特别是实现五种地图自由切换的时候,看着机器人在不同地形里蛇皮走位,成就感直接拉满。

先看个刺激的,这是我们的地图工厂(代码烫手,小心食用):

# 地图生成器:五种模式任君选择
def create_map(mode, size=20):
    grid = [[0]*size for _ in range(size)]  # 初始化全空地
    if mode == 'random_obstacles':
        # 随机障碍模式,30%概率生成障碍
        return [[1 if random() < 0.3 else 0 for _ in range(size)] for _ in range(size)]
    elif mode == 'maze':
        # 迷宫模式,生成经典回型走廊
        return [[1 if (i%2==0 or j%2==0) else 0 for j in range(size)] for i in range(size)]
    elif mode == 'checkerboard':
        # 棋盘障碍,专治路径规划强迫症
        return [[(i+j)%2 for j in range(size)] for i in range(size)]
    elif mode == 'rooms':
        # 房间结构,中间留出门洞
        grid = [[1 if (i in [7,12] or j in [7,12]) else 0 for j in range(size)] for i in range(size)]
        for x in [8,9,10,11]:
            grid[7][x] = grid[12][x] = grid[x][7] = grid[x][12] = 0
        return grid
    elif mode == 'empty':
        # 无障碍模式,方便测试极限情况
        return grid

每种地图都藏着不同的坑。比如棋盘地图看着整齐,实际路径必须走之字形;房间地图的门洞要是没处理好,机器人分分钟表演撞墙绝活。

基于A*算法的机器人路径规划 五种地图随意切换, 内涵详细的代码注释

核心的A*算法长这样(内含祖传调试技巧):

def a_star(start, end, grid):
    open_set = PriorityQueue()
    open_set.put((0, start))  # 优先级队列,自动排序
    came_from = {}  # 路径追溯字典
    g_score = defaultdict(lambda: float('inf'))  # 到起点的实际代价
    g_score[start] = 0

    while not open_set.empty():
        current = open_set.get()[1]

        if current == end:
            # 找到终点,开始回撤构造路径
            path = []
            while current in came_from:
                path.append(current)
                current = came_from[current]
            return path[::-1]

        # 八邻域探索(想改四方向?把对角线坐标去掉就行)
        for dx, dy in [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(-1,1),(1,-1),(1,1)]:
            neighbor = (current[0]+dx, current[1]+dy)
            
            # 越界检测和障碍检测
            if not (0 <= neighbor[0] < len(grid) and 0 <= neighbor[1] < len(grid[0])):
                continue
            if grid[neighbor[0]][neighbor[1]] == 1:
                continue

            # 对角线移动代价为√2,取整简化计算
            tentative_g = g_score[current] + (1 if dx*dy==0 else 1.414)
            
            if tentative_g < g_score.get(neighbor, float('inf')):
                came_from[neighbor] = current
                g_score[neighbor] = tentative_g
                f_score = tentative_g + heuristic(neighbor, end)
                if not any(neighbor == item[1] for item in open_set.queue):
                    open_set.put((f_score, neighbor))

    return None  # 实在没路走了

这里有个骚操作——用PriorityQueue实现开放列表,比手动维护有序列表省心多了。启发函数我用的是曼哈顿距离(别问为啥不用欧式,问就是计算快):

def heuristic(a, b):
    # 对角线优化启发值 (Octile distance)
    dx = abs(a[0] - b[0])
    dy = abs(a[1] - b[1])
    return 1.414 * min(dx, dy) + abs(dx - dy)

调试时发现个坑:如果启发函数的权重过大,会导致"贪心"搜索,可能错过最优解。后来加了个系数平衡才解决。

可视化部分用了PyGame,这段代码能实时显示搜索过程:

def draw_grid(surface, grid, path=None):
    cell_size = 20
    for i in range(len(grid)):
        for j in range(len(grid[0])):
            color = (255,255,255)  # 默认白色空地
            if grid[i][j] == 1:
                color = (40,40,40)  # 障碍物深灰色
            if path and (i,j) in path:
                color = (0,255,0)   # 路径绿色
            pygame.draw.rect(surface, color, (j*cell_size, i*cell_size, cell_size-1, cell_size-1))

最后来个压轴测试——在房间地图中从(0,0)到(19,19),算法生成的路径居然完美绕过了所有墙体,还优先选择对角线移动缩短距离。看着绿色路径丝滑地穿过门洞,老父亲般的欣慰油然而生。

不同地图的实测数据:

  • 空地图:平均0.03秒完成
  • 随机障碍:约0.1-0.5秒(看脸)
  • 迷宫地图:稳定0.2秒左右
  • 棋盘地图:最长1秒(之字形走位太为难)
  • 房间地图:约0.15秒

想自己折腾的,记得处理边界情况——比如完全封闭的区域会导致无限循环,我加了超时退出机制(代码太丑就不放了)。下次准备试试JPS跳点优化,听说能让搜索速度快到飞起,等我有空继续折腾...

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐