A*算法详解【附算法代码与运行结果】

article/2025/8/15 3:32:00

算法背景

A*算法是一种在图形平面上,有多个路径中寻找一条从起始点到目标点的最短遍历路径的算法。它属于启发式搜索算法(Heuristic Search Algorithm),因为它使用启发式方法来计算图中的节点,从而减少实际计算的节点数量,提高搜索效率。


基本原理

A*算法的核心思想是结合了Dijkstra算法和Best-First-Search算法的特点。它使用一个估价函数f(n) = g(n) + h(n)来估算从起始点到目标点的总代价,其中g(n)表示从起始点到当前节点的代价,h(n)是从当前节点到目标点的启发式估计代价。

A*算法通过扩展路径代价最小的节点来寻找目标点,它保证了如果h(n)的估计值是准确的,那么找到的路径也是最短的。相比于Dijkstra算法中会一直遍历选择距离较近的点收录,在A*算法中增加启发式函数h(n)会减少收录的栅格数目,加快算法的导向终点的速度


算法流程

相比于Dijkstra算法,就多了一个启发式函数h(n)。

在实际应用中需权衡速度和最优性。启发式函数取的大的话可以快速找到终点。但可能会损失最优性,所以保证A*算法最优性需要加上下面这个条件,即启发项小于等于最优的那条路径:

h(n)<=*h(n)

open list表示存放从当前节点到节点已经更新过的路径;

closed list表示截至目前到起点最短路径的节点进行收录。

那么针对下面这个图的存储流程应如下:

open list:2(1+6)  3(5+0)  # 2(1+6)表示节点2到起点的距离为1,加上启发项为6

closed list:1(0)

open list:2(1+6)    

closed list:1(0)  3(5+0)  #实际上上面的路径理论上会更短,丧失了最优性


案例实操

对起点[-5,-5],终点 [50, 50]在边长范围为[-10,60]的正方形进行路径规划,其中设置了两个墙体作为障碍物,对比A*算法与Dijkstra算法。

【实验代码】


import mathimport matplotlib.pyplot as pltshow_animation = Trueclass AStarPlanner:def __init__(self, ox, oy, resolution, rr):"""Initialize grid map for a star planningox: x position list of Obstacles [m]oy: y position list of Obstacles [m]resolution: grid resolution [m]rr: robot radius[m]"""self.resolution = resolutionself.rr = rrself.min_x, self.min_y = 0, 0self.max_x, self.max_y = 0, 0self.obstacle_map = Noneself.x_width, self.y_width = 0, 0self.motion = self.get_motion_model()self.calc_obstacle_map(ox, oy)class Node:def __init__(self, x, y, cost, parent_index):self.x = x  # index of gridself.y = y  # index of gridself.cost = costself.parent_index = parent_indexdef __str__(self):return str(self.x) + "," + str(self.y) + "," + str(self.cost) + "," + str(self.parent_index)def planning(self, sx, sy, gx, gy):"""A star path searchinput:s_x: start x position [m]s_y: start y position [m]gx: goal x position [m]gy: goal y position [m]output:rx: x position list of the final pathry: y position list of the final path"""start_node = self.Node(self.calc_xy_index(sx, self.min_x),self.calc_xy_index(sy, self.min_y), 0.0, -1)goal_node = self.Node(self.calc_xy_index(gx, self.min_x),self.calc_xy_index(gy, self.min_y), 0.0, -1)open_set, closed_set = dict(), dict()open_set[self.calc_grid_index(start_node)] = start_nodewhile 1:if len(open_set) == 0:print("Open set is empty..")break# 选择扩展点 f(n) = g(n) 即cost+ h(n)即启发式函数calc_heuristic,计算goal_node终点与open_set里面的点c_id = min(open_set,key=lambda o: open_set[o].cost + self.calc_heuristic(goal_node,open_set[o]))current = open_set[c_id]# show graphif show_animation:  # pragma: no coverplt.plot(self.calc_grid_position(current.x, self.min_x),self.calc_grid_position(current.y, self.min_y), "xc")# for stopping simulation with the esc key.plt.gcf().canvas.mpl_connect('key_release_event',lambda event: [exit(0) if event.key == 'escape' else None])if len(closed_set.keys()) % 10 == 0:plt.pause(0.001)if current.x == goal_node.x and current.y == goal_node.y:print("Find goal")goal_node.parent_index = current.parent_indexgoal_node.cost = current.costbreak# Remove the item from the open setdel open_set[c_id]# Add it to the closed setclosed_set[c_id] = current# expand_grid search grid based on motion modelfor i, _ in enumerate(self.motion):node = self.Node(current.x + self.motion[i][0],current.y + self.motion[i][1],current.cost + self.motion[i][2], c_id)n_id = self.calc_grid_index(node)# If the node is not safe, do nothingif not self.verify_node(node):continueif n_id in closed_set:continueif n_id not in open_set:open_set[n_id] = node  # discovered a new nodeelse:if open_set[n_id].cost > node.cost:# This path is the best until now. record itopen_set[n_id] = noderx, ry = self.calc_final_path(goal_node, closed_set)return rx, rydef calc_final_path(self, goal_node, closed_set):# generate final courserx, ry = [self.calc_grid_position(goal_node.x, self.min_x)], [self.calc_grid_position(goal_node.y, self.min_y)]parent_index = goal_node.parent_indexwhile parent_index != -1:n = closed_set[parent_index]rx.append(self.calc_grid_position(n.x, self.min_x))ry.append(self.calc_grid_position(n.y, self.min_y))parent_index = n.parent_indexreturn rx, ry@staticmethoddef calc_heuristic(n1, n2):#计算两个点之间的距离w = 1.0  # 权重weight of heuristic,假设有多个启发式函数可以设置不同权重d = w * math.hypot(n1.x - n2.x, n1.y - n2.y)return ddef calc_grid_position(self, index, min_position):"""calc grid position:param index::param min_position::return:"""pos = index * self.resolution + min_positionreturn posdef calc_xy_index(self, position, min_pos):return round((position - min_pos) / self.resolution)def calc_grid_index(self, node):return node.y * self.x_width + node.xdef verify_node(self, node):px = self.calc_grid_position(node.x, self.min_x)py = self.calc_grid_position(node.y, self.min_y)if px < self.min_x:return Falseelif py < self.min_y:return Falseelif px >= self.max_x:return Falseelif py >= self.max_y:return False# collision checkif self.obstacle_map[node.x][node.y]:return Falsereturn Truedef calc_obstacle_map(self, ox, oy):self.min_x = round(min(ox))self.min_y = round(min(oy))self.max_x = round(max(ox))self.max_y = round(max(oy))print("min_x:", self.min_x)print("min_y:", self.min_y)print("max_x:", self.max_x)print("max_y:", self.max_y)self.x_width = round((self.max_x - self.min_x) / self.resolution)self.y_width = round((self.max_y - self.min_y) / self.resolution)print("x_width:", self.x_width)print("y_width:", self.y_width)# obstacle map generationself.obstacle_map = [[False for _ in range(self.y_width)]for _ in range(self.x_width)]for ix in range(self.x_width):x = self.calc_grid_position(ix, self.min_x)for iy in range(self.y_width):y = self.calc_grid_position(iy, self.min_y)for iox, ioy in zip(ox, oy):d = math.hypot(iox - x, ioy - y)if d <= self.rr:self.obstacle_map[ix][iy] = Truebreak@staticmethoddef get_motion_model():# dx, dy, costmotion = [[1, 0, 1],[0, 1, 1],[-1, 0, 1],[0, -1, 1],[-1, -1, math.sqrt(2)],[-1, 1, math.sqrt(2)],[1, -1, math.sqrt(2)],[1, 1, math.sqrt(2)]]return motiondef main():print(__file__ + " start!!")# start and goal positionsx = -5.0  # [m]sy = -5.0  # [m]gx = 50.0  # [m]gy = 50.0  # [m]grid_size = 2.0  # [m]robot_radius = 1.0  # [m]# set obstacle positionsox, oy = [], []for i in range(-10, 60):ox.append(i)oy.append(-10.0)for i in range(-10, 60):ox.append(60.0)oy.append(i)for i in range(-10, 61):ox.append(i)oy.append(60.0)for i in range(-10, 61):ox.append(-10.0)oy.append(i)#两堵墙for i in range(-10, 40):ox.append(20.0)oy.append(i)for i in range(0, 40):ox.append(40.0)oy.append(60.0 - i)if show_animation:  # pragma: no coverplt.plot(ox, oy, ".k")plt.plot(sx, sy, "og")plt.plot(gx, gy, "xb")plt.grid(True)plt.axis("equal")a_star = AStarPlanner(ox, oy, grid_size, robot_radius)rx, ry = a_star.planning(sx, sy, gx, gy)if show_animation:  # pragma: no coverplt.plot(rx, ry, "-r")plt.pause(0.001)plt.show()if __name__ == '__main__':main()

【运行结果】

】两种算法的比较

        A*算法显然比Dijkstra算法具有更快地收敛速度如果去掉障碍物会更加明显。

对比的Dijkstra算法可详见如下:

Dijkstra算法详解【附算法代码与运行结果】_dijkstra 算法代码讲解-CSDN博客


http://www.hkcw.cn/article/fxiASavPeO.shtml

相关文章

【leetcode】逐层探索:BFS求解最短路的原理与实践

前言 &#x1f31f;&#x1f31f;本期讲解关于力扣的几篇题解的详细介绍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…

七大排序算法深度解析:从原理到代码实现

1.排序 排序算法是计算机科学中最基础的技能之一&#xff0c;无论你是编程新手还是经验丰富的开发者&#xff0c;理解这些算法都能显著提升代码效率。本文将用最简单的方式&#xff0c;带你快速掌握七大经典排序算法的核心原理与实现。 1.1排序概念及其运用 排序是指将一组数据…

Python的情感词典情感分析和情绪计算

一.大连理工中文情感词典 情感分析 (Sentiment Analysis)和情绪分类 (Emotion Classification&#xff09;都是非常重要的文本挖掘手段。情感分析的基本流程如下图所示&#xff0c;通常包括&#xff1a; 自定义爬虫抓取文本信息&#xff1b;使用Jieba工具进行中文分词、词性标…

C++之vector类(超详细)

这节我们来学习一下&#xff0c;C中一个重要的工具——STL&#xff0c;这是C中自带的一个标准库&#xff0c;我们可以直接调用这个库中的函数或者容器&#xff0c;可以使效率大大提升。这节我们介绍STL中的vector。 文章目录 前言 一、标准库类型vector 二、vector的使用 2.…

C++ 面试题常用总结 详解(满足c++ 岗位必备,不定时更新)

&#x1f4da; 本文主要总结了一些常见的C面试题&#xff0c;主要涉及到语法基础、STL标准库、内存相关、类相关和其他辅助技能&#xff0c;掌握这些内容&#xff0c;基本上就满足C的岗位技能&#xff08;红色标记为重点内容&#xff09;&#xff0c;欢迎大家前来学习指正&…

『C++成长记』string模拟实现

🔥博客主页:小王又困了 📚系列专栏:C++ 🌟人之为学,不日近则日退 ❤️感谢大家点赞👍收藏⭐评论✍️ ​ 目录 一、存储结构 二、默认成员函数 📒2.1构造函数 📒2.2析构函数 📒2.3拷贝构造 📒2.4赋值重载 三、容量操作 📒3.1获取有效字符长度…

多态的使用和原理(c++详解)

一、多态的概念 多态顾名思义就是多种形态&#xff0c;它分为编译时的多态&#xff08;静态多态&#xff09;和运行时的多态&#xff08;动态多态&#xff09;&#xff0c;编译时多态&#xff08;静态多态&#xff09;就是函数重载&#xff0c;模板等&#xff0c;通过不同的参数…

C++ 底层实现细节隐藏全攻略:从简单到复杂的五种模式

目录标题 1 引言&#xff1a;为什么要“隐藏实现”1.1 头文件暴露带来的三大痛点1.2 ABI 稳定 vs API 兼容&#xff1a;先分清概念1.3 选型三问法——评估你到底要不要隐藏 2 模式一&#xff1a;直接按值成员 —— “裸奔”也能跑2.1 典型写法与最小示例2.2 何时按值最合适&…

使用国内镜像网站在线下载安装Qt(解决官网慢的问题)——Qt

国内镜像网站 中国科学技术大学&#xff1a;http://mirrors.ustc.edu.cn/qtproject/清华大学&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/qt/北京理工大学&#xff1a;http://mirror.bit.edu.cn/qtproject/ 南京大学&#xff1a;https://mirror.nju.edu.cn/qt腾讯镜像&…

超全超详细!JDK 安装及环境配置(Java SE 8 保姆级教程)

一、JDK 简介 JDK&#xff08;Java Development Kit&#xff09;是用于开发 Java 程序的工具包&#xff0c;包括编译器 javac、Java 运行环境&#xff08;JRE&#xff09;以及各种开发工具。安装和配置 JDK 是学习和使用 Java 编程的第一步&#xff0c;以下是 Java 和 JDK 的具…

Java 大视界 -- 基于 Java 的大数据分布式数据库在社交网络数据存储与查询中的架构设计与性能优化(225)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

C++协程从入门到精通

文章目录 一、C协程入门知识&#xff08;一&#xff09;基本概念&#xff08;二&#xff09;特点&#xff08;三&#xff09;应用场景 二、C协程精通知识&#xff08;一&#xff09;高级特性&#xff08;二&#xff09;优化技巧&#xff08;三&#xff09;错误处理机制&#xf…

蓝桥杯第十六届c组c++题目及个人理解

本篇文章只是部分题目的理解&#xff0c;代码和思路仅供参考&#xff0c;切勿当成正确答案&#xff0c;欢迎各位小伙伴在评论区与博主交流&#xff01; 目录 题目&#xff1a;2025 题目解析 核心提取 代码展示 题目&#xff1a;数位倍数 题目解析 核心提取 代码展示 …

C++日新月异的未来代码:C++11(上)

文章目录 1.统一的列表初始化1.1 普通{ }初始化1.2 initializer_list 2.声明2.1 auto、nullptr2.2 decltype 3.左值右值3.1 概念3.2 左值引用与右值引用比较3.3 左值引用与右值引用的应用3.4 完美转发 希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动力&#xf…

C++从入门到实战(十二)详细讲解C++如何实现内存管理

C从入门到实战&#xff08;十二&#xff09;详细讲解C如何实现内存管理 前言一、C内存管理方式1. new/delete操作内置类型2. 异常与内存管理的联系&#xff08;简单了解&#xff09;3. new和delete操作自定义类型 二、 operator new与operator delete函数&#xff08;重点&…

【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细)

文章目录 【2025年最新版】Java JDK安装、环境配置教程 &#xff08;图文非常详细&#xff09;1. JDK介绍2. 下载 JDK3. 安装 JDK4. 配置环境变量5. 验证安装6. 创建并测试简单的 Java 程序6.1 创建 Java 程序&#xff1a;6.2 编译和运行程序&#xff1a;6.3 在显示或更改文件的…

【Linux系统】从 C 语言文件操作到系统调用的核心原理

文章目录 前言lesson 15_基础IO一、共识原理二、回顾C语言接口2.1 文件的打开操作2.2 文件的读取与写入操作2.3 三个标准输入输出流 三、过渡到系统&#xff0c;认识文件系统调用3.1 open 系统调用1. 比特位标志位示例 3.2 write 系统调用1. 模拟实现 w 选项2. 模拟实现 a 选项…

JavaSwing之--JTextField

JavaSwing之–JTextField JTextField 是一个允许编辑单行文本的轻量级组件&#xff0c;它提供了一系列的构造方法和常用方法用来编写可以存储文本的文本框满足程序功能的需求。 以下在简要介绍常用构造方法、普通方法后详解各种方法的应用及举例。 一、构造方法 方法名称功…

Windows系统之VHD安装

环境准备 工具说明Dism部署系统、提取和转换系统镜像等等&#xff0c;还有很多功能大家可以自行探索。这里只用到Dism的部署系统功能。 Releases Chuyu-Team/Dism-Multi-language GitHubbcdedit.exe自带工具 C:\Windows\System32\bcdedit.exe 创建虚拟磁盘 首先右键点击我…

解决Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field ‘com.sun.tools.javac.tre

问题描述 在更新自建基础项目过程中&#xff0c;compile、install报错。 Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field com.sun.tools.javac.tree.JCTree qualid 解决方案 问题原因是Lombok &#xff0c;与 JDK 21 兼容的最低 Lombok 版本是…