路径规划算法BFS/Astar/HybridAstar简单实现

借鉴本文所述代码简单实现一下BFS,Astar和HybridAstar路径规划算法,用于辅助理解算法原理。
代码在这里,画图用到了matplotlibcpp库,需要先装一下,然后直接在文件目录下执行如下代码即可运行:

mkdir build
cd build
cmake ..
make
./HAstar

1. 场景

这里没考虑朝向

start_pose: 6, 10         // 起始点和朝向
end_pose: 90, 90          // 目标点和朝向
boundary: 0, 100, 0, 100  // 整个场景的边界(这里没有使用)
obstacle: 0, 0, 0, 100, 100, 100, 100, 0  // 整个场景的边界,四个顶点,顺时针组成一个四边形
obstacle: 12, 12, 12, 36, 30, 36, 30, 12  // 障碍物(下面都是),四个顶点,顺时针组成一个四边形
obstacle: 20, 50, 20, 80, 40, 80, 40, 50  
obstacle: 50, 6, 50, 60, 60, 60, 60, 6
obstacle: 60, 70, 60, 95, 85, 95, 85, 70
obstacle: 75, 20, 75, 50, 95, 50, 90, 20

网格大小取0.2(可以选其他值,可能会有不同的效果),原点处网格坐标为(0,0),将场景信息都转化成网格坐标,场景示意图如下图所示,其中红色圆点是起点,绿色圆点是终点:
在这里插入图片描述

2. BFS

std::vector<Vec2i> bfs(const Scenario& scenario,const std::unordered_set<Vec2i, Vec2iHash>& obstacles) {// 每个节点的查找方向,左上右下std::vector<Vec2i> neighbors{Vec2i{-1,0}, Vec2i{0,1}, Vec2i{1,0}, Vec2i{0,-1}}; // 每个节点的查找方向,8个方向//vector<Vec2i> neighbors{Vec2i{-1,1}, Vec2i{0, 1}, Vec2i{1, 1},//                           Vec2i{1,0}, Vec2i{1,-1}, Vec2i{0,-1}, //                           Vec2i{-1,-1}, Vec2i{-1,0}};                   std::vector<Vec2i> path; // 路径结果std::queue<Vec2i> open_nodes; // 待check的节点std::unordered_map<std::string, Vec2i> pre_node; // 每个节点的父节点bool isvalid = true;Vec2i start{static_cast<int>(scenario.start_pos.x/RES_GRID), static_cast<int>(scenario.start_pos.y/RES_GRID)};Vec2i goal{static_cast<int>(scenario.end_pos.x/RES_GRID), static_cast<int>(scenario.end_pos.y/RES_GRID)};std::string goal_name = std::to_string(goal.x)+"_" + std::to_string(goal.y);std::string start_name = std::to_string(start.x)+"_" + std::to_string(start.y);open_nodes.push(start);pre_node[start_name] = start;while(!open_nodes.empty()){Vec2i curr_node = open_nodes.front();open_nodes.pop();// 判断是否到达终点if(curr_node.x == goal.x && curr_node.y == goal.y){pre_node[goal_name] = pre_node[std::to_string(curr_node.x)+"_" + std::to_string(curr_node.y)];break;}// 获取相邻的有效节点for(int i = 0; i < 4; i++){isvalid = true;Vec2i neighbor{curr_node.x+neighbors[i].x, curr_node.y+neighbors[i].y};// 检查节点是否已经被遍历过std::string n_name = std::to_string(neighbor.x) + "_" + std::to_string(neighbor.y);if(pre_node.find(n_name) != pre_node.end()){continue;}// 简单的碰撞检测,节点附近一定范围内不能有障碍点 for(int i = -4; i< 5; i++){for(int j = 4; j > -5; j--){Vec2i pt{neighbor.x+i, neighbor.y+j};if(obstacles.find(pt) != obstacles.end()){isvalid = false;break;}}}if(!isvalid) continue;pre_node[n_name] = curr_node;open_nodes.push(neighbor);}}if(pre_node.find(goal_name) == pre_node.end()){std::cout<<"未找到有效路径!"<<std::endl;}else{std::cout<<"找到有效路径!"<<std::endl;Vec2i pt = goal;while(pt.x != start.x || pt.y != start.y){path.emplace_back(pt);pt = pre_node[std::to_string(pt.x) + "_" + std::to_string(pt.y)];   }path.emplace_back(start);}return path;
}

运行结果如下图所示:
在这里插入图片描述

3. Astar

std::vector<Vec2i> AStar(const Scenario& scenario,const std::unordered_set<Vec2i, Vec2iHash>& obstacles) {// 每个节点的查找方向,8个方向std::vector<Vec2i> neighbors{Vec2i{-1,1}, Vec2i{0, 1}, Vec2i{1, 1},Vec2i{1,0}, Vec2i{1,-1}, Vec2i{0,-1}, Vec2i{-1,-1}, Vec2i{-1,0}}; std::vector<Vec2i> path; // 路径结果                               // 节点队列,保存节点名和节点总代价(路径代价+启发函数代价)std::priority_queue<std::pair<std::string, double>, std::vector<std::pair<std::string, double>>, cmp> open_nodes_cost;// <节点名,<节点坐标,节点路径代价>>std::unordered_map<std::string, std::pair<Vec2i, double>> open_nodes;// <节点名,<节点坐标,节点路径代价>>std::unordered_map<std::string, Vec2i> close_nodes;// 保存节点的父节点std::unordered_map<std::string, Vec2i> pre_node; bool isvalid = true;Vec2i start{static_cast<int>(scenario.start_pos.x/RES_GRID), static_cast<int>(scenario.start_pos.y/RES_GRID)};Vec2i goal{static_cast<int>(scenario.end_pos.x/RES_GRID), static_cast<int>(scenario.end_pos.y/RES_GRID)};double start_cost = std::sqrt((goal.x - start.x)*(goal.x - start.x) + (goal.y - start.y)*(goal.y - start.y));std::string goal_name = std::to_string(goal.x) + "_" + std::to_string(goal.y);std::string start_name = std::to_string(start.x) + "_" + std::to_string(start.y);open_nodes_cost.emplace(start_name, start_cost);open_nodes.emplace(start_name, std::pair<Vec2i, double>(start, 0));pre_node.emplace(start_name, start);while(!open_nodes_cost.empty()){const std::string curr_name = open_nodes_cost.top().first;open_nodes_cost.pop();Vec2i curr_node = open_nodes[curr_name].first;double curr_path_cost = open_nodes[curr_name].second;// 判断是否到达终点if(curr_node.x == goal.x && curr_node.y == goal.y){pre_node[goal_name] = pre_node[curr_name];break;} // 当前节点已checkclose_nodes.emplace(curr_name, curr_node);// 遍历相邻节点for(int i = 0; i < 8; i++){isvalid = true;Vec2i neighbor{curr_node.x+neighbors[i].x, curr_node.y+neighbors[i].y};std::string neighbor_name = std::to_string(neighbor.x) + "_" + std::to_string(neighbor.y);// 简单的碰撞检测,节点附近一定范围内不能有障碍点 for(int i = -4; i< 5; i++){for(int j = 4; j > -5; j--){Vec2i pt{neighbor.x+i, neighbor.y+j};if(obstacles.find(pt) != obstacles.end()){isvalid = false;break;}}}if(!isvalid) continue;// 如果该点已经check过if(close_nodes.find(neighbor_name) != close_nodes.end()){continue;}// 计算节点代价double neighbor_path_cost = curr_path_cost + std::sqrt(neighbors[i].x*neighbors[i].x+neighbors[i].y*neighbors[i].y);// 启发函数直接用欧式距离double H_cost = std::sqrt((goal.x - neighbor.x)*(goal.x - neighbor.x) + (goal.y - neighbor.y)*(goal.y - neighbor.y));if(open_nodes.find(neighbor_name) == open_nodes.end()){open_nodes.emplace(neighbor_name, std::pair<Vec2i, double>(neighbor, neighbor_path_cost));open_nodes_cost.emplace(neighbor_name, H_cost+neighbor_path_cost);pre_node[neighbor_name] = curr_node;}}}if(pre_node.find(goal_name) == pre_node.end()){std::cout<<"未找到有效路径!"<<std::endl;}else{std::cout<<"找到有效路径!"<<std::endl;Vec2i pt = goal;while(pt.x != start.x || pt.y != start.y){path.emplace_back(pt);pt = pre_node[std::to_string(pt.x) + "_" + std::to_string(pt.y)];   }path.emplace_back(start);}return path;
}

与BFS结果一起显示,如下图所示,红色路径是BFS结果,紫色路径结果是Astar:
在这里插入图片描述

4. HybridStar

std::vector<Vec2i> HybridAStar(const Scenario& scenario,const unordered_set<Vec2i, Vec2iHash>& obstacles,double wheelbase, double step_size, double max_steer) {std::shared_ptr<Node> last_node = nullptr;  std::vector<Vec2i> path; // 路径结果                             // 节点队列,保存节点名和节点总代价(路径代价+启发函数代价)std::priority_queue<std::pair<std::string, double>, std::vector<std::pair<std::string, double>>, cmp> open_nodes_cost;// <节点名,节点状态>std::unordered_map<std::string, std::shared_ptr<Node>> open_nodes;// <节点名,节点状态>std::unordered_map<std::string, std::shared_ptr<Node>> close_nodes;Node start(scenario.start_pos.x, scenario.start_pos.y, 0, 0, 0, nullptr);Node goal(scenario.end_pos.x, scenario.end_pos.y, 0, 0, 0, nullptr);Vec2i goal_index{static_cast<int>(goal.x / RES_GRID), static_cast<int>(goal.y / RES_GRID)};double start_cost = std::sqrt((goal.x - start.x)*(goal.x - start.x) + (goal.y - start.y)*(goal.y - start.y)); // 真实坐标距离Vec2i start_index{static_cast<int>(start.x / RES_GRID),static_cast<int>(start.y / RES_GRID)};std::string start_name = std::to_string(start_index.x) +"_" + std::to_string(start_index.y) + "_" + std::to_string(0);std::shared_ptr<Node> start_Node = std::make_shared<Node>();start_Node->x = start.x;start_Node->y = start.y;start_Node->theta = start.theta;start_Node->g = start.g;start_Node->f = start.f;start_Node->parent = nullptr;open_nodes_cost.emplace(start_name, start_cost);open_nodes.emplace(start_name, start_Node);while(!open_nodes_cost.empty()){const std::string curr_name = open_nodes_cost.top().first;open_nodes_cost.pop();auto curr_node = open_nodes[curr_name];// 判断当前节点是否到达终点Vec2i curr_index{static_cast<int>(curr_node->x / RES_GRID), static_cast<int>(curr_node->y / RES_GRID)};if(curr_index.x == goal_index.x && curr_index.y == goal_index.y){last_node = curr_node;break;}close_nodes[curr_name] = curr_node;// 往下扩展,-45°到45°采样5次且只考虑前进for(int i = 0; i < 5; i++){std::shared_ptr<Node> next_Node = std::make_shared<Node>();double next_x = 0;double next_y = 0;double next_theta = 0;double steer = -M_PI/4 + i * (M_PI/8);  // 转向角if(i != 2){ // 转弯double radius = wheelbase / tan(steer); // 转弯半径double delt_theta = step_size / radius; // 航向角偏移next_theta = NormalizeAngle(curr_node->theta - delt_theta); // 转向角正时右转,航向角向负偏移if(radius < 0){ // 左转next_x = curr_node->x + abs(radius)*(cos(curr_node->theta + abs(delt_theta))-cos(curr_node->theta));next_y = curr_node->y + abs(radius)*(sin(curr_node->theta + abs(delt_theta))-sin(curr_node->theta));}else{ // 右转next_x = curr_node->x + abs(radius)*(-cos(curr_node->theta - abs(delt_theta))+cos(curr_node->theta));next_y = curr_node->y + abs(radius)*(sin(-curr_node->theta + abs(delt_theta))+sin(curr_node->theta));}}else{ // 直行next_x = curr_node->x - std::sin(curr_node->theta) * step_size;next_y = curr_node->y + std::cos(curr_node->theta) * step_size;next_theta = curr_node->theta;}next_Node->x = next_x;next_Node->y = next_y;next_Node->theta = next_theta;next_Node->g = curr_node->g + step_size; next_Node->f = next_Node->g + std::sqrt((next_Node->x-goal.x)*(next_Node->x-goal.x) +(next_Node->y-goal.y)*(next_Node->y-goal.y));next_Node->parent = curr_node;Vec2i next_node{static_cast<int>(next_Node->x / RES_GRID),static_cast<int>(next_Node->y / RES_GRID)};std::string next_name = std::to_string(next_node.x) + "_" + std::to_string(next_node.y)+"_"+to_string(i);if (close_nodes.find(next_name) != close_nodes.end()) {continue;}// 简单的碰撞检测,节点附近一定范围内不能有障碍点 bool isvalid_node = true;for(int i = -4; i< 5; i++){for(int j = 4; j > -5; j--){Vec2i pt{next_node.x+i, next_node.y+j};if(obstacles.find(pt) != obstacles.end()){isvalid_node = false;break;}}}if(!isvalid_node) continue;if(open_nodes.find(next_name) == open_nodes.end()){open_nodes.emplace(next_name, next_Node);open_nodes_cost.emplace(next_name, next_Node->f);}}}if(last_node == nullptr){std::cout<<"未找到有效路径!"<<std::endl;}else{std::cout<<"找到有效路径!"<<std::endl;std::shared_ptr<Node> temp_node = last_node;while(temp_node->parent != nullptr){path.emplace_back(Vec2i{static_cast<int>(temp_node->x / RES_GRID), static_cast<int>(temp_node->y / RES_GRID)});temp_node = temp_node->parent;}}return path;
}

与BFS和Astar的结果一起显示,如下图所示,红色路径是BFS结果,紫色路径结果是Astar,棕色路径是HybridAstar,看起来不是很平滑,感兴趣的可以自己调调看:
在这里插入图片描述

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

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

相关文章

get_the_category() 和 get_the_terms() 的区别

get_the_category() 和 get_the_terms() 是WordPress中用于获取文章分类的两个函数&#xff0c;但它们之间存在一些关键差异&#xff1a; get_the_category() 特定于分类&#xff1a;get_the_category() 函数专门用于获取文章的分类(category)。它返回一个包含所有分类对象的…

RocketMq的消息类型及代码案例

RocketMQ 提供了多种消息类型&#xff0c;以满足不同业务场景对 顺序性、事务性、时效性 的要求。其核心设计思想是通过解耦 “消息传递模式” 与 “业务逻辑”&#xff0c;实现高性能、高可靠的分布式通信。 一、主要类型包括 普通消息&#xff08;基础类型&#xff09;顺序…

maxkey单点登录系统

github地址 https://github.com/MaxKeyTop/MaxKey/blob/master/README_zh.md 1、官方镜像 https://hub.docker.com/u/maxkeytop 2、MaxKey:Docker快速部署 参考地址&#xff1a; Docker部署 | MaxKey单点登录认证系统 拉取docker脚本MaxKey: Dromara &#x1f5dd;️MaxK…

基于AI生成测试用例的处理过程

基于AI生成测试用例的处理过程是一个结合机器学习、自然语言处理&#xff08;NLP&#xff09;和领域知识的系统性流程。以下是其核心步骤和关键技术细节&#xff0c;以帮助理解如何利用AI自动化生成高效、覆盖全面的测试用例。 1. 输入分析与需求建模 目标 将用户需求、系统文…

《Java vs Go vs C++ vs C:四门编程语言的深度对比》

引言​​ 从底层硬件操作到云端分布式系统&#xff0c;Java、Go、C 和 C 四门语言各自占据不同生态位。本文从​​设计哲学​​、​​语法范式​​、​​性能特性​​、​​应用场景​​等维度进行对比&#xff0c;为开发者提供技术选型参考。 一、​​设计哲学与历史定位​​…

无损提速黑科技:YOLOv8+OREPA卷积优化方案解析(原理推导/代码实现/调参技巧三合一)

文章目录 一、OREPA核心思想与创新突破1.1 传统重参数化的局限性1.2 OREPA的核心创新二、OREPA实现原理与数学推导2.1 卷积核分解策略2.2 动态融合公式三、YOLOv8集成实战(完整代码实现)3.1 OREPA卷积模块定义3.2 YOLOv8模型集成3.3 训练与推理配置四、性能对比与实验分析4.1…

RestTemplate 发送的字段第二个大写字母变成小写的问题探究

在使用RestTemplate 发送http 请求的时候&#xff0c;发现nDecisonVar 转换成了ndecisonVar ,但是打印日志用fastjson 打印的没有问题&#xff0c;换成jackson 打印就有问题。因为RestTemplate 默认使用的jackson 作为json 序列化方式&#xff0c;导致的问题&#xff0c;但是为…

C#核心概念解析:析构函数、readonly与this关键字

&#x1f50d; 析构函数&#xff1a;资源清理的最后防线 核心作用 析构函数&#xff08;~ClassName&#xff09;在对象销毁前执行&#xff0c;专用于释放非托管资源&#xff08;如文件句柄、非托管内存&#xff09;。托管资源&#xff08;如.NET对象&#xff09;由GC自动回收…

FFmpeg中使用Android Content协议打开文件设备

引言 随着Android 10引入的Scoped Storage&#xff08;分区存储&#xff09;机制&#xff0c;传统的文件访问方式发生了重大变化。FFmpeg作为强大的多媒体处理工具&#xff0c;也在不断适应Android平台的演进。本文将介绍如何在FFmpeg 7.0版本中使用Android content协议直接访…

vue——v-pre的使用

&#x1f530; 基础理解 ✅ 什么是 v-pre&#xff1f; v-pre 是一个跳过编译的 Vue 指令。 它告诉 Vue&#xff1a;“这个元素和其子元素中的内容不要被编译处理&#xff0c;按原样输出。” ✅ 使用场景&#xff1a; 展示原始的 Mustache 插值语法&#xff08;{{ xxx }}&a…

PyTorch中TensorBoardX模块与torch.utils.tensorboard模块的对比分析

文章目录 说明1. 模块起源与开发背景2. 功能特性对比3. 安装与依赖关系4. 性能与使用体验5. 迁移与兼容性策略6. 最佳实践与建议7. 未来展望8. 结论实际相关信息推荐资源 说明 TensorBoard&#xff1a;独立工具&#xff0c;只需安装tensorboard。TensorFlow&#xff1a;非必需…

单片机中断系统工作原理及定时器中断应用

文件目录 main.c #include <REGX52.H> #include "TIMER0.H" #include "KEY.H" #include "DELAY.H"//void Timer0_Init() { // TMOD 0x01; // TL0 64536 % 256; // TH0 64536 / 256; // ET0 1; // EA 1; // TR0 1; //}unsigned char…

Python爬虫实战:研究Portia框架相关技术

1. 引言 1.1 研究背景与意义 在大数据时代,网络数据已成为企业决策、学术研究和社会分析的重要资源。据 Statista 统计,2025 年全球数据总量将达到 175ZB,其中 80% 以上来自非结构化网络内容。如何高效获取并结构化这些数据,成为数据科学领域的关键挑战。 传统爬虫开发需…

【机器学习基础】机器学习与深度学习概述 算法入门指南

机器学习与深度学习概述 算法入门指南 一、引言&#xff1a;机器学习与深度学习&#xff08;一&#xff09;定义与区别&#xff08;二&#xff09;发展历程&#xff08;三&#xff09;应用场景 二、机器学习基础&#xff08;一&#xff09;监督学习&#xff08;二&#xff09;无…

[C语言初阶]扫雷小游戏

目录 一、原理及问题分析二、代码实现2.1 分文件结构设计2.2 棋盘初始化与打印2.3 布置雷与排查雷2.4 游戏主流程实现 三、后期优化方向 在上一篇文章中&#xff0c;我们实现了我们的第二个游戏——三子棋小游戏。这次我们继续结合我们之前所学的所有内容&#xff0c;制作出我们…

ROS云课三分钟-破壁篇GCompris-一小部分支持Edu应用列表-2025

开启蓝桥云课ROS ROS 机器人操作系统初级教程_ROS - 蓝桥云课 安装和使用GCompris 终端输入&#xff1a;sudo apt install gcompris sudo apt install gcompris ok&#xff0c;完成即可。 sudo apt install gcompris 如果是平板&#xff0c;秒变儿童学习机。 启动 流畅运…

Linux系统基础——是什么、适用在哪里、如何选

一、Linux是什么 Linux最初是由林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;基于个人兴趣爱好开发的个人项目&#xff0c;他编写了最核心的内核&#xff1b;后面为了发展壮大Linux系统他将整个项目开源到GitHub上&#xff0c;可以让全世界的人都参与到项目的开发维护中…

26、AI 预测性维护 (燃气轮机轴承) - /安全与维护组件/ai-predictive-maintenance-turbine

76个工业组件库示例汇总 AI 预测性维护模拟组件 (燃气轮机轴承) 概述 这是一个交互式的 Web 组件,旨在模拟基于 AI 的预测性维护 (Predictive Maintenance, PdM) 概念,应用于工业燃气轮机的关键部件(例如轴承)。它通过模拟传感器数据、动态预测剩余使用寿命 (RUL),并根…

el-form 使用el-row el-col对齐 注意事项

1.el-form 使用inline&#xff0c;el-form-item宽度会失效。 2.为了保证el-form-item 和 它内部的el-input 能在一行&#xff0c;要设置el-form-item的label-width <el-form :model"editInspectform"><el-row style"margin-bottom: 20px"><…

mac 安装 mysql 和 mysqlshell

1. 安装 mysql https://dev.mysql.com/downloads/mysql/?spma2c6h.12873639.article-detail.4.37474f4dTHdszC 默认mysql未配置环境变量&#xff0c;可以在设置中找到 2. 安装 mysqlshell https://dev.mysql.com/downloads/shell/ #启动mysql-shell mysqlsh 3. 使用 mysq…