路径规划——A*算法

news2024/11/15 13:28:23

路径规划——A*算法

算法原理

为了解决Dijkstra算法效率低的问题,A Star 算法作为一种启发式算法被提出。该算法在广度优先的基础上加入了一个估价函数。如BFS和常规方法如Dijsktra算法结合在一起的算法,有点不同的是,类似BFS的启发式经常给出一个近似解而不是保证最佳解,A Star是路径搜索中最受欢迎的选择,它相当灵活,并且能用于多种多样的情理之中,A Star 潜在的搜索一个很大的区域,A Star算法能用于搜索最短路径,A Star 能用启发式函数引发它自己,在简单的情况中,它和BFS一样快。
BFS算法:Best First Search
Dijkstra算法:Dijkstra

1968年,启发式方法提出了A* ,其中包括将已知区域划分为单个单元,并计算每个单元达到目标的总成本。给定从起点到当前分析像元的总距离以及从分析像元到目标的距离,总路径成本使用以下公式计算:
F ( n ) = G ( n ) + H ( n ) , ( 1 ) F ( n ) 是总路径开销,也是节点 n 的综合优先级。每次选择下一个要访问的节点时, 总是选取综合优先级最高的节点,也就是总开销最小的节点; G ( n ) 是从起点到达指定单元的成本和; H ( n ) 是从指定单元到达目标的预估成本,也是 A ∗ 算法的启发函数。 F(n)=G(n)+H(n), \quad(1)\\ F(n)是总路径开销,也是节点n的综合优先级。每次选择下一个要访问的节点时,\\总是选取综合优先级最高的节点,也就是总开销最小的节点;\\ G(n)是从起点到达指定单元的成本和;\\ H(n)是从指定单元到达目标的预估成本,也是A^*算法的启发函数。 F(n)=G(n)+H(n),(1)F(n)是总路径开销,也是节点n的综合优先级。每次选择下一个要访问的节点时,总是选取综合优先级最高的节点,也就是总开销最小的节点;G(n)是从起点到达指定单元的成本和;H(n)是从指定单元到达目标的预估成本,也是A算法的启发函数。

  • start:路径规划的起始点
  • goal:路径规划的终点
  • g_score:从当前点(current_node)到出发点(start)的移动代价
  • h_score:不考虑障碍物,从当前点(current_node)到目标点的估计移动代价
  • f_score:f_score=g_score+h_score
  • openlist:寻找过程中的待检索节点列表
  • closelist:不需要再次检索的节点列表
  • came_Form:存储父子节点关系的列表,用于回溯最优路径,非必须,也可以通过节点指针实现

A*算法的核心就是上式,在运算过程中,每次从优先队列中选取F(n)值最小的节点作为下一个待访问的节点。

优点:利用启发式函数,搜索范围小,提高了搜索效率;如果最优路径存在,那么一定能找到最优路径。
缺点:A* 算法不适用于动态环境;不适用于高维空间,计算量大;目标点不可时会造成大量性能消耗。

算法流程:
1.从起点start开始,将start放入open优先队列中,open中存储的是将要遍历的节点,列表closed为空,closed是用来存放已经遍历的节点;
2.遍历start,将start从open中移除并添加到closed中,寻找start的邻居节点,至于最多可以找到多少邻居节点取决于算法规划,这里假设只允许节点向上下左右4个方向寻找那便是最多可以找到4个可通行的邻居节点,并计算F(n),将可通行的邻居节点放入open中;
3.从open中选择F(n)值最小的节点node,将node节点从open中移除并添加到closed中,寻找node的可通行的邻居节点并计算F(n);
4.如果邻居节点neighbor已经在closed中了,则跳过,不进行其他处理,
5.如果邻居节点neighbor不在open中,则将其添加到open中,并且将node设置为neighbor的父节点;
6.如果邻居节点neighbor已经在open中了,则计算新的路径从start到neighbor的代价即G(n)值:
如果新的G(n)值比之前存储的值更小则更新,并且将neighbor的父节点改为节点node,并计算F(n),之后存储到open中会根据F(n)值进行排序,
如果新的G(n)值比之前存储的值更大,则说明新的路径代价更高便不会选取这个节点也就不需要做出更新等处理;
7.再次从第3步开始执行,如此循环,直到在第3步中找出的节点node是目标节点goal时结束循环,也就成功找到了路径;或者在执行第3步时open为空,也要结束搜索过程了,此时说明无法找到合适路径。

算法实现

"""
    Filename: a_star.py
    Description: Plan path using A* Algorithm
    Author: Benxiaogu:https://github.com/Benxiaogu
    Date: 2024-08-05
"""
import heapq
import math
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation


class AStar:
    def __init__(self,grid,start,goal,board_size):
        self.grid = grid
        self.start = self.Node(start,0,0)
        self.goal = self.Node(goal,0,0)
        self.board_size = board_size

    class Node:
        def __init__(self, position, g, h, parent=None):
            self.position = position    # position of node
            self.g = g                  # distance from node to start
            self.h = h                  # heuristic value from node to goal
            self.parent = parent        # parent node

        def __eq__(self, other) -> bool:
            return self.position == other.position
        
        def __lt__(self, other):
            # Prioritise nodes based on heuristic value
            return self.g+self.h < other.g+other.h or (self.g+self.h==other.g+other.h and self.h<other.h)
        
    class Neighbor:
        def __init__(self,directions,cost):
            self.directions = directions
            self.cost = cost
    
    def get_neighbors(self, node):
        neighbors = []
        distances = []
        nexts = [self.Neighbor((-1, 0),1), self.Neighbor((0, 1),1), self.Neighbor((0, -1),1), self.Neighbor((1,0),1),
                self.Neighbor((-1,1),math.sqrt(2)), self.Neighbor((1,1),math.sqrt(2)),self.Neighbor((1, -1),math.sqrt(2)), self.Neighbor((-1,-1),math.sqrt(2))]
        for next in nexts:
            neighbor = (node[0] + next.directions[0], node[1] + next.directions[1])
            if self.board_size <= neighbor[0] < len(self.grid)-self.board_size and self.board_size <= neighbor[1] < len(self.grid[0])-self.board_size:
                if self.grid[neighbor[0]][neighbor[1]] == 0:
                    if next.directions[0] != 0 and next.directions[1] != 0:  # 对角线方向
                        if self.grid[node[0] + next.directions[0]][node[1]] == 0 and self.grid[node[0]][node[1] + next.directions[1]] == 0:
                            neighbors.append(neighbor)
                            distances.append(next.cost)
                    else:
                        neighbors.append(neighbor)
                        distances.append(next.cost)

        return neighbors,distances

    def plan(self):
        open = []
        self.searched = [] # Used to record nodes that are searched
        g_cost = {self.start.position:0}

        self.start.h = self.heuristic(self.start.position)
        heapq.heappush(open, self.start)

        while open:
            # Select the node closest to the start node
            current_node = heapq.heappop(open)
            if current_node.position in self.searched:
                continue
            self.searched.append(current_node.position)
            
            # Find the goal
            if current_node == self.goal:
                self.goal = current_node
                self.path = []
                while current_node:
                    self.path.append(current_node.position)
                    current_node = current_node.parent
                self.path = self.path[::-1]
                        
                return self.goal.g
            
            # Find the neighbors of the current node and determine in turn if they have already been closed
            neighbors, distances = self.get_neighbors(current_node.position)
            for neighbor,distance in zip(neighbors,distances):
                if neighbor in self.searched:
                    continue
                h = self.heuristic(neighbor)
                g = current_node.g+distance

                if neighbor not in g_cost or g < g_cost[neighbor]:
                    g_cost[neighbor] = g
                    neighbor_node = self.Node(neighbor,g,h,current_node)
                    heapq.heappush(open,neighbor_node)
                
        return -1

    # def heuristic(self, node):
    #     # Manhattan distance from current node to goal node
    #     return abs(node[0] - self.goal.position[0]) + abs(node[1] - self.goal.position[1])
    
    # def heuristic(self, node):
    #     # Chebyshev Distance
    #     D = 1
    #     D2 = math.sqrt(2)
    #     dx = abs(node[0] - self.goal.position[0])
    #     dy = abs(node[1] - self.goal.position[1])
    #     return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)
    
    def heuristic(self, node):
        # Euclidean Distance
        D = 1
        dx = abs(node[0] - self.goal.position[0])
        dy = abs(node[1] - self.goal.position[1])
        return D * math.sqrt(dx * dx + dy * dy)

结果:
在这里插入图片描述

代码如下,除了Python还提供了C++版本的,欢迎大家Follow andStar:
https://github.com/Benxiaogu/PathPlanning/tree/main/A_Star

启发式函数

曼哈顿距离
标准的启发式函数是曼哈顿距离(Manhattan distance)。考虑你的代价函数并找到从一个位置移动到邻近位置的最小代价D。因此,地图中的启发式函数应该是曼哈顿距离的D倍,常用于在地图上只能前后左右移动的情况:
h ( n ) = D ∗ ( a b s ( n . x – g o a l . x ) + a b s ( n . y – g o a l . y ) ) h(n) = D * (abs ( n.x – goal.x ) + abs ( n.y – goal.y ) ) h(n)=D(abs(n.xgoal.x)+abs(n.ygoal.y))

function heuristic(node) =
    dx = abs(node.x - goal.x)
    dy = abs(node.y - goal.y)
    return D * (dx + dy)

对角距离:
又称为切比雪夫距离。
对角距离是指在路径方案中允许斜着朝邻近的节点移动时的起点到终点的距离。
对角距离的计算如下:

function heuristic(node) =
    dx = abs(node.x - goal.x)
    dy = abs(node.y - goal.y)
    return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)

这里的D2指的是两个斜着相邻节点之间的移动代价。如果所有节点都是正方形,则其值等于sqrt(2)*D

欧几里得距离
如果你的单位可以沿着任意角度移动(而不是网格方向),那么你也许应该使用直线距离:
h ( n ) = D ∗ s q r t ( ( n . x − g o a l . x ) 2 + ( n . y − g o a l . y ) 2 ) h(n) = D * sqrt((n.x-goal.x)^2 + (n.y-goal.y)^2) h(n)=Dsqrt((n.xgoal.x)2+(n.ygoal.y)2)

function heuristic(node) =
    dx = abs(node.x - goal.x)
    dy = abs(node.y - goal.y)
    return D * sqrt(dx * dx + dy * dy)

对于网格形式的图,上述启发函数的应用场景:

如果图形中只允许朝上下左右四个方向移动,此时使用曼哈顿距离;
如果图形中允许朝上下左右以及对角线总共八个方向移动,此时使用对角距离;
如果图形中允许朝任何方向移动,此时使用欧几里得距离。

启发函数会影响A Star算法的行为。
1.如果启发函数H(n)始终为0,A Star算法将仅由G(n)决定节点的优先级,此时A Star算法便退化成了Dijkstra算法
2.如果H(n)始终小于等于节点n到终点的实际代价,则A Star算法一定能够找到最短路径。但是当H(n)的值越小,算法将遍历越多的节点,便就导致了算法越慢。(后面将证明)
3.如果H(n)完全等于节点n到终点的实际代价,则A Star算法将能够找到最佳路径,并且速度也很快。不过并非在所有场景下都能够做到这一点。因为在没有达到终点之前,我们很难确切算出距离终点的代价。
4.如果H(n)大于节点n到终点的实际代价,则A Star算法无法保证找到最短路径,不过此时会很快完成运算过程。
5.如果H(n)相较于G(n)大很多,达到一种极端情况,此时H(n)便直接影响算法的结果,也就变成了贪婪最佳优先搜索,不一定找到最短路径。

以上几种均是近似启发式函数,用于预估代价值。

除了近似启发式,也有精确启发式,但是一般情况下获取精确启发式函数值比较占用时间。精确启发式的计算:

1.在运行A*搜索算法之前,预先计算每对单元格之间的距离;
2.如果没有阻塞单元格(障碍物),我们可以使用距离公式/欧几里德距离,在不进行任何预先计算的情况下找到h的精确值。

作为初学者,难免存在不足之处,如果有误,还请指正,谢谢!
一起学习,一起进步!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RGB红绿灯——Arduino

光的三原色 牛顿发现光的色散奥秘之后&#xff0c;进一步计算发现&#xff1a;七种色光中只有红、绿、蓝三种色光无法被分解&#xff0c;而其他四种颜色的光均可由这三种色光以不同比例相合而成。于是红、绿、蓝被称为“三原色光”或“光的三原色”。后经证实&#xff1a;红、绿…

提升C++开发效率的利器:深入解析Clang Power Tools

目录 一、引言 二、Clang Power Tools 简介 什么是 Clang Power Tools&#xff1f; 背景与发展历史 与 Clang 编译器的关系 主要开发团队和社区支持 系统要求 安装步骤 基本配置和使用 三、主要功能 代码格式化&#xff08;Clang-Format&#xff09; 代码质量提升 …

springboot+Loki+Loki4j+Grafana搭建轻量级日志系统

文章目录 前言一、日志组件介绍1.1 Loki组件1.2 Loki4j组件1.3 Grafana 二、组件下载安装运行Loki下载安装运行Grafana下载安装运行 三、创建springboot项目总结 前言 日志在任何一个web应用中都是不可忽视的存在&#xff0c;它已经成为大部分系统的标准组成部分。搭建日志可视…

【大模型】Unsloth安装及使用教程

Unsloth是一个开源的大模型训练加速项目&#xff0c;使用OpenAI的Triton对模型的计算过程进行重写&#xff0c;大幅提升模型的训练速度&#xff0c;降低训练中的显存占用。Unsloth能够保证重写后的模型计算的一致性&#xff0c;实现中不存在近似计算&#xff0c;模型训练的精度…

【Material-UI】Button 组件中的基本按钮详解

文章目录 一、基本按钮变体1. 文本按钮&#xff08;Text Button&#xff09;2. 实心按钮&#xff08;Contained Button&#xff09;3. 轮廓按钮&#xff08;Outlined Button&#xff09; 二、应用场景与注意事项1. 使用场景2. 注意事项 三、总结 Material-UI 的 Button 组件是前…

ShardingProxy使用自定义策略,数据迁移方案

文章目录 ShardingProxy功能扩展分库分表数据迁移方案 ShardingProxy功能扩展 我们在使用ShardingJDBC时&#xff0c;会进行自定义分布式主键生成策略、自定义分片策略 如果我们想要我们自定义的这些策略在ShardingProxy中也能使用&#xff0c;应该如何操作嘞&#xff1f; 我…

使用F1C200S从零制作掌机之I2C传感器

访问I2C设备&#xff08;比如eeprom&#xff09;&#xff0c;我知道的有三总方法&#xff1a; &#xff08;一&#xff09;i2c-dev操作I2C设备&#xff1a;不用添加设备驱动&#xff0c;用户直接在应用层完成对具体I2C 设备的驱动工作。 &#xff08;二&#xff09;sysfs操作…

微信小程序教程011-:2:京西购物商城实战之TabBar实现

2、tabBar 2.0 创建tabBar分支 运行如下命令,基于master分支,创建本地tabBar子分支,用来开发和tabBar相关的功能 git checkout -b tabbar2.1 创建tabBar页面 在pages目录中,创建首页(home)、分类(cate)、购物车(cart)、我的(my)这4个tabBar页面,在HBuilderX中…

【网络世界】数据链路层

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 初识数据链路层 &#x1f4c2; 概念 &#x1f4c2; 协议格式 &#x1f4c1; MAC地址 &#x1f4c2; 概念 &#x1f4c2; 与IP地址的区别 &#x1f4c1; MTU &#x1f4c2; 对IP协议的影响 &#x1f4c2; 对UDP协议的影响…

思维+位运算,CF 1934D1 - XOR Break --- Solo Version

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1934D1 - XOR Break --- Solo Version 二、解题报告 1、思路分析 合法操作会让 n 越变越小 假如最高位1为 b1, 次高位1 为b2 那么我们去掉b1 的 1最大能够得到的数为 &#xff08;1 << b2&#xff…

图像传感器 - 从零开始认识各种传感器【二十六期】

图像传感器|从零开始认识各种传感器 1、什么是图像传感器&#xff1f; 图像传感器是将光信号转换为电信号的设备&#xff0c;图像传感器通过捕捉光子并将其转化为电子信号&#xff0c;从而生成数字图像。它是数码相机、摄像机、智能手机、无人机、自动驾驶汽车以及各种工业和医…

良心推荐!分享6个强大的电脑软件,每一个都非常实用

良心推荐&#xff01;分享6个功能强大的电脑软件&#xff0c;每一个都非常实用&#xff01; 1.Listen1 一个可以免费听音乐的软件&#xff0c;也可以在浏览器上作为插件使用&#xff0c;曲库丰富&#xff0c;里面涵盖了QQ音乐、酷狗音乐、网易云音乐等多个平台的歌单资源&…

【MySQL】索引——索引的实现、B+ vs B、聚簇索引 VS 非聚簇索引、索引操作、创建索引、查询索引、删除索引

文章目录 MySQL5. 索引的实现5.1 B vs B5.2 聚簇索引 VS 非聚簇索引 6. 索引操作6.1 创建主键索引6.2 创建唯一索引6.3 创建普通索引6.4 创建全文索引6.5 查询索引6.6 删除索引 MySQL 5. 索引的实现 因为MySQL和磁盘交互的基本单位为Page&#xff08;页&#xff09;。 MySQL 中…

LTrack:实现夜间多目标追踪,并开放低光多目标追踪数据集LMOT

摘要 低光场景在现实应用中很常见&#xff08;例如&#xff0c;夜间的自动驾驶和监控&#xff09;。最近&#xff0c;多目标跟踪在各种实际用例中受到了很多关注&#xff0c;但黑暗场景中的多目标跟踪却很少被考虑。在本文中&#xff0c;我们专注于黑暗场景中的多目标跟踪。为…

点菜吧——随便点 C#生成套餐

前言 一到食堂发现有多种选择&#xff0c;但是有一个固定的套路&#xff0c;只能是一个荤&#xff0c;二个小荤&#xff0c;菜品数量也不少&#xff0c;任君选择&#xff0c;如果是一个选择困难症&#xff0c;就有点烦了&#xff0c;所以出品这个自动生成套餐软件。各位老板可…

Mysql原理与调优-InnoDB行记录结构

目录 1.绪论 2.InnoDB的记录格式分类 3.compact格式 3.1 基本组成 3.2 记录额外信息 3.2.1 记录额外信息组成 3.2.2 变成字段长度列表 3.2.3 null值列表 3.2.4 记录头信息 3.3 实际记录 3.3.1 溢出页 3.3.2 varchar最多可以储存多少个字符的数据 4.其他记录格式和c…

配置python的基本环境

python的背景知识 python是咋来的&#xff1f; Python 是一种广泛使用的高级编程语言&#xff0c;由 Guido van Rossum 在1989年底发明&#xff0c;第一个公开发行版发行于1991年。Python 的设计哲学强调代码的可读性和简洁的语法&#xff08;尤其是使用空格缩进来区分代码块…

tcpdump使用指南

tcpdump 是一款强大的网络抓包工具&#xff0c;它使用 libpcap 库来抓取网络数据包&#xff0c;这个库在几乎在所有的 Linux/Unix 中都有。 tcpdump src 10.5.2.3 and dst port 3389 # 1. 基于IP地址过滤 # 根据源ip进行过滤 $ tcpdump -i eth2 src 192.168.10.100# 根据目标…

sqli-labs靶场练习(1、5-8关)

自己搭建环境啊喂...http://127.0.0.1/sqli-labs-php7-master/ 第一关 1.单引号判断是否存在注入点 /?id1 2.查询列数 ?id1 order by 3-- ?id1 order by 4-- 由此可判断有3列 3.查询用户名和密码分别在哪列 ?id-1 union select 1,2,3 -- 4.查询数据库名称为security ?…

CC2530组网实验-1

1&#xff0c;ZigBee协议栈 协议是一系列的通信标准&#xff0c;通信双方需要共同按照这一标准进行正常的数据发射和接收。协议栈是协议的具体实现形式&#xff0c;通俗点来理解就是协议栈是协议和用户之间的一个接口&#xff0c;开发人员通过使用协议栈来使用这个协议的&…