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

news2024/9/24 18:39:00

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.保存路径。从终点开始,每个方格沿着父节点移动直至起点,这就是你的路径。

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

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

相关文章

【C++】拷贝构造函数,析构函数详解!

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

国家超级计算济南中心低代码平台应用实践

摘要&#xff1a;文章主要介绍了济南超算使用低代码平台明道云解决了一系列业务问题&#xff0c;包括资产管理、人员与机构管理、流程制度管理等。通过明道云平台&#xff0c;济南超算成功地将不同部门的业务信息进行整合&#xff0c;提高了工作效率和管理水平。文章还强调了明…

外部网关协议_边界网关协议BGP

一.边界网关协议BGP的基本概念 边界网关协议(Border Gateway Protocol&#xff0c;BGP&#xff09;属于外部网关协议EGP这个类别&#xff0c;用于自治系统AS之间的路由选择协议。由于在不同AS内度量路由的“代价”(距离、带宽、费用等&#xff09;可能不同&#xff0c;因此对于…

批量创建表空间数据文件(DM8:达梦数据库)

DM8:达梦数据库 - - 批量创建表空间数据文件 环境介绍1 批量创建表空间SQL2 达梦数据库学习使用列表 环境介绍 在某些场景(分区表子表)需要批量创建表空间,给不同的表使用,以下代码是批量创建表空间的SQL语句; 1 批量创建表空间SQL --创建 24个数据表空间,每个表空间有3个数…

LeetCode.283移动零(双指针)

LeetCode.283移动零 1.问题描述2.解题思路3.代码 1.问题描述 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1…

在一个页面里向两张表里插入内容时,有一些复杂的BUG简单化

向两张表里插入内容时&#xff0c;有一些复杂的BUG简单化 当在第一张表里的页面操作&#xff0c;在第一张表查询结果的页面进行编辑&#xff0c;在编辑的时候需要对第二张表里和第一张表都保存内容&#xff0c;而且插入之后两张表的id关联着&#xff0c;这个时候这张表的id就不…

【LeetCode】挑战100天 Day12(热题+面试经典150题)

【LeetCode】挑战100天 Day12&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-142.1 题目2.2 题解 三、面试经典 150 题-143.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…

2023年度全国国土变更调查工作相关技术文档、质检软件

1.资料清单&#xff1a; (1)01技术文件 2023年度国土变更调查数据库建设技术要求.pdf 2023年度全国国土变更调查实施方案.doc 附件1.国土调查数据库更新数据规范&#xff08;2023年度&#xff09;.pdf 附件2.国土调查数据库更新变更规则&#xff08;2023年度&#xff09;.pdf…

unity3d人物由静止到跑,出现人物漂移一段距离后再跑

减少动画切换过程中第一个动画的时间&#xff0c;交叉部分适当调小&#xff0c;动作切换就顺畅了

大模型生态新篇章:以AI Agent为引,助企业创新应用落地

文 | 智能相对论 作者 | 沈浪 以聊天机器人、虚拟助手、智能客服等为代表的对话式人工智能 (Conversational AI Agents ) 在具体服务场景中的应用已经十分普遍。今年以来&#xff0c;随着大模型技术的爆发与加持&#xff0c;对话式AI被市场赋予了更高的期望。 “所有行业都值…

C语言——指针(一)

&#x1f4dd;前言 这篇文章主要带大家初步认识一下指针&#xff0c;供大家理解参考。 主要归纳与讲解&#xff1a; 1&#xff0c;指针与指针变量 2&#xff0c;指针的基本使用&#xff08;如何定义&#xff0c;初始化&#xff0c;引用&#xff09; &#x1f3ac;个人简介&…

线性回归中的函数求导

在线性回归中&#xff0c;函数求导是一个重要的数学工具&#xff0c;用于计算损失函数关于模型参数的导数。通过求导&#xff0c;我们可以找到最优的参数值&#xff0c;以实现更好的线性回归拟合。 本文将介绍线性回归的基本原理&#xff0c;以及如何通过函数求导来优化线性回…

【JavaEE】Spring小练习——存储和获取对象

一、题目&#xff1a; 在 Spring 项目中&#xff0c;通过 main 方法获取到 Controller 类&#xff0c;调用 Controller 里面通过注入的方式调用Service 类&#xff0c;Service 再通过注入的方式获取到 Repository 类&#xff0c;Repository 类里面有一个方法构建⼀个 User 对象…

掌动智能带您深入了解业务连续性演练

业务连续性演练是一种有目的的模拟活动&#xff0c;旨在测试和评估组织业务连续性计划的有效性。这涉及到模拟各种可能的业务中断情况&#xff0c;以便团队可以在真实事件发生时做出迅速、协调的响应。 一、业务连续性演练设计原则 全面性&#xff1a;涵盖所有关键业务流程&…

shell 条件语句 if case

目录 测试 test测试文件的表达式 是否成立 格式 选项 比较整数数值 格式 选项 字符串比较 常用的测试操作符 格式 逻辑测试 格式 且 &#xff08;全真才为真&#xff09; 或 &#xff08;一真即为真&#xff09; 常见条件 双中括号 [[ expression ]] 用法 &…

unity Terrain 性能问题

在实践过程中unity发生进入场景GPU爆显存的情况&#xff0c;经过调查发现是使用Terrain造成的问题&#xff0c;这个问题在使用一个Terrain的时候并不会发生&#xff0c;但是在使用多个时会发生。 似乎在使用过程中Terrain会直接把Terrain的整个地图加载&#xff0c;造成移动设…

前缀树及其实现解析

前缀树 前缀树&#xff1a;又称单词查找树或键树&#xff0c;是一种哈希树的变种。 典型应用是用于统计和排序大量的字符串&#xff08;但不仅限于字符串&#xff09; 利用字符串的公共前缀来减少查询时间&#xff0c;最大限度地减少无谓的字符串比较。 将一组字符串数组放入…

Web 自动化神器 TestCafe(三)—用例编写篇

一、用例编写基本规范 1、 fixture 测试夹具 使用 TestCafe 编写测试用例&#xff0c;必须要先使用 fixture 声明一个测试夹具&#xff0c;然后在这个测试夹具下编写测试用例&#xff0c;在一个编写测试用例的 js 或 ts 文件中&#xff0c;可以声明多个测试夹具 fixture(测试…

内容营销频频出圈,这些品牌号做对了什么?

小红书拥有大量的年轻用户&#xff0c;通过运营品牌号既能降低投放成本&#xff0c;又能更好地连接消费者和品牌&#xff0c;在平台完成一站式闭环营销。 今天就借助几个成功案例&#xff0c;来分析下他们是如何搭建官方账号&#xff0c;通过内容运营吸引更多用户&#xff0c;实…