路徑規劃算法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
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84398.shtml
英文地址,請注明出處:http://en.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…