人工智能-A*算法-最优路径搜索实验

上次学会了《A*算法-八数码问题》,初步了解了A*算法的原理,本次再用A*算法完成一个最优路径搜索实验。

一、实验内容
1. 设计自己的启发式函数。
2. 在网格地图中,设计部分障碍物。
3. 实现A*算法,搜索一条最优路径。

二、A*算法实现步骤

1. 初始化:设置起始节点和目标节点,并创建一个open列表和一个closed列表。open列表用于存储待检查的节点,closed列表用于存储已经检查过的节点。
2. 开始循环,直到open列表为空:
  2.1. 从open列表中取出f值最小的节点(这个值是由g值(从起始节点到当前节点的实际距离)和h值(从当前节点到目标节点的启发式估计距离)之和组成的)。
  2.2. 如果这个节点是目标节点,那么就找到了最优路径,结束循环。
  2.3. 否则,将这个节点从open列表移动到closed列表,并检查它的所有邻居节点。
  2.4. 对于每一个邻居节点:
    i. 如果它已经在closed列表中,那么跳过它。
    ii. 如果它不在open列表中,那么添加它,并计算它的g值和f值。
    iii. 如果它已经在open列表中,那么比较g值:如果当前节点有更小的g值,那么更新它的g值和f值。
3. 如果open列表为空(没有找到路径),那么算法失败。
4. 返回从起始节点到目标节点的最短路径。

三、启发式估算距离方法

1, 曼哈顿距离计算公式

2, 欧几里得距离计算公式

 

3, 定义启发式函数

# 曼哈顿距离计算函数(上下左右4个方向移动)
def manhattan_distance(pos1, pos2):# 曼哈顿距离计算; D = |X1-X2|+|Y1-Y2| ;两点X,Y值差的绝对值和return abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1])# 欧几里得距离的计算函数(上下左右及其对角线8个方向移动)
# 相对来说,欧几里得距离计算函数更准确点
def euclidean_distance(pos1, pos2):# 欧几里得距离计算; D = 开平方根( (X1-X2)^2 + (Y1-Y2)^2 ) ;两点X,Y值差的平方和,再开平方根,即2点间的绝对距离。return math.sqrt((pos1[0] - pos2[0]) ** 2 + (pos1[1] - pos2[1]) ** 2)# 定义启发式函数,这里只使用欧几里得距离作为估价值
def heuristic(pos1, pos2):return euclidean_distance(pos1, pos2)

四、A*算法寻找最优路径

1,根据欧几里得距离计算寻找最优路径示例图

2,A*算法寻找最优路径方法

# 定义A*算法
def a_star_path(array, start, goal):# 所有可能的移动方向,包括对角线方向neighbors = [(0, 1), (0, -1), (1, 0), (-1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]# 只考虑上下左右移动方向,不包括对角线方向# neighbors = [(0, 1), (0, -1), (1, 0), (-1, 0)]'''计算每个节点的总评分f值(f(n) = g(n) + h(n))来寻找最优路径g值(从起始节点到下一个移动方向节点的实际距离)h值(从下一个移动方向到目标节点的估计距离,使用启发式heuristic函数计算)'''# 起点的g,f初始值g0 = 0h0 = heuristic(start, goal)gscore = {start: g0}fscore = {start: g0 + h0}# 定义一个优先队列,open列表,用于存储待检查的节点,open列表的优先级由f值决定open_list = []# 关闭列表(表示已经访问过),用于存储已经检查过的节点,以避免重复检查close_set = set()# 从哪里来字典,记录节点来源,当成父节点come_from = {}'''heapq实现了一个适合与Python的列表一起使用的最小堆排序算法。heapq.heappush(),创建一个堆;当数据源添加新项时,将维护元素的堆排序顺序。heapq.heappop(),弹出并返回堆中的最小项。将起点加入open列表,fscore[start]为起点的优先级f值,即启发式函数估算值'''# 将起点加入open列表,fscore[start]为起点的优先级f值,即启发式函数估算值heapq.heappush(open_list, (fscore[start], start))# 遍历open列表while open_list:# 取出f值最小的节点作为当前节点current = heapq.heappop(open_list)[1]# 如果到达目标点,返回路径if current == goal:data = []# 从目标点向起点遍历路径while current in come_from:# 将当前点的位置加入路径data.append(current)# 将当前点设为从哪里来的节点,继续向上遍历current = come_from[current]# 将起始点的位置也加入路径data.append(start)# 将路径反转,因为我们是从目标向起点遍历的,所以需要反转得到真正的路径return data[::-1]# 将当前节点加入closed列表,表示已经检查过该节点了close_set.add(current)# 对当前节点的所有移动方向进行遍历for i, j in neighbors:# 根据当前节点的位置计算邻居节点的位置neighbor = current[0] + i, current[1] + j# 从当前节点到邻居节点的距离,作为 tentative_g_score 值(暂时的g值)tentative_g_score = gscore[current] + heuristic(current, neighbor)# 如果邻居节点在地图之外,跳过该邻居节点if 0 <= neighbor[0] < array.shape[0]:if 0 <= neighbor[1] < array.shape[1]:# 如果邻居节点是障碍物,跳过该邻居节点if array[neighbor[0]][neighbor[1]] == 1:continueelse:continueelse:continue'''如果邻居节点在closed列表中,并且从当前节点到邻居节点的距离比之前计算的距离长,则跳过该邻居节点'''if neighbor in close_set and tentative_g_score >= gscore.get(neighbor, 0):continue'''如果从当前节点到邻居节点的距离比之前计算的更短,或者邻居节点不在open列表中的话更新邻居节点的g值和f值,并将邻居节点重新加入open列表中(因为g值发生了变化)'''if tentative_g_score < gscore.get(neighbor, 0) or neighbor not in [i[1] for i in open_list]:come_from[neighbor] = current#''' 更新邻居节点的g值和f值计算每个节点的总评分f值(f(n) = g(n) + h(n))来寻找最优路径g值(从起始节点到下一个移动方向节点的实际距离)h值(从下一个移动方向到目标节点的估计距离,使用启发式heuristic函数计算)'''gscore[neighbor] = tentative_g_scoreh_score = heuristic(neighbor, goal)fscore[neighbor] = tentative_g_score + h_score# 将邻居节点重新加入open列表中heapq.heappush(open_list, (fscore[neighbor], neighbor))return False

 3,绘制网格地图和障碍物

# 定义网格矩阵长宽
map_size = (20, 20)
# 设置起点和终点
start = (0, 0)
end = (map_size[0] - 1, map_size[1] - 1)# 随机生成网格矩阵障碍物位置
def generate_obstacles(map_size):# 生成障碍物位置个数在10到20之间的随机数num_obstacles = random.randint(map_size[0], (map_size[0] / 4) * (map_size[1] / 4))# 生成所有可能的位置all_positions = [(i, j) for i in range(map_size[0]) for j in range(map_size[1])]# print(all_positions)# 去除起始点all_positions.remove(start)all_positions.remove(end)# 从所有可能的位置中随机选择一些位置作为障碍物的位置obstacles = random.sample(all_positions, num_obstacles)# print(obstacles)return obstacles# 随机生成障碍物位置列表
obstacles = generate_obstacles(map_size)# 定义屏幕一个格子大小
CELL_SIZE = 20
# 定义屏幕宽高大小
WIDTH, HEIGHT = map_size[0] * CELL_SIZE, map_size[1] * CELL_SIZE# 定义颜色
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
BLACK = (0, 0, 0)# 初始化网格地图
def grid_init(map_size, obstacles):# 创建网格地图grid = [[0 for x in range(map_size[0])] for y in range(map_size[1])]for x, y in obstacles:# 设置障碍物grid[x][y] = 1return grid# 打印网格地图
def grid_print(grid):for line in grid:print(line)# 绘制主地图
def draw_grid(pygame, screen):for i in range(0, WIDTH, CELL_SIZE):pygame.draw.line(screen, BLACK, (i, 0), (i, HEIGHT))for i in range(0, HEIGHT, CELL_SIZE):pygame.draw.line(screen, BLACK, (0, i), (WIDTH, i))for obs in obstacles:# 设置矩形的位置和大小rect_pos = (obs[1] * CELL_SIZE, obs[0] * CELL_SIZE)rect_size = (CELL_SIZE, CELL_SIZE)# 使用pygame.draw.rect绘制矩形pygame.draw.rect(screen, BLUE, (rect_pos[0], rect_pos[1], rect_size[0], rect_size[1]))# 绘制A*算法找到的路径
def draw_a_star_path():print("绘制A*算法找到的路径地图:")# 初始化 Pygamepygame.init()# 创建一个窗口(屏幕)对象screen = pygame.display.set_mode((WIDTH, HEIGHT))# 窗口描述pygame.display.set_caption("A星算法动画演示")# 定义字体font = pygame.font.Font(None, 30)# 填充屏幕背景为白色screen.fill(WHITE)# 绘制地图draw_grid(pygame, screen)# 更新显示屏幕pygame.display.flip()# 初始化网格地图grid = grid_init(map_size, obstacles)print("初始化网格地图:")# 打印网格地图grid_print(grid)# 设置起点和终点# start = (0, 0)# end = (map_size[0] - 1, map_size[1] - 1)# 执行A*算法path = a_star_path(np.array(grid), start, end)print("最优路径为:")print(path)# 循环刷新地图,显示最优路径for node in path:# 设置矩形的位置和大小rect_pos = (node[1] * CELL_SIZE, node[0] * CELL_SIZE)rect_size = (CELL_SIZE, CELL_SIZE)# 使用pygame.draw.rect绘制矩形pygame.draw.rect(screen, RED, (rect_pos[0], rect_pos[1], rect_size[0], rect_size[1]))# 更新显示屏幕pygame.display.flip()# 间隔0.5秒刷新地图time.sleep(0.5)# 退出 Pygamepygame.quit()if __name__ == "__main__":draw_a_star_path()

五、A*算法寻找最优路径-运行效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/282700.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT自带打包问题:无法定位程序输入点?metaobject@qsound

文章目录 无法定位程序输入点?metaobjectqsound……检查系统环境变量的配置&#xff1a;打包无须安装qt的文件 无法定位程序输入点?metaobjectqsound…… 在执行release打包程序后&#xff0c;相应的release文件夹下的exe文件&#xff0c;无法打开 如有错误欢迎指出 检查系…

Mysql存储引擎-InnoDB

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…

ARS430毫米波雷达标定步骤

工具准备&#xff1a;CANoe&#xff0c; 标定工程文件&#xff0c;雷达标定板&#xff0c;三脚架&#xff0c;激光器&#xff0c;平口钳&#xff0c;气泡水平仪&#xff0c;小镜子&#xff0c;双面胶。 将车辆放置在车辆前方至少有20米空白视野的场地上。使用气泡水平仪大概使…

JUC并发编程 06——Synchronized与锁升级

一.Java对象内存布局和对象头 在HotSpot虚拟机里&#xff0c;对象在堆内存中的存储布局可以划分为三个部分&#xff1a;对象头(Header) 、实例数据 (Instance Data) 和 对文填充 (Padding)。 对象内部结构分为&#xff1a;对象头、实例数据、对齐填充&#xff08;保证8个字节的…

HashMap构造函数解析与应用场景

目录 1. HashMap简介 2. HashMap的构造函数 2.1 默认构造函数 2.2 指定初始容量和加载因子的构造函数 3. 构造函数参数的影响 3.1 初始容量的选择 3.2 加载因子的选择 4. 构造函数的应用场景 4.1 默认构造函数的应用场景 4.2 指定初始容量和加载因子的构造函数的应用…

Ubuntu 常用命令之 cp 命令用法介绍

cp命令在Ubuntu系统中用于复制文件或目录。它的基本格式是cp [选项] 源文件或目录 目标文件或目录。 以下是一些常用的cp命令选项 -i&#xff1a;在覆盖目标文件之前将给出提示。-r或-R&#xff1a;递归复制&#xff0c;用于目录的复制操作。-v&#xff1a;详细模式&#xff…

EasyRecovery2024功能强大的mac苹果数据恢复软件

Ontrack EasyRecovery2024是一款功能强大的数据恢复软件&#xff0c;它可以帮助用户从各种存储设备中恢复丢失或删除的数据。它支持多种文件系统和文件类型&#xff0c;可以恢复包括照片、视频、音频、文档、电子邮件和归档文件等不同类型的数据。 Ontrack EasyRecovery 是一款…

【i.MX6ULL】使用buildroot构建根文件系统

文章目录 前言1、下载源码2、构建根文件系统1. 配置buildroot2. 编译buildroot3. buildroot根文件系统测试 3、buildroot第三方软件和库的配置1. 使能 alsa-lib2. 使能 alsa-utils 4、 buildroot下的busybox配置1. busybox配置2. 使能常用命令 前言 本篇文章时参考的正点原子的…

计算机操作系统-第十八天

目录 进程调度时机 补充知识 进程调度的方式 非剥夺调度方式 剥夺调度方式 进程的切换与过程 本节思维导图 进程调度时机 进程调度&#xff08;低级调度&#xff09;&#xff0c;即按照某种算法从就绪队列中选择一个进程为其分配处理机。 共有两种需要进行进程调度与…

Leetcode刷题笔记题解(C++):224. 基本计算器

思路&#xff1a; step 1&#xff1a;使用栈辅助处理优先级&#xff0c;默认符号为加号。 step 2&#xff1a;遍历字符串&#xff0c;遇到数字&#xff0c;则将连续的数字字符部分转化为int型数字。 step 3&#xff1a;遇到左括号&#xff0c;则将括号后的部分送入递归&#x…

【LeetCode刷题-树】-- 156.上下翻转二叉树

156.上下翻转二叉树 方法&#xff1a;自底向上迭代 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, Tre…

蜂鸣器驱动电路设计原理图讲解

以下介绍的几种蜂鸣器驱动电路是针对单片机I/O口的驱动电路&#xff0c;适用于现行的压电式蜂鸣器。 压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。 当接通电源后&#xff08;1.5~15V直流工作电压&#xff09;,多谐振荡器起振,输出1.5~2.5kHZ…