手撕A*算法(详解A*算法)

A*算法原理

全局路径规划算法,根据给定的起点和终点在全局地图上进行总体路径规划。

导航中使用A*算法计算出机器人到目标位置的最优路线,一般作为规划的参考路线

在这里插入图片描述

// 定义地图上的点
struct Point
{
    int x,y; // 栅格行列
    Point(int x, int y):x(x),y(y){}; // 参数列表初始化
    double distance(Point& p)        // 求距离
    {
        return sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)); // 欧几里得距离
    }
};
// 定义节点
struct Node
{
    Point point; // 栅格点
    double g,h,f;// 代价值,f总价值,g到起点的代价值,h到终点的估计代价(启发式函数)
    Node *parent;// 父节点指针
    Node(Point point, double g, double h, Node* parent = nullptr):point(point), g(g), h(h), f(g+h), parent(parent)
    {}
};

在这里插入图片描述

// 定义地图
 vector<vector<int>> gridmap = {
         {0, 1, 0, 0, 0},
         {0, 0, 1, 0, 0},
         {0, 0, 1, 1, 0},
         {0, 0, 1, 0, 0},
         {0, 0, 1, 1, 0}
 };
 // 定义起点和终点
 Point start{0, 0};
 Point goal{4, 4};

A*算法的寻路原理
在这里插入图片描述

A的结束条件
在这里插入图片描述
A
算法的寻路详细步骤
在这里插入图片描述
在这里插入图片描述

手撕A*代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
// 定义地图上的点
struct Point
{
    int x,y; // 栅格行列
    Point(int x, int y):x(x),y(y){}; // 参数列表初始化
    double distance(Point& p)        // 求距离
    {
        return sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)); // 欧几里得距离
    }
};


// 定义节点
struct Node
{
    Point point; // 栅格点
    double g,h,f;// 代价值,f总价值,g到起点的代价值,h到终点的估计代价(启发式函数)
    Node *parent;// 父节点指针
    Node(Point point, double g, double h, Node* parent = nullptr):point(point), g(g), h(h), f(g+h), parent(parent)
    {}
};

// 自定义Node*排序规则
struct NodeCompare{
    bool operator()(Node* n1, Node* n2){
        return (n1->f) < (n2->f); // 表示升序排列
    }
};
// 基于栅格地图的路径规划A*算法,返回由Point组成的路径path,输入为地图,起点和终点
vector<Point> AstarPathPlanning(vector<vector<int>> &gridmap, Point& start, Point& goal)
{
    // 获取地图参数
    int row = gridmap.size(); // 行,表示地图的宽度
    int col = gridmap[0].size(); // 列,表示地图的长
    // 定义openlist, closelist
    vector<Node *> openlist; // openlist 表示待搜索的节点
    vector<Node *> closelist;// closelist表示已搜索的节点
    openlist.push_back(new Node(start, start.distance(start), start.distance(goal))); // 将起点加入openlist中,作为初始化
    int count1 = 1;
    // 进入循环,开始搜索,搜索到终点则返回路径
    vector<Point> path;
    while (!openlist.empty()) // 当openlist为空,表示所有可搜索节点已经被搜索,此时循环结束
    {
        // 获取当前搜索节点current,即openlist中f最小节点
        sort(openlist.begin(), openlist.end(), NodeCompare{}); // 先对openlist排序,这里自定义排序规则(从小到大)
        Node* current = *openlist.begin(); // *openlist.begin()排序后即为f最小的迭代器位置
        // 将current对应的元素从openlist中删除
        openlist.erase(openlist.begin());
        // 将current加入到closelist中
        closelist.push_back(current);
        // 对当前搜索节点current进行分类讨论
        // 1-current是终点,则返回路径,表示找到路径
        if (current->point.x == goal.x && current->point.y == goal.y)
        {
            while (current != nullptr) // 利用父节点,从终点向起点回溯最短路径,因为起点没有父节点,所以起点current父节点为nullptr
            {
                path.push_back(current->point);
                current = current->parent;
            }
            reverse(path.begin(), path.end()); // 路径是反的,翻转路径
            int count2 = 0; // delete 次数
            for (auto o : openlist)
            {
                delete o;
                count2++;
            }
            for (auto c : closelist)
            {
                delete c;
                count2++;
            }
            cout << "new times: " << count1 << endl;
            cout << "delete times: " << count2 << endl;
            return path;
        }
        // 2-current 不是终点,需要讨论其邻近的节点neighbors
        int x = current->point.x;
        int y = current->point.y;
        vector<Point> neighbors = { // 8个邻近节点的坐标
                {x-1,y-1}, {x-1,y}, {x-1,y+1},
                {x,y-1},     {x,y+1},
                {x+1,y-1}, {x+1,y}, {x+1,y+1}
        };
        // 遍历所有的临近节点,每一个邻近节点n必须满足在地图范围内同时不是障碍物
        for (auto n : neighbors)
        {
            if ((n.x >= 0 && n.x < row) && (n.y >= 0 && n.y < col) && gridmap[n.x][n.y]==0)
            {
                // 1 n在closelist中,表示已经搜索过了,此时直接跳过
                bool incloselist = false;
                for (auto c : closelist)
                {
                    if (c->point.x == n.x && c->point.y == y)
                    {
                        incloselist = true;
                        break;
                    }
                }
                if (incloselist)
                {
                    continue;
                }
                // 2 n是否在openlist中进行讨论
                bool inopenlist = false;
                for (auto o : openlist)
                {
                    if (o->point.x == n.x && o->point.y == n.y)
                    {
                        inopenlist = true;
                        // n 在openlist中,对比f值,更新代价值和父节点parent
                        double g = current->g + n.distance(current->point); // 临近节点n到起点的距离 = 当前搜索节点current到起点的距离 + 当前搜索节点current到邻近节点n距离
                        double h = n.distance(goal); // 临近节点n到终点的估计距离代价
                        double f = g + h;
                        if (f < (o->f))
                        {
                            o->f = f;
                            o->parent = current;
                        }
                        break;
                    }
                }
                if (!inopenlist) // n不在openlist中,对比f值,计算代价值,添加到openlist中,下次备选
                {
                    double g = current->g + n.distance(current->point); // 临近节点n到起点的距离 = 当前搜索节点current到起点的距离 + 当前搜索节点current到邻近节点n距离
                    double h = n.distance(goal); // 临近节点n到终点的估计距离代价
                    double f = g + h;
                    openlist.push_back(new Node(n,g,h,current));
                    count1++;
                }
            }
        }

    }


    // 搜索完成没有路径,表示路径规划失败,此时返回空路径
    return path;
}
int main()
{
    // 定义地图
    vector<vector<int>> gridmap = {
            {0, 1, 0, 0, 0},
            {0, 0, 1, 0, 0},
            {0, 0, 1, 1, 0},
            {0, 0, 1, 0, 0},
            {0, 0, 1, 1, 0}
    };
    // 定义起点和终点
    Point start{0, 0};
    Point goal{4, 4};
    vector<Point> path = AstarPathPlanning(gridmap, start, goal);
    cout << path.size() << endl;
    for (auto p : path)
    {
        if (p.x == goal.x && p.y == goal.y)
        {
            cout << "(" << p.x << ',' << p.y << ")" << endl;
        }
        else
        {
            cout << "(" << p.x << ',' << p.y << ")" << "->";
        }
    }
    return 0;
}

A*算法总结

把起点加入 open list 。
重复如下过程:
a. 遍历 open list ,查找 F 值最小的节点,把它作为当前要处理的节点。
b. 把这个节点移到 close list 。
c. 对当前方格的 8 个相邻方格的每一个方格?
◆ 如果它是不可抵达的或者它在 close list 中,忽略它。否则,做如下操作。
◆ 如果它不在 open list 中,把它加入 open list ,并且把当前方格设置为它的父亲,记录该方格的 F , G 和 H 值。
◆ 如果它已经在 open list 中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 G 值作参考。更小的 G 值表示这是更好的路径。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 和 F 值。如果你的 open list 是按 F 值排序的话,改变后你可能需要重新排序。
d. 停止,当你
◆ 把终点加入到了 open list 中,此时路径已经找到了,或者
◆ 查找终点失败,并且 open list 是空的,此时没有路径。
3.保存路径。从终点开始,每个方格沿着父节点移动直至起点,这就是你的路径。

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

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

相关文章

51单片机利用I/O口高阻状态实现触摸控制LED灯

51单片机利用I/O口高阻状态实现触摸控制LED灯 1.概述 这篇文章介绍使用I/O口的高阻状态实现一个触摸控制LED灯亮灭的实验。该实验通过手触摸P3.7引脚&#xff0c;改变电平信号控制灯的亮灭。 2.实验过程 2.1.实验材料 名称型号数量单片机STC12C20521LED彩灯无1晶振12MHZ1电…

PDF 批量处理软件BatchOutput PDF mac中文版介绍

BatchOutput PDF mac是一款适用于 Mac 的 PDF 批量处理软件。它可以帮助用户将多个 PDF 文件进行异步处理&#xff0c;提高工作效率。 BatchOutput PDF 可以自动化执行许多任务&#xff0c;包括 PDF 文件的打印、转换、分割、压缩、加密、重命名等&#xff0c;而且它还可以将自…

开启数据库审计(db,extended级别或os级别),并将审计文件存放到/home/oracle/audit下

文章目录 开启数据库审计&#xff08;db,extended级别或os级别&#xff09;&#xff0c;并将审计文件存放到/home/oracle/audit下一. 简介二. 配置2.1. 审计是否安装2.2. 审计表空间迁移2.3. 审计参数2.4. 审计级别2.5. 其他审计选项2.6. 审计相关视图 三. 使用3.1. 开启/关闭审…

案例023:基于微信小程序的童装商城的设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

wpf使用CefSharp.OffScreen模拟网页登录,并获取身份cookie,C#后台执行js

目录 框架信息&#xff1a;MainWindow.xamlMainWindow.xaml.cs爬取逻辑模拟登录拦截请求Cookie获取 CookieVisitorHandle 框架信息&#xff1a; CefSharp.OffScreen.NETCore 119.1.20 MainWindow.xaml <Window x:Class"Wpf_CHZC_Img_Identy_ApiDataGet.MainWindow&qu…

关于前端上传

类似于 上面的传参form-data形式&#xff0c;第一个参数为上传的文件&#xff0c;第二个参数为json格式

Centos部署GitLab-备份恢复

1. 下载rpm包 wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-10.8.4-ce.0.el7.x86_64.rpm2. 安装依赖 yum -y install policycoreutils openssh-server openssh-clients postfix policycoreutils-python3. rpm安装 rpm -ivh gitlab-ce-10.8.4-ce.…

OpenStack云计算平台

目录 一、OpenStack 1、简介 2、硬件需求 3、网络 二、环境搭建 1、安全 2、主机网络 3、网络时间协议(NTP) 4、OpenStack包 5、SQL数据库 6、消息队列 7、Memcached 一、OpenStack 1、简介 官网&#xff1a;https://docs.openstack.org/2023.2/ OpenStack系统由…

git查看某个commit属于哪个分支方法(如何查看commit属于哪个分支)

有时候&#xff0c;当我们由于业务需求很多时&#xff0c;基于同一个分支新建的项目分支也会很多。 在某个时间节点&#xff0c;我们需要合并部分功能点时&#xff0c;我们会忘了这个分支是否已经合入哪个功能点&#xff0c;我们就会查看所有的commit记录&#xff0c;当我们找到…

Jmeter快速入门

文章目录 1.安装Jmeter1.1.下载1.2.解压1.3.运行 2.快速入门2.1.设置中文语言2.2.基本用法 1.安装Jmeter Jmeter依赖于JDK&#xff0c;所以必须确保当前计算机上已经安装了JDK&#xff0c;并且配置了环境变量。 1.1.下载 可以Apache Jmeter官网下载&#xff0c;地址&#xf…

Word中如何实现 图片 | 表格 自动编号与文中引用编号对应

当我们在进行大篇幅word文档的编写时&#xff0c;为了节约修改文章中图片或表格所花费的大量时间&#xff0c;可以将图片自动编号&#xff0c;且让文中引用的顺序跟着图片顺序的变化而变化&#xff0c;具体操作如下&#xff1a; 1. 将鼠标定位在图片或者表格欲加编号的下方或上…

【SpringBoot3+Vue3】五【完】【实战篇】-前端(配合后端)

目录 一、环境准备 1、创建Vue工程 2、安装依赖 2.1 安装项目所需要的vue依赖 2.2 安装element-plus依赖 2.2.1 安装 2.2.2 项目导入element-plus 2.3 安装axios依赖 2.4 安装sass依赖 3、目录调整 3.1 删除部分默认目录下文件 3.1.1 src/components下自动生成的…

2.HTML入门

目录 一.HTML介绍 二.HTML常用标签 2.1 标题标签 2.2 段落标签 2.3 超链接标签 2.4 图片标签 2.5 换行与空格 2.6 布局标签 2.7 列表标签 2.8 表单标签 一.HTML介绍 定义&#xff1a;将内容显示在网页&#xff0c;用来描述网页的一种语言&#xff0c;负责网页的架构…

objdump反汇编文件解析

命令使用 objdump可以对可执行文件进行反汇编 其常用参数为: objdump -d <file(s)>: 将代码段反汇编&#xff1b;objdump -S <file(s)>: 将代码段反汇编的同时&#xff0c;将反汇编代码与源代码交替显示&#xff0c;编译时需要使用-g参数&#xff0c;即需要调试信…

常见树种(贵州省):014槭树、梧桐、鹅掌楸、檫木、梓木、油桐、泡桐、川楝、麻楝

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、色木槭…

机器学习笔记 - 复杂任务的CNN组合

基础CNN架构可通过多种方式进行组合和扩展,从而解决更多、更复杂的任务。 1. 分类和定位 在分类和定位任务中,你不仅需要说出在图像中找到的物体的类别,而且还需指出物体显现在图像中的边界框坐标。这类任务假设在图像中只有一个物体实例。 这个任务可通过在典型的分类网络…

Cobalt Strike的各类反向上线操作

声明&#xff1a;本文仅限于技术讨论与分享&#xff0c;严禁用于非法途径。若读者因此作出任何危害网络安全行为后果自负&#xff0c;与本号及原作者无关。 前言 Cobalt Strike 使用 GUI 框架 SWING&#xff08;一种java GUI的库&#xff09;开发&#xff0c;攻击者可通过CS木马…

如何使用VisualSVN在Windows系统上设置SVN服务器并公网远程访问

文章目录 前言1. VisualSVN安装与配置2. VisualSVN Server管理界面配置3. 安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4. 固定公网地址访问 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学…

常见树种(贵州省):013桉树、米槠、栲类

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、桉树 …

常见树种(贵州省):012茶、花椒、八角、肉桂、杜仲、厚朴、枸杞、忍冬

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、茶 灌…