自动驾驶控制与规划——Project 6: A* Route Planning

目录

  • 零、任务介绍
  • 一、算法原理
    • 1.1 A* Algorithm
    • 1.2 启发函数
  • 二、代码实现
  • 三、结果分析
  • 四、效果展示
    • 4.1 Dijkstra距离
    • 4.2 Manhatten距离
    • 4.3 欧几里德距离
    • 4.4 对角距离
  • 五、后记

零、任务介绍

  1. carla-ros-bridge/src/ros-bridge/carla_shenlan_projects/carla_shenlan_a_star_planner/src/Astar_searcher.cpp
    中的 TODO部分
  2. 对⽐分析不同的启发函数的计算耗时,每次运⾏后在终端内会打印计算时间,需要截图放⼊⽂档中上传。

一、算法原理

1.1 A* Algorithm

A*算法的步骤如下:
在这里插入图片描述

启发函数有如下两条性质

  1. 可采纳性:启发式函数h(n)必须永远不超过从节点n到终点的实际成本(即h(n) ≤ h*(n),其中h*(n)是从节点n到终点的真实成本)。
  2. 一致性:对于任意两个节点n和m,若存在一条从n到m的边,则启发式函数满足h(n) ≤ c(n, m) + h(m),其中c(n, m)是从n到m的边成本。

当启发函数满足上述两条性质时,A算法能够保证路径的最优性,但实际应用中,启发函数可能不满足性质。若启发函数过于保守(远小于h),则会导致搜索效率低下,若启发函数过于激进(远大于h*),则会导致算法得到次优路径。比较极端的情况有两种:

  1. h=0:Dijkstra
  2. h>>h*:Greedy

1.2 启发函数

本项目中使用了三种常见的启发函数,分别为曼哈顿距离、欧氏距离和对角距离。
记起点到终点沿直角坐标系坐标轴方向的距离分别为 ( d x , d y , d z ) (d_x, d_y, d_z) (dx,dy,dz),曼哈顿距离定义为每一步只能向坐标轴方向移动
d M a n h a t t e n = d x + d y + d z d_{Manhatten} = d_x + d_y + d_z dManhatten=dx+dy+dz
欧式距离定义为空间中的直线距离
d E u c l i d e a n = d x 2 + d y 2 + d z 2 d_{Euclidean} = \sqrt{d_x^2 + d_y^2 + d_z^2} dEuclidean=dx2+dy2+dz2
三维的对角距离可以理解为先按照x,y,z三个方向的距离中最短的一个构成的正方体的体对角线移动到和终点相同的平面内,然后在面内沿正方形的对角线移动,最后沿一个坐标轴方向移动。
d max ⁡ = max ⁡ ( d x , d y , d z ) d min ⁡ = min ⁡ ( d x , d y , d z ) d m i d = ( d x + d y + d z ) − d max ⁡ − d min ⁡ d D i a g o n a l = 3 d min ⁡ + 2 ( d m i d − d min ⁡ ) + ( d max ⁡ − d m i d ) \begin{aligned} d_{\max} &= \max(d_x, d_y, d_z) \\ d_{\min} &= \min(d_x, d_y, d_z) \\ d_{mid} &= (d_x + d_y + d_z) - d_{\max} - d_{\min} \\ d_{Diagonal} &= \sqrt{3}d_{\min} + \sqrt{2} (d_{mid} - d_{\min}) + (d_{\max} - d_{mid}) \end{aligned} dmaxdmindmiddDiagonal=max(dx,dy,dz)=min(dx,dy,dz)=(dx+dy+dz)dmaxdmin=3 dmin+2 (dmiddmin)+(dmaxdmid)

二、代码实现

计算启发函数

double AstarPathFinder::getHeu(GridNodePtr node1, GridNodePtr node2) {
    /*
    choose possible heuristic function you want
    Manhattan, Euclidean, Diagonal, or 0 (Dijkstra)
    Remember tie_breaker learned in lecture, add it here ?
    */
    bool tie_breaker = true;
    double distance_heuristic;
    Eigen::Vector3d node1_coordinate = node1->coord;
    Eigen::Vector3d node2_coordinate = node2->coord;

    // auto start = std::chrono::high_resolution_clock::now();
    Eigen::VectorXd abs_vec = (node1_coordinate - node2_coordinate).cwiseAbs();
    // **** TODO: Manhattan *****
    distance_heuristic = abs_vec.sum();

    // **** TODO: Euclidean  *****
    distance_heuristic = abs_vec.norm();

    // **** TODO: Diagonal  *****
    double min_coeff = abs_vec.minCoeff();
    double max_coeff = abs_vec.maxCoeff();
    double mid_coeff = abs_vec.sum() - min_coeff - max_coeff;

    distance_heuristic = 0.0;
    distance_heuristic += min_coeff * sqrt(3.0);
    distance_heuristic += (mid_coeff - min_coeff) * sqrt(2.0);
    distance_heuristic += (max_coeff - mid_coeff);

    if (tie_breaker) {
        distance_heuristic = distance_heuristic * (1.0 + 1.0 / 100.0);
    }

    return distance_heuristic;
}

A*路径搜索函数

void AstarPathFinder::AstarGraphSearch(Vector3d start_pt, Vector3d end_pt) {
    // ros::Time time_1 = ros::Time::now();

    rclcpp::Time time_1 = this->now();    // 计时开始时间
    // ->now();
    // rclcpp::Time end_mpc;
    // start_mpc = this->now();

    // RVIZ中点选的目标点坐标不一定是体素的中心
    // index of start_point and end_point
    Vector3i start_idx = coord2gridIndex(start_pt);    // 坐标和体素地图索引互转
    Vector3i end_idx = coord2gridIndex(end_pt);
    goalIdx = end_idx;
    // 此处转换完成后的start_pt和end_pt是体素中心的坐标
    // position of start_point and end_point
    start_pt = gridIndex2coord(start_idx);
    end_pt = gridIndex2coord(end_idx);

    // Initialize the pointers of struct GridNode which represent start node and goal node
    GridNodePtr startPtr = new GridNode(start_idx, start_pt);
    GridNodePtr endPtr = new GridNode(end_idx, end_pt);

    // openSet is the open_list implemented through multimap in STL library
    // openSet的类型是std::multimap<double, GridNodePtr>
    openSet.clear();
    // currentPtr represents the node with lowest f(n) in the open_list
    GridNodePtr currentPtr = NULL;
    GridNodePtr neighborPtr = NULL;

    // put start node in open set
    startPtr->gScore = 0;                           // 起点到起点的距离为0
    startPtr->fScore = getHeu(startPtr, endPtr);    // 计算起点到终点的启发函数
    startPtr->id = 1;
    startPtr->coord = start_pt;
    openSet.insert(make_pair(startPtr->fScore, startPtr));    // 将起点加入openSet

    GridNodeMap[start_idx[0]][start_idx[1]][start_idx[2]]->id = 1;    // 更新起点的状态为已加入openSet

    vector<GridNodePtr> neighborPtrSets;    // 存储邻居节点
    vector<double> edgeCostSets;

    // this is the main loop
    while (!openSet.empty()) {
        /* Remove the node with lowest cost function from open set to closed set */
        // 将openSet中f最小的节点从openSet移动到closedSet
        currentPtr = openSet.begin()->second;    // 从openSet移除当前节点
        openSet.erase(openSet.begin());
        Eigen::Vector3i current_index = currentPtr->index;
        GridNodeMap[current_index[0]][current_index[1]][current_index[2]]->id = -1;    // 将当前节点标记为已扩展(ClosedSet)

        // if the current node is the goal
        if (currentPtr->index == goalIdx) {
            // ros::Time time_2 = ros::Time::now();

            rclcpp::Time time_2 = this->now();    // 计时结束时间
            terminatePtr = currentPtr;
            std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl;
            std::cout << "[A*]{sucess}  Time in A*  is " << (time_2 - time_1).nanoseconds() / 1000000.0 << "ms, path cost is " << currentPtr->gScore * resolution << "m." << std::endl;
            ;
            std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl;

            return;
        }
        // get the succession
        AstarGetSucc(currentPtr, neighborPtrSets, edgeCostSets);

        /*
        For all unexpanded neigbors "m" of node "n"
        */
        for (int i = 0; i < (int)neighborPtrSets.size(); i++) {
            /*
            Judge if the neigbors have been expanded
            IMPORTANT NOTE!!!
            neighborPtrSets[i]->id = -1 : expanded, equal to this node is in close set
            neighborPtrSets[i]->id = 1 : unexpanded, equal to this node is in open set
            */
            neighborPtr = neighborPtrSets[i];
            if (neighborPtr->id == 0)    // discover a new node, which is not in the closed set and open set
            {
                /*
                As for a new node, put neighbor in open set and record it
                */
                neighborPtr->gScore = currentPtr->gScore + edgeCostSets[i];
                neighborPtr->fScore = neighborPtr->gScore + getHeu(neighborPtr, endPtr);
                neighborPtr->cameFrom = currentPtr;
                openSet.insert(make_pair(neighborPtr->fScore, neighborPtr));
                neighborPtr->id = 1;
                continue;
            } else if (neighborPtr->id == 1)    // this node is in open set and need to judge if it needs to update
            {
                /*
                As for a node in open set, update it , maintain the openset ,and then put neighbor in open set and record it
                */
                if (neighborPtr->gScore > (currentPtr->gScore + edgeCostSets[i])) {
                    neighborPtr->gScore = currentPtr->gScore + edgeCostSets[i];
                    neighborPtr->fScore = neighborPtr->gScore + getHeu(neighborPtr, endPtr);
                    neighborPtr->cameFrom = currentPtr;
                }
                continue;
            } else    // this node is in closed set
            {
                continue;
            }
        }
    }
}

三、结果分析

本次实验中使用的地图如下:
在这里插入图片描述

Dijkstra算法的结果为最优解,作为下面不同启发函数的对照。

目标点位置规划时间(ms)路径长度(m)
右上341.45651.3047
右下259.77639.7067
左上194.33834.1463

Manhatten距离

目标点位置规划时间(ms)路径长度(m)
右上1.1548551.8406
右下3.2998741.3636
左上3.4943236.7279

欧几里德距离

目标点位置规划时间(ms)路径长度(m)
右上5.2185551.3047
右下12.243339.8031
左上1.3394334.2426

对角距离

目标点位置规划时间(ms)路径长度(m)
右上3.054651.8406
右下7.6939841.9993
左上4.6761336.7276

对比上述启发函数,可以发现,欧氏距离作为其中最保守的距离,最终规划得到的路径长度接近Dijkstra算法得到的最优解,而曼哈顿距离和对角距离得到的路径长度略大于最优解,但是规划耗时较小。三种启发函数的A*规划时间都远远小于Dijkstra。
规划耗时:曼哈顿距离<对角距离<欧几里德距离
路径长度:欧几里德距离<对角距离 ≈ \approx 曼哈顿距离

四、效果展示

Dijkstra演示视频

自动驾驶控制与规划——Project 5(Dijkstra)

A*演示视频

自动驾驶控制与规划——Project 5(A*)

4.1 Dijkstra距离

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2 Manhatten距离

Manhatten距离,目标点右上
在这里插入图片描述
Manhatten距离,目标点右下
在这里插入图片描述
Manhatten距离,目标点左上
在这里插入图片描述

4.3 欧几里德距离

欧几里德距离,目标点右上
在这里插入图片描述
欧几里德距离,目标点右下
在这里插入图片描述
欧几里德距离,目标点左上
在这里插入图片描述

4.4 对角距离

对角距离,目标点右上
在这里插入图片描述
对角距离,目标点右下
在这里插入图片描述
对角距离,目标点左上
在这里插入图片描述

五、后记

自动驾驶控制与规划是我在深蓝学院完完整整学完的第一门课,能坚持下来和我从无人机到航天器再到自动驾驶的心路历程转变密不可分,其中有的原因牵涉单位利益,不便多说。总而言之,无论在个人发展还是薪资待遇等方面,自动驾驶这个行业的前景还是非常可观的。
在短短一个多月的时间内熟练掌握自动驾驶必然是不可能的,但是这门课程让我了解了自动驾驶的常用动力学、控制、规划等基本原理和设计思路,已经成功入门了。学习过程中的代码全部开源在我的github上:AutonomousVehiclePnCCourseProjects,往期文章也可以在本专栏中阅读。
所谓师傅领进门,修行在个人,后续还需要进一步广泛的阅读相关文献,将我之前在多智能体博弈方面积累的经验和自动驾驶结合起来,努力推进自动驾驶技术和我个人的发展。
想说的就这么多,也衷心祝愿看到这里的朋友们在各自的领域坚持下去,早日实现理想!!!

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

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

相关文章

单纯形法的学习笔记

文章目录 A. 单纯形法概述1. 优化模型示例 B. 理论基础C. 算法思想D. 实现算法1. 线性规划的标准型2. 顶点解的理解及表示2.1 在标准型中变量取值为零的意义2.2 顶点解的表示 3. 最优性判断4. 解的更新5. 完成迭代过程 E. 单纯形法的基本概念与本文对照F. 文档源码 前言&#x…

ArmSoM RK3588/RK3576核心板,开发板网络设置

ArmSoM系列产品都搭配了以太网口或WIFI模块&#xff0c;PCIE转以太网模块、 USB转以太网模块等&#xff0c;这样我们的网络需求就不止是上网这么简单了&#xff0c;可以衍生出多种不同的玩法。 1. 网络连接​ 连接互联网或者组成局域网都需要满足一个前提–设备需要获取到ip&a…

[Linux]线程概念与控制

目录 一、线程概念 1.什么是线程 2.线程的轻量化 3.LWP字段 4.局部性原理 5.线程的优缺点 6.进程VS线程 二、线程的控制 1.线程创建 2.获取线程id 3.线程退出与等待 4.创建轻量级进程 三、线程的管理 1.pthread库管理线程 2.线程局部存储 四、C线程库 1.构造函…

cmake--库链接--RPATH--RUNPATH

RPATH--RUNPATH RPATH 是一种嵌入到二进制文件(可执行文件/库文件)中的路径信息&#xff0c;也就是存在于可执行文件或者库文件中的&#xff0c; 用RPATH(旧)或者RUNPATH(新)参数记录的路径信息&#xff0c; 指示动态链接器在运行时查找共享库的位置。 查看二进制文件的RPATH或…

Chapter 4.4:Adding shortcut connections

4 Implementing a GPT model from Scratch To Generate Text 4.4 Adding shortcut connections 接下来&#xff0c;让我们讨论 shortcut connections&#xff08;快捷连接&#xff09;背后的概念&#xff0c;也称为 skip connections&#xff08;跳跃连接&#xff09;或 resid…

Web渗透测试之XSS跨站脚本 原理 出现的原因 出现的位置 测试的方法 危害 防御手段 面试题 一篇文章给你说的明明白白

目录 XSS介绍的原理和说明 Cross Site Scripting 钓鱼 XSS攻击原理 XSS漏洞出现的原因&#xff1a; XSS产生的原因分析 XSS出现位置&#xff1a; XSS测试方法 XSS的危害 防御手段&#xff1a; 其它防御 面试题: 备注&#xff1a; XSS介绍的原理和说明 嵌入在客户…

热门数据手套对比,应用方向有何不同?

AI与人形机器人是目前市场中大热的两个新行业。在人形机器人或拟人仿真机器人制造与开发中动作捕捉技术的融入是必不可少的&#xff0c;通过将动捕数据与先进的AI大数据训练技术相结合&#xff0c;不仅能够省去枯燥乏味的动作编程过程大幅减少训练时间&#xff0c;还可以使训练…

dbt Semantic Layer 详细教程-1 :总体概述

dbt 语义模型提供语言描述方式快速定义业务指标。本文介绍语义模型作用和意义&#xff0c;以及语义模型的组成部分&#xff0c;后面会继续介绍如何定义语义模型&#xff0c;基于语义模型定义指标&#xff0c;如何通过MetricFlow&#xff08;语义层框架&#xff09;能够构建用于…

JAVA:探讨 CopyOnWriteArrayList 的详细指南

1、简述 在 Java 的并发编程中&#xff0c;CopyOnWriteArrayList 是一种特殊的线程安全的集合类。它位于 java.util.concurrent 包中&#xff0c;主要用于在并发读写场景下提供稳定的性能。与传统的 ArrayList 不同&#xff0c;CopyOnWriteArrayList 通过在每次修改时创建一个…

简单编程实现QT程序黑色主题显示

代码如下 int main(int argc, char *argv[]) {QApplication a(argc, argv);//QSurfaceFormat::setDefaultFormat(QVTKOpenGLStereoWidget::defaultFormat());QPalette darkpalette;a.setStyle(QStyleFactory::create("Fusion"));darkpalette.setColor(QPalette::Wind…

沁恒CH32V208GBU6外设PWM:注意分辨时钟使能函数RCC_APB2PeriphClockCmd;PWM模式1和模式2的区别;PWM动态开启和关闭

从事嵌入式单片机的工作算是符合我个人兴趣爱好的,当面对一个新的芯片我即想把芯片尽快搞懂完成项目赚钱,也想着能够把自己遇到的坑和注意事项记录下来,即方便自己后面查阅也可以分享给大家,这是一种冲动,但是这个或许并不是原厂希望的,尽管这样有可能会牺牲一些时间也有哪天原…

飞书企业消息实践

一、飞书自带的消息机器人限制 频控策略 - 服务端 API - 飞书开放平台 自定义机器人的频率控制和普通应用不同&#xff0c;为单租户单机器人 100 次/分钟&#xff0c;5 次/秒。建议发送消息尽量避开诸如 10:00、17:30 等整点及半点时间&#xff0c;否则可能出现因系统压力导致…

0107作业

思维导图 练习: 要求在堆区连续申请5个int的大小空间用于存储5名学生的成绩&#xff0c;分别完成空间的申请、成绩的录入、升序 排序、 成绩输出函数以及空间释放函数&#xff0c;并在主程序中完成测试 要求使用new和delete完成 #include <iostream>using namespace std…

以C++为基础快速了解C#

using System: - using 关键字用于在程序中包含 System 命名空间。 一个程序一般有多个 using 语句, 相当于C的 using namespace std; C# 是大小写敏感的。 所有的语句和表达式必须以分号&#xff08;;&#xff09;结尾。 程序的执行从 Main 方法开始。 与 Java 不同的是&#…

面试题:并发与并行的区别?

并发&#xff08;Concurrency&#xff09;和并行&#xff08;Parallelism&#xff09;是计算机科学中两个相关但不同的概念&#xff0c;它们都涉及到同时处理多个任务&#xff0c;但在实现方式和效果上有显著的区别。理解这两者的区别对于编写高效的多任务程序非常重要。 并发&…

面向对象分析和设计OOA/D,UML,GRASP

目录 什么是分析和设计&#xff1f; 什么是面向对象的分析和设计&#xff1f; 迭代开发 UML 用例图 交互图 基于职责驱动设计 GRASP 常见设计原则 什么是分析和设计&#xff1f; 分析&#xff0c;强调是对问题和需求的调查研究&#xff0c;不是解决方案。例如&#x…

MySQL使用navicat新增触发器

找到要新增触发器的表&#xff0c;然后点击设计&#xff0c;找到触发器标签。 根据实际需要&#xff0c;填写相关内容&#xff0c;操作完毕&#xff0c;点击保存按钮。 在右侧的预览界面&#xff0c;可以看到新生成的触发器脚本

Anthropic 的人工智能 Claude 表现优于 ChatGPT

在人工智能领域&#xff0c;竞争一直激烈&#xff0c;尤其是在自然语言处理&#xff08;NLP&#xff09;技术的发展中&#xff0c;多个公司都在争夺市场的主导地位。OpenAI的ChatGPT和Anthropic的Claude是目前最具影响力的两款对话型AI产品&#xff0c;它们都能够理解并生成自然…

《罪恶装备-奋战》官方中文学习版

《罪恶装备 -奋战-》是Arc System Works开发的格斗游戏《罪恶装备》系列的第二十五部作品 [1]&#xff0c;男主角索尔历时25年的故事就此画上句号&#xff0c;而罪恶装备的故事却并未结束。 《罪恶装备-奋战》官方中文版 https://pan.xunlei.com/s/VODWAm1Dv-ZWVvvmUMflgbbxA1…

期末概率论总结提纲(仅适用于本校,看文中说明)

文章目录 说明A选择题1.硬币2.两个事件的关系 与或非3.概率和为14.概率密度 均匀分布5.联合分布率求未知参数6.联合分布率求未知参数7.什么是统计量&#xff08;记忆即可&#xff09;8.矩估计量9.117页12题10.显著水平阿尔法&#xff08;背公式就完了&#xff09; 判断题11.事件…