A星算法(A* A Star algorithm)原理以及代码实例,超详细,超简单,大白话谁都能看懂

本文以这篇博主的文章为基础【精选】A*算法(超级详细讲解,附有举例的详细手写步骤)-CSDN博客

这篇文章的博主做了一个UI界面,但我感觉,这样对新手关注算法和代码本身反而不利,会被界面的代码所干扰。所以笔者在他的基础上舍去了界面的内容,只关注算法本身。

A星算法的作用就是:已知起点和终点坐标,将地图离散化为网格,可以使用A star算法寻路。

A star算法简单来说三个步骤:一是准备两个列表,开列表和闭列表,开列表将节点移进移出,闭列表只进不出。二是在每一步探索的时候,计算三个“花费”,起点走到当前点的实际花费 G,从当前点到目标点的预估花费 H, 总花费F = G + H。三是计算父节点,父节点的作用是推算花费以及到达终点后倒推路径。

具体来说,我们称现在所处的网格为 checking point k检查checking point k周围所有的下一步可到达点并『临时』将这些可到达点的父节点记为checking point k。可到达点是没有障碍物且不在闭列表中的网格(near point 1, near point 2, ......, near point i, ......, near point n),对于near point i,计算起点到near point i的实际花费:

起点到near point i的实际花费 = 起点到checking point k的实际花费 + checking point k到near point i的实际花费。

计算near point i到终点的预估花费:

near point i到终点的预估花费 = near point i到终点的曼哈顿距离。

near point i的总花费 = 实际花费 + 预估花费。这里只是“花费“的一种定义形式,也可以用其他的定义形式。

每个点都是我们建立的点类的类实例,在点类中储存这三个花费,以及储存父节点

如果near point i不在开列表中,将其加进去。注意我们前面『临时』标记的父节点,这里正式标记checking point k为父节点

如果near point i已经在开列表中,则说明,near point i在我们到达checking point k之前,处于另一个checking point j时,作为checking point j的可到达点被计算过了。这里我们比较一下near point i旧的总花费和新的总花费,如果新的总花费小,则更新花费,并把near point i的父节点更新为checking point k;如果旧的总花费小,则保持旧的花费,以及保持旧的父节点。

当checking point k的所有可到达点都被检查完后,将其移进闭列表

之后,从开列表中选一个总花费最小的点作为新的checking point,重复上述的检查可达点操作,直到找到终点,起始时,将起点加入开列表,如果在途中开列表空了,则不存在可达路径。

完整代码如下:

import numpy as np
import json
import matplotlib.pyplot as plt


class Map:
    def __init__(self):
        # 设置起点,终点所在的行列数,左上角为0,0
        start_row = 17
        start_col = 2
        end_row = 9
        end_col = 16
        with open('map.txt', 'r') as f:
            my_map = json.loads(f.read())

        my_map = np.array(my_map)
        self.map = np.where(my_map == 0, 1, np.where(my_map == 1, 0, my_map))
        self.map[start_row, start_col] = -100   # 起点
        self.map[end_row, end_col] = 100        # 终点
        # self.map = np.array([[1, 1, 1, 1, 0, 100],
        #                      [1, 0, 0, 1, 0, 1],
        #                      [1, -100, 0, 1, 0, 1],
        #                      [1, 1, 0, 1, 0, 1],
        #                      [1, 1, 1, 1, 1, 1]])

    def get_start_point(self):
        indices = np.where(self.map == -100)
        return indices[0][0], indices[1][0]

    def get_end_point(self):
        indices = np.where(self.map == 100)
        return indices[0][0], indices[1][0]

    def check_grid(self, point):
        return self.map[point.x, point.y]


class Point:
    def __init__(self, x_, y_):
        self.x = x_
        self.y = y_
        self.father = None
        self.G = 0  # 起点到当前节点所花费的消耗
        self.H = 0  # 到终点的预估消耗
        self.F = 0

    def get_x_y(self):
        return self.x, self.y

    def set_GHF(self, G, H, F):
        self.H = H
        self.G = G
        self.F = F


class Astar:
    def __init__(self):
        self.openlist = []
        self.closelist = []
        self.map = Map()
        self.start_x, self.start_y = self.map.get_start_point()
        self.start_position = None
        self.end_x, self.end_y = self.map.get_end_point()
        self.find_path = False
        self.path = []

    def cal_GHF(self, checkpoint):
        if checkpoint.father is not None:
            G = checkpoint.father.G + 1   # 起点到父节点的花费加上父节点到本节点的花费
        else:
            G = 0
        H = abs(checkpoint.x - self.end_x) + abs(checkpoint.y - self.end_y)
        F = G + H
        return G, H, F

    def add_near_point(self, check_point):
        x, y = check_point.get_x_y()
        tmp_list = [Point(x-1, y-1), Point(x-1, y), Point(x-1, y+1),
                    Point(x, y-1), Point(x, y+1),
                    Point(x+1, y-1), Point(x+1, y), Point(x+1, y+1)]
        near_list = []
        for pi in tmp_list:
            if self.map.map.shape[0] > pi.x >= 0 and self.map.map.shape[1] > pi.y >= 0:     # 在地图范围内
                if self.map.check_grid(pi) == 100:
                    return [pi]
                elif self.map.check_grid(pi) == 1 and self.not_in_closelist(pi):
                    near_list.append(pi)

        return near_list

    def choose_min_F_point(self):
        minF = 1e10
        choosed_point = None
        for pi in self.openlist:
            if pi.F < minF:
                minF = pi.F
                choosed_point = pi
        return choosed_point

    def not_in_openlist(self, pi):
        not_in = True
        for pii in self.openlist:
            if pii.x == pi.x and pii.y == pi.y:
                not_in = False
        return not_in

    def not_in_closelist(self, pi):
        not_in = True
        for pii in self.closelist:
            if pii.x == pi.x and pii.y == pi.y:
                not_in = False
        return not_in

    def run(self):
        self.start_position = Point(self.start_x, self.start_y)
        G, H, F = self.cal_GHF(self.start_position)
        self.start_position.set_GHF(G, H, F)
        self.openlist.append(self.start_position)

        while True:
            checking_point = self.choose_min_F_point()
            if checking_point is None or self.find_path:
                print("End!")
                break
            self.openlist.remove(checking_point)
            self.closelist.append(checking_point)
            near_list = self.add_near_point(checking_point)
            for pi in near_list:
                if self.map.check_grid(pi) == 100:
                    self.find_path = True
                    # print("find path:\n{}".format(checking_point.get_x_y()))
                    self.path.append([checking_point.get_x_y()[0], checking_point.get_x_y()[1]])
                    reverse_point_father = checking_point.father
                    while reverse_point_father.father is not None:
                        # print(reverse_point_father.get_x_y())
                        self.path.append([reverse_point_father.get_x_y()[0], reverse_point_father.get_x_y()[1]])
                        reverse_point_father = reverse_point_father.father
                    break

                if self.not_in_openlist(pi):
                    pi.father = checking_point
                    G, H, F = self.cal_GHF(pi)
                    pi.set_GHF(G, H, F)
                    self.openlist.append(pi)
                else:
                    G_old = pi.G + 1
                    G_new = checking_point.G + 1
                    if G_new < G_old:
                        pi.father = checking_point
                        G, H, F = self.cal_GHF(pi)
                        pi.set_GHF(G, H, F)

        # 打印路性
        print("path: ")
        print(self.path)
        self.path = self.path[::-1]
        with open('my_path.txt', 'w') as file:
            for item in self.path:
                file.write(str(item) + '\n')

    def check(self):
        for pi in self.path:
            self.map.map[pi[0], pi[1]] = 2

        fig = plt.figure("A Star Algorithm")

        cmap = plt.cm.colors.ListedColormap(['yellow', 'black', 'white', 'blue', 'green'])

        # 创建一个离散的归一化器,根据不同数值映射到不同颜色
        bounds = [-101, -99, 0, 1, 2, 99, 101]
        norm = plt.cm.colors.BoundaryNorm(bounds, cmap.N)

        # 显示二维数组
        plt.imshow(self.map.map, cmap=cmap, norm=norm)

        # 添加颜色条,以便查看数值与颜色的对应关系
        cb = plt.colorbar()

        # 显示图
        plt.show()


if __name__ == "__main__":
    astar = Astar()
    astar.run()
    astar.check()

使用我的代码前,可以先运行文章开头提到的那个博主的代码,生成一个地图保存,我的代码加载他的地图(也可以使用我注释掉的那个地图),我的地图中,1表示可行网格,0表示障碍物网格。如果我们足够幸运的话,我们两篇文章的结果应该是一致的。

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux - 进程的优先级 和 如何使用优先级调度进程

理解linux 当中如何做到 把一个PCB 放到多个 数据结构当中 在Linux 当中&#xff0c;一个进程的 PCB 不会仅仅值存在一个 数据结构当中&#xff0c;他既可以在 某一个队列当中&#xff0c;又可以在 一个 多叉树当中。 队列比如 cpu 的 运行队列&#xff0c;键盘的阻塞队列等等…

git教程(1)---本地仓库操作

git教程 git安装-Centos基本操作git initgit config工作区和版本库工作区暂存区/索引版本库 添加文件---场景一git statusgit log查看.git目录结构 添加文件---场景二修改文件版本回退撤销修改场景一只有工作区有code工作区和暂存区有code所有区域都有code并且没有push到远程仓…

实体店做商城小程序如何

互联网电商深入各个行业&#xff0c;传统线下店商家无论产品销售还是服务业&#xff0c;仅靠以往的经营模式&#xff0c;很难拓展到客户&#xff0c;老客流失严重&#xff0c;同时渠道单一&#xff0c;无法实现外地客户购物及线上客户赋能等。 入驻第三方平台有优势但也有不足…

大数据采集技术与预处理学习一:大数据概念、数据预处理、网络数据采集

目录 大数据概念&#xff1a; 1.数据采集过程中会采集哪些类型的数据&#xff1f; 2.非结构化数据采集的特点是什么&#xff1f; 3.请阐述传统的数据采集与大数据采集的区别&#xff1f; ​​​​​​​ ​​​​​​​4.大数据采集的数据源有哪些&#xff1f;针对不同的数…

理解V3中的proxy和reflect

现有如下面试题 结合GeexCode和Gpt // 这个函数名为onWatch&#xff0c;接受三个参数obj、setBind和getlogger。 // obj是需要进行监视的对象。 // setBind是一个回调函数&#xff0c;用于在设置属性时进行绑定操作。 // getlogger是一个回调函数&#xff0c;用于在获取属性时…

C# 压缩图片

.net下跨平台图像处理 https://github.com/mono/SkiaSharp 安装包 skiasharp 效果 代码 ImageCompression.cs using SkiaSharp;namespace ImageCompressStu01 {/// <summary>/// 图片压缩/// </summary>public class ImageCompression{/// <summary>/…

在spring boot+vue项目中@CrossOrigin 配置了允许跨域但是依然报错跨域,解决跨域请求的一次残酷经历

首先&#xff0c;说一下我们的项目情况&#xff0c;我们项目中后端有一个过滤器&#xff0c;如果必须要登录的接口路径会被拦下来检查&#xff0c;前端要传一个token&#xff0c;然后后端根据这个token来判断redis中这个用户是否已经登录。 if (request.getMethod().equals(&qu…

深入了解 Elasticsearch 8.1 中的 Script 使用

一、什么是 Elasticsearch Script&#xff1f; Elasticsearch 中的 Script 是一种灵活的方式&#xff0c;允许用户在查询、聚合和更新文档时执行自定义的脚本。这些脚本可以用来动态计算字段值、修改查询行为、执行复杂的条件逻辑等等。 二、支持的脚本语言有哪些 支持多种脚本…

数据分享 I 地级市人口和土地使用面积基本情况

数据地址&#xff1a; 地级市人口和土地使用面积基本情况https://www.xcitybox.com/datamarketview/#/Productpage?id394 基本信息. 数据名称: 地级市人口和土地使用面积基本情况 数据格式: ShpExcel 数据时间: 2021年 数据几何类型: 面 数据坐标系: WGS84坐标系 数据…

oracle19c配置驱动

1.遇到的问题 下载jar包 https://www.oracle.com/database/technologies/appdev/jdbc-ucp-19c-downloads.html 执行命令 mvn install:install-file -DgroupIdcom.oracle -DartifactIdojdbc19 -Dversion19.3.0.0 -Dpackagingjar -Dfileojdbc8.jar2.配置驱动 # 数据源配置 data…

lua-web-utils和proxy设置示例

以下是一个使用lua-web-utils和proxy的下载器程序&#xff1a; -- 首先安装lua-web-utils库 local lwu require "lwu" ​ -- 获取服务器 local function get_proxy()local proxy_url "duoipget_proxy"local resp, code, headers, err lwu.fetch(proxy_…

基于jquery+html开发的json格式校验工具

json简介 JSON是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式&#xff0c;但是也使用了类似于C语言家族…

Django分页功能的使用和自定义分装

1. 在settings中进行注册 # drf配置 REST_FRAMEWORK {DEFAULT_AUTHENTICATION_CLASSES: (# rest_framework_jwt.authentication.JSONWebTokenAuthentication,rest_framework_simplejwt.authentication.JWTAuthentication,rest_framework.authentication.SessionAuthenticatio…

【Opencv4快速入门】轮廓检测findContours

7.2 轮廓检测findContours 7.2.1 轮廓查找findContours7.2.2 轮廓绘制drawContours图像轮廓是指图像中对象的边界,是图像目标的外部特征,这个特征对于图像分析、目标识别和理解更深层次的含义具有重要的作用。 7.2.1 轮廓查找findContours 图像的轮廓补单能够提供物体的边缘,…

【ubuntu】 Linux(ubuntu)创建python的虚拟环境

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

protobuf对象与JSON相互转换

除了之前的 protobuf-java依赖之外&#xff0c;还需要引入 protobuf-java-uti 依赖&#xff1a; <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java --><dependency><groupId>com.google.protobuf</groupId><artifactId&…

数据结构和算法——用C语言实现所有排序算法

文章目录 前言排序算法的基本概念内部排序插入排序直接插入排序折半插入排序希尔排序 交换排序冒泡排序快速排序 选择排序简单选择排序堆排序 归并排序基数排序 外部排序多路归并败者树置换——选择排序最佳归并树 前言 本文所有代码均在仓库中&#xff0c;这是一个完整的由纯…

ICLR 2023丨3DSQA:3D 场景中的情景问答

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://arxiv.org/pdf/2210.07474.pdf 主页链接&#xff1a;http://sqa3d.github.io 图 1&#xff1a;3D 场景中情景问答 (SQA3D) 的任务图示。给定场景上下文 S&#xff08;例如&#…

FileInputStream文件字节输入流

一.概念 以内存为基准&#xff0c;把磁盘文件中的数据以字节形式读入内存中 二.构造器 public FileInputStream(File file) public FileInputStream(String pathname) 这两个都是创建字节输入流管道与源文件接通 三.方法 public int read() :每次读取一个字节返回&#xff0c;如…

计算机网络基础二

课程目标 了解 OSI 七层模型分层结构 了解 TCP/IP 协议簇四层模型分层结构 能够说出 TCP/IP 协议簇中 运输层、网络层和数据链路 层常见的 相关协议 能够说出 TCP/IP 的三次握手四次断开过程 了解 Vmware 的三种网络模式 能够使用客户端工具连接虚拟机 掌握主机名、 DNS…
最新文章