ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于A星算法的无人机三维路径规划与Matlab实现详解

基于A星算法的无人机三维路径规划与Matlab实现详解 做无人机三维路径规划时很多朋友一上来就想上深度学习、强化学习那套结果光环境搭建和调参就把人劝退了。我当年第一次接这个课题的时候也一样后来老老实实把A星算法在Matlab里从二维改到三维才真正把路径规划这层窗户纸捅破。所谓A星算法说白了就是在一个带权重的网格地图里每次用“起点到当前点的已知代价 当前点到终点的预估代价”去排序候选节点优先走总代价最小的方向直到抵达目标点。三维场景下无非是把地图从平面格子换成体素格子把四方向八方向的邻居扩展到26个方向核心思路完全不变。本文把这套基于A星算法的无人机三维路径规划完整实现拆开讲清楚包括三维环境建模、Matlab代码架构、启发函数选型、参数调节和常见坑适合正在做无人机航迹规划、机器人路径规划课程设计或科研项目的同学直接参考复现。我调试这套代码前后花了大概三周踩了不少坑比如启发函数选错导致路径绕远、栅格精度设太高直接内存溢出、规划出的路径一卡一卡没法给飞控用等等。下面把每个环节的细节和教训都摊开说。1. 三维路径规划的问题定义与A星算法核心思路1.1 为什么三维路径规划选中A星算法路径规划算法五花八门从最早的Dijkstra、A星到后来的RRT、RRT*、PRM再到各种元启发式算法和深度强化学习方法每个算法都有自己的脾气。三维无人机路径规划为什么我推荐先用A星核心原因是三个有界最优性、可解释性、工程实现简单。有界最优性指的是在启发函数满足“可采纳性”的前提下A星一定能找到最优路径而且比Dijkstra聪明得多——Dijkstra向四面八方均匀扩散A星知道朝目标方向发力搜索效率高出一个量级。RRT是随机采样的速度快但路径质量不稳定每次跑出来的结果都不一样做对比实验时写论文很痛苦。元启发式算法比如粒子群、遗传算法虽然能处理复杂约束但迭代时间长参数多到让人崩溃而且不保证收敛到合理路径。可解释性对科研和工程同样重要。A星每一步探索的节点、每一条代价计算都有明确的公式依据中间出了问题能一步步回溯调试。神经网络那套就不行网络输出一条乱七八糟的轨迹你还得琢磨它到底“学到了什么”。另外Matlab里实现A星的代码量很小不需要额外装工具箱用纯脚本就能跑通这对学生党和没有完整开发环境的人特别友好。可能有人会问既然A星这么好业界为什么还搞那么多算法答案是A星怕高维状态空间。如果无人机的运动状态除了三维位置还有姿态、速度状态维度一涨栅格数量呈指数爆炸A星就扛不住了。但如果我们只做三维位置路径规划不考虑动力学细节A星依然是最合适的选择。先把它跑通后续要加约束再加这是最务实的路线。1.2 代价函数与启发函数的“为什么”A星的核心表达式一句话就能说清楚[ F(n)G(n)H(n) ]G(n)表示从起点到当前节点n已经付出的实际代价H(n)表示从当前节点n到目标点的预估代价。整个搜索就是在OpenList和CloseList两个集合之间倒腾节点反复选出F值最小的节点扩展直到目标节点被选出来。代价函数的设计决定了路径的“个性”。最基本的代价就是路径长度也就是欧几里得距离累积。但工程上往往还要叠加其他代价比如飞行高度代价——离地太低可能有撞树撞楼风险太高又可能暴露或者超过飞行高度限制再比如危险区域代价——提前把雷达威胁区、禁飞区叠加进地图里某个体素被标记为威胁经过它的代价就惩罚性加大。这样A星规划出来的路径就不是单纯的最短路径而是综合考虑安全和长度后的折中路径。启发函数的设计更关键它是A星的灵魂。工程实现中常用三种启发函数公式计算开销路径风格适用场景曼哈顿距离|Δx||Δy||Δz|最低带有明显折线拐角限制只能正交飞行的场合欧几里得距离sqrt(Δx²Δy²Δz²)中等方向自然、贴目标开阔地形、自由飞行切比雪夫距离max(|Δx|,|Δy|,|Δz|)最低允许斜向穿行允许任意方向直线运动的场合我做这套无人机规划用的就是欧几里得距离。理由很简单无人机在三维空域里只要没有硬性禁飞区限制理论上可以朝任意方向飞行欧几里得距离是这种连续运动的无偏估计。曼哈顿距离在三维格子里会强迫路径先走完一个轴向再走另一个轴向产生很多不必要的直角转弯无人机飞起来要多走不少冤枉路。但欧几里得距离有个容易被忽视的坑在带权图的栅格地图里如果允许对角线移动沿着对角线走一格的实际代价是√2倍的边长二维三维里对角线移动的代价是√3倍边长。很多教程代码里这个地方喜欢偷懒不管横走竖走还是斜走代价一律按1算结果就是启发函数严重高估实际代价A星退化得不太正常路径质量明显变差。我当时意识到这个问题后把所有移动代价按边长倍数重新标定路径才显得顺滑合理。提示启发函数高估实际代价会让A星丢掉最优性保证这是最隐蔽也最容易犯的原理性错误。调代码时如果发现路径明显怪异先检查移动代价矩阵和启发函数之间是否自洽。2. 三维环境建模与栅格地图构建2.1 从海拔数据到三维体素地图路径规划不是凭空做的第一步是把真实环境塞进一个A星能理解的数据结构里。我在项目里做的是模拟山区环境用几个高斯函数叠加生成数字高程模型再在上面放障碍物构建一个三维体素空间。所谓体素Voxel就是三维的像素。把规划空间划分成大小一致的立方体格子比如每个格子长宽高都是1米规划空间是100米×100米×50米那体素总数就是100×100×5050万个。每个体素记录两个信息是否被障碍物占据以及经过它的额外代价。A星要做的就是在这50万个格子里找出一条从起点体素到终点体素的通路。Matlab里表示这种地图最直接的方式就是三维逻辑数组。比如map zeros(nx, ny, nz)对应位置赋1表示障碍物。这一步看似简单实际有个大坑Matlab的数组索引顺序容易搞混。我在写代码初期就犯过把x、y、z映射错误的错误导致规划出来的路径在地图上完全错位调试了半天才发现是维度顺序问题。建议一开始就用统一的约定比如行索引对应x坐标列索引对应y坐标页索引对应z坐标所有函数都按这个约定走不要换来换去。把连续地形转成体素地图时还得注意“膨胀”处理。无人机不是质点它有物理尺寸而且飞控有定位误差和响应延迟贴着障碍物边缘飞非常危险。我的做法是在障碍物周围膨胀1到2个体素相当于在障碍物外面加一圈安全缓冲区。这样A星搜出来的路径天然和障碍物保持一定距离后续交给飞控执行的时候容错空间就大多了。地图分辨率怎么选也是个需要反复权衡的事。分辨率越高路径越精确但体素数量按三次方增长。同样100米×100米×50米的空间2米分辨率下只有50×50×256.25万个体素A星毫秒级算完1米分辨率下变成50万个搜索时间翻好几倍0.5米分辨率直接400万个普通电脑的Matlab基本跑不动。我的经验是先做全局粗规划用低分辨率地图比如5米网格把大方向定下来固定翼或旋翼无人机再做局部精修时用高分辨率地图。这是“粗规划细规划”的分层思想工程上非常实用。2.2 三维邻域扩展策略与碰撞检测二维A星只需要考虑4邻域或者8邻域三维就得考虑多少个邻居理论上在三维体素中每个体素最多有26个邻居6个面邻接、12个边邻接、8个角邻接。但实际不一定要全用。扩展方式邻居数路径特点计算代价6邻域6路径只能沿轴向走转弯僵硬最低18邻域18斜对角受限中等26邻域26路径最平滑方向最自然最高做无人机规划我通常用26邻域。因为是空域规划无人机自由度足够高26邻域搜出来的路径更贴近真实可飞轨迹。但26邻域有个连带问题对角线移动会斜着穿过体素角点可能“擦碰”障碍物。比如沿体素对角线移动时如果两个相邻体素中有一个是障碍物斜穿过去的路径就可能和障碍物发生碰撞但单纯检查终点体素是否空闲根本发现不了。解决方法是碰撞检测时不仅检查目标体素还要检查路径穿过的中间体素。最严谨的做法是使用“超采样”或者超级覆盖算法把连线经过的所有体素都枚举出来判断。工程上我常用的折中方案是把26邻域中涉及角点或边线穿越的移动额外检查共享的相邻体素。比如沿x和y方向同时移动一格时需要保证当前体素、目标体素以及中间两只角对应的体素都不是障碍物。多几个判断条件换来的是路径的绝对安全这笔账非常划算。障碍物膨胀和碰撞检测配合起来基本上可以杜绝A星路径穿墙的问题。还有一个细节是体素地图边界规划边界要留出至少一个体素的净空防止无人机路径贴着空间边界被判定越界。3. Matlab代码框架与关键实现3.1 整体程序结构设计这套代码我按“地图生成—A星搜索—路径后处理—可视化”四个模块组织每个模块拆成独立函数方便替换和调试。完整程序结构如下%% 主脚本main_astar3d.m clc; clear; close all; % 1. 参数设置 map_size [100, 100, 50]; % 规划空间尺寸(x, y, z) 单位:米 grid_res 2.0; % 网格分辨率 单位:米 inflate_size 1; % 障碍物膨胀体素个数 % 2. 构建三维栅格地图 map create3DMap(map_size, grid_res, inflate_size); % 3. 设置起点和终点(世界坐标) start_world [5, 5, 10]; goal_world [90, 90, 30]; % 世界坐标转栅格坐标 start_grid worldToGrid(start_world, map_size, grid_res); goal_grid worldToGrid(goal_world, map_size, grid_res); % 4. A星搜索 [path_grid, cost] aStar3D(map, start_grid, goal_grid, euclidean); % 5. 路径平滑与后处理 path_smooth smoothPath(path_grid, grid_res, 5); % 6. 可视化 visPath3D(map, path_grid, path_smooth, start_world, goal_world);每个函数的职责要单一。create3DMap只负责生成地图数据aStar3D只负责搜索逻辑smoothPath只做轨迹平滑。这样后面不管你是换启发函数、换地图生成方式还是加动态障碍物都不需要动其他模块。3.2 A星主循环与核心代码解析A星主循环写出来没多长但细节都在数据结构里。我最早用Matlab结构体数组存OpenList和CloseList代码写起来很直观结果地图一大就卡得要命——结构体数组的动态增删在Matlab里是出了名的慢。后来我把OpenList改成N×4的数值矩阵每一行存节点的x索引、y索引、z索引和F值再用循环找F值最小的那一行性能提升了不只一点点。关键代码如下function [path, cost] aStar3D(map, start, goal, hType) % A星三维路径规划核心函数 % 输入: % map - 三维逻辑栅格地图, 障碍物为true % start - 起点栅格坐标 [sx, sy, sz] % goal - 终点栅格坐标 [gx, gy, gz] % hType - 启发函数类型: euclidean | manhattan | chebyshev % 输出: % path - 路径栅格坐标序列 N*3 % cost - 路径总代价 [nx, ny, nz] size(map); % 邻居偏移表(26邻域) - 用权重计算移动代价 [neighbors, moveCosts] genNeighbor3D(); % 代价存储: 用大矩阵直接初始化, 避免动态扩容 gScore inf(nx, ny, nz); fScore inf(nx, ny, nz); gScore(start(1), start(2), start(3)) 0; fScore(start(1), start(2), start(3)) heuristic(start, goal, hType); % 父节点回溯表 cameFrom zeros(nx, ny, nz, 3); % OpenList: [x, y, z, fScore] openList [start, fScore(start(1), start(2), start(3))]; % 记录节点是否在OpenList中 inOpen false(nx, ny, nz); inOpen(start(1), start(2), start(3)) true; while ~isempty(openList) % 找到F值最小的节点 [~, idx] min(openList(:, 4)); current openList(idx, 1:3); % 检查是否到达目标 if isequal(current, goal) path reconstructPath(cameFrom, current); cost gScore(goal(1), goal(2), goal(3)); return; end % 从OpenList移除, 加入CloseList openList(idx, :) []; inOpen(current(1), current(2), current(3)) false; closed(current(1), current(2), current(3)) true; % 扩展邻居 for k 1:size(neighbors, 1) nb current neighbors(k, :); % 边界检查 if nb(1) 1 || nb(1) nx || nb(2) 1 || nb(2) ny || nb(3) 1 || nb(3) nz continue; end % 障碍物检查 if map(nb(1), nb(2), nb(3)) continue; end % 对角线碰撞检测 if ~isMoveValid(map, current, nb) continue; end % 已经在CloseList中 if closed(nb(1), nb(2), nb(3)) continue; end tentativeG gScore(current(1), current(2), current(3)) moveCosts(k); if tentativeG gScore(nb(1), nb(2), nb(3)) cameFrom(nb(1), nb(2), nb(3), :) current; gScore(nb(1), nb(2), nb(3)) tentativeG; fScore(nb(1), nb(2), nb(3)) tentativeG heuristic(nb, goal, hType); if ~inOpen(nb(1), nb(2), nb(3)) openList [openList; nb, fScore(nb(1), nb(2), nb(3))]; inOpen(nb(1), nb(2), nb(3)) true; else % 更新OpenList中的F值 rowIdx find(openList(:,1) nb(1) openList(:,2) nb(2) openList(:,3) nb(3)); openList(rowIdx, 4) fScore(nb(1), nb(2), nb(3)); end end end end % OpenList为空说明无路径 error(A星搜索失败: 未找到可行路径); end这段代码看起来不长但有两个地方特别值得说。第一个是genNeighbor3D生成邻居偏移表这个表决定了搜索的“方向性格”。我把它单独抽成函数因为每次手写26个偏移容易出错。第二个是isMoveValid做对角线碰撞检测刚才说过这个检查不能省。启发函数heuristic的实现也贴一下function h heuristic(node, goal, hType) % 启发函数计算 dx abs(node(1) - goal(1)); dy abs(node(2) - goal(2)); dz abs(node(3) - goal(3)); switch hType case euclidean h sqrt(dx^2 dy^2 dz^2); case manhattan h dx dy dz; case chebyshev h max([dx, dy, dz]); otherwise error(未知启发函数类型); end end特别提醒一点如果用的欧几里得启发函数移动代价表里的对角线移动代价必须严格按照空间距离计算。26邻域里轴向邻居移动代价是1个网格边长面对角线邻居是√2倍边长体对角线邻居是√3倍边长。我把这三档代价分别定为1、1.414、1.732代入移动代价表才算自洽。3.3 路径回溯与坐标系变换搜索到目标节点后沿着cameFrom表一路回溯到起点得到的就是栅格坐标系下的路径点序列。这里有个工程细节栅格坐标是离散的、以体素为单位的整数坐标而无人机实际飞行需要的是连续世界坐标。所以回溯完必须做一次坐标变换。function path_world gridToWorldPath(path_grid, map_size, grid_res) % 栅格路径转世界坐标路径, 体素中心对齐 offset grid_res / 2; path_world zeros(size(path_grid)); for i 1:size(path_grid, 1) path_world(i, 1) (path_grid(i, 1) - 1) * grid_res offset; path_world(i, 2) (path_grid(i, 2) - 1) * grid_res offset; path_world(i, 3) (path_grid(i, 3) - 1) * grid_res offset; end end这个变换看起来简单但很多人写A星的时候会忽略“体素中心对齐”这件事。栅格坐标(1,1,1)对应的世界坐标不是(0,0,0)而是(grid_res/2, grid_res/2, grid_res/2)。因为体素覆盖的是从0到grid_res的范围实际位置应取中心点。这个细节不影响A星搜索本身但会影响路径导入飞控或者跟真实地图做叠加显示时的对齐精度。另一个容易忽略的是坐标轴方向约定。Matlab里图像坐标系和地理坐标系习惯不一样容易把x轴和y轴搞反。我的建议是在代码文件头部写清楚统一约定所有函数共用一套别中途改。4. 路径质量优化与参数调节4.1 锯齿路径的破解贝塞尔曲线与B样条平滑A星在栅格上搜出来的路径天然带有锯齿感。26邻域虽然比4邻域平滑不少但无人机真要沿这条路径飞还是有明显的问题路径点之间存在方向突变飞控实现起来要么需要频繁急转弯要么产生很大的超调量。我实际飞行的体会是直接拿A星输出的原始路径给飞控飞行品质惨不忍睹尤其转弯处飞机会明显飘。解决锯齿最通用的办法是B样条平滑。B样条的好处是曲线不会完全经过控制点而是被控制点“吸引”从而自然消除折线尖角。Matlab里可以自己实现B样条递推也可以用interp1配合smoothdata做近似处理。我的做法最朴素先把路径点做三次样条插值再用滑动窗口平滑高度方向的速度变化。function path_smooth smoothPath(path_grid, grid_res, iter) % 路径平滑: 基于三次样条插值移动平均 t 1:size(path_grid, 1); % 三个坐标轴分别插值 tt linspace(1, size(path_grid, 1), size(path_grid, 1) * 5); % 5倍细分 xx spline(t, path_grid(:,1), tt); yy spline(t, path_grid(:,2), tt); zz spline(t, path_grid(:,3), tt); path_smooth [xx, yy, zz] * grid_res; % 迭代移动平均, 进一步降噪 kernel ones(3,1) / 3; for k 1:iter path_smooth(:,1) conv(path_smooth(:,1), kernel, same); path_smooth(:,2) conv(path_smooth(:,2), kernel, same); path_smooth(:,3) conv(path_smooth(:,3), kernel, same); end end每次平滑之后必须重新做碰撞检测。这是血的教训平滑曲线可能在拐角处“切”进障碍物内部表面上看路径很顺滑实际上已经撞上了。我实际项目中专门写了一个碰撞校验函数把平滑后的路径按小步长采样逐点检查是否落入障碍物体素。如果发现碰撞就减小平滑力度或者迭代次数优先保证安全性。4.2 核心参数怎么调网格分辨率、启发权重、安全距离参数调节是A星在工程中落地最烦人的环节。我把调参心得整理成一个速查表参数调大影响调小影响建议取值依据网格分辨率路径更精确计算量指数增长路径粗糙容易漏掉窄通道按无人机翼展/机身体积的1.5~2倍设定启发函数权重w搜索更快路径可能非最优搜索慢路径更优严格最优用1.0工程妥协用1.05~1.2障碍物膨胀半径安全性高可行通道变少路径贴近障碍物风险高至少覆盖定位误差无人机半径最大迭代次数防止死循环的兜底过低会漏解体素总量的1~2倍启发函数权重这个参数特别有意思。理论上A星要求H(n)不能高估实际代价H(n)权重必须小于等于1否则丢失最优性。但工程上我经常把权重调到1.1或者1.2因为无人机路径规划这种场景最优性不是绝对的效率往往更重要。权重稍微调大搜索方向性更强扩展的节点数明显减少速度提升非常显著代价是路径可能比最优长度长百分之几。只要不影响任务执行这种交换很划算。网格分辨率的选择上我走过一段弯路。刚开始做实验的时候总想着分辨率越高越好结果把100米范围划到0.5米网格Matlab直接卡死内存也报错。后来我悟出一个道理分辨率应该跟着无人机的物理尺寸走。旋翼无人机翼展不到1米用2米网格足够了固定翼无人机转弯半径动辄几十米用10米网格都不嫌粗。先粗后细的分层策略才是王道。高度层的采样方式也值得单独说说。三维空间在垂直方向的复杂度通常远低于水平方向山里地形起伏无非是几十米的范围不值得拿和水平方向一样精细的分辨率去切。我习惯把水平网格设成2米垂直方向设成5米Z方向层数少了体素总量直接降低了约六成搜索时间大幅缩短路径高度基本没受影响。4.3 高度代价约束与地形跟随策略纯粹的无约束A星容易搜出一条贴着地形上下起伏的路径对无人机来说既不安全也不经济。飞行高度变化越频繁动力系统消耗越大续航时间越短。所以在实际项目中我额外叠加了一个高度代价惩罚项。具体做法是在heuristic函数里增加高度项的权重function h heuristicWithHeight(node, goal, hType, zWeight) % 带高度权重的启发函数 h_base heuristic(node, goal, hType); % 高度惩罚: 鼓励保持巡航高度, 减少不必要爬升下降 dh abs(node(3) - goal(3)); h h_base zWeight * dh; end这里zWeight设多少要因地制宜。平坦地域我设0.05对高度变化做轻惩罚主要防止路径频繁上下抖动地形起伏大的山区我反而会设成0.01左右让A星有更多自由度去跟随地形飞行而不是强制爬升到固定巡航高度——那会白白消耗大量能量。这个参数本质上是在“高度稳定”和“地形跟随”之间做权衡没有万能数值只能根据任务需求反复试。注意修改启发函数一定要同步检查可采纳性。加了高度惩罚项后H(n)可能在某些情形下高估实际代价A星会从“最优搜索”退化成“贪心搜索”。如果任务强调最优性就不要在启发函数里加额外项而是把高度惩罚放进G(n)代价函数里这样能保持严谨性。5. 常见问题排查与性能优化5.1 高频Bug与调试经验调试三维A星比二维麻烦不少因为可视化困难路径出错不容易看出来。我把这轮调试中踩过最深的几个坑整理出来内存溢出或运行极慢。三维地图体素总量特别容易失控。我遇到过在2米分辨率下规划100米×100米×50米空间Matlab直接闪退。后来我把地图数据类型改成logical而不是double把gScore、fScore都用single精度存储内存占用立刻降了一半。OpenList频繁增删也是性能瓶颈尽量避免用结构体数组数值矩阵的预分配和复用效率高得多。路径穿透障碍物但地图显示正常。这个问题最诡异表面上看规划出来的路径在地图上绕着障碍物走但放到无人机实飞模型里就是穿墙。原因大概率是障碍物膨胀半径不够或者对角线碰撞检测没做。我在代码里加了一个后置校验脚本沿着路径逐点采样如果采样点落进障碍物体素就报警方便立刻定位问题。路径绕远或者直接卡死不动。一般是启发函数权重过高导致A星提前收敛到次优路径或者地图中存在无法通过的“死胡同”A星反复扩展却找不到出路。排查时先把启发函数权重调回1.0再把地图的连通性检查一遍从起点到目标点是否存在至少一条通道。一个简单办法是在地图生成后跑一次BFS检查连通性提前知道有没有解。算法图省事把移动代价全设为1。我前面多次强调这点因为真的是高频问题。代价不按实际空间距离处理后果就是对角线移动被严重低估A星会疯狂选择斜穿路线路径看起来又短又顺一算真实长度其实绕远了。根源是启发函数和移动代价不一致。一定记住节点间移动代价必须跟空间距离成正比。5.2 从实验室到工程落地的性能优化实验室里跑通几秒钟算完没问题但真放到无人机载电脑上算力往往很紧张。嵌入式平台的处理器性能跟台式机比差距很大所以一定要做性能优化。我分享几个实用的小技巧使用稀疏数据结构。三维地图虽然体素总量很大但绝大多数体素是空的。A星搜索过程中访问的节点有限可以在搜索时用containers.Map只记录访问过的节点而不是给整个地图都分配gScore和fScore数组。特别是大尺度地图上这个优化能把内存占用从几百兆降到几兆。用小顶堆思想优化OpenList。Matlab里没有原生的PriorityQueue但可以用函数min配合向量化操作实现类似效果。如果地图很大每次线性扫描找最小值会成为瓶颈。这时可以把OpenList改成二叉堆结构插入和弹出最小值都变成O(logN)复杂度在高密度栅格里性能提升肉眼可见。麻省理工那套使用Python的路径规划课程里用的就是标准库堆Matlab里可以手写或者用java.util.PriorityQueue接口接进来。提前计算启发函数缓存。如果多次运行A星比如在动态环境中频繁重规划启发函数值会被反复计算。三维欧几里得距离要开平方计算开销不小。可以把整个空间的启发函数预先算好存成三维数组空间换时间。100×100×50的地图也就50万个浮点数内存完全吃得消。地图重新规划时利用上一帧路径。无人机遇到动态障碍物需要局部重规划没有必把整条路径重算。一个工程上常用的做法是保留原路径上离当前点最近的节点作为子目标只对一小块局部区域做A星搜索绕过新出现的障碍物再插回原路径。这样单次重规划耗时从上秒级降到毫秒级够飞控实时用。5.3 规划结果的可视化技巧三维路径规划的项目演示和论文插图离不开可视化。Matlab的可视化做得足够好但有些细节能让图的质量拉开差距function visPath3D(map, path_grid, path_smooth, start_world, goal_world) % 三维路径可视化 % 用patch绘制障碍物表面 % 用plot3绘制原始路径和平滑路径 figure(Color, w, Position, [100, 100, 1000, 700]); % 绘制障碍物 [fx, fy, fz] ind2sub(size(map), find(map)); scatter3(fx, fy, fz, 3, [0.5, 0.5, 0.5], filled); hold on; grid on; % 绘制原始A星路径 plot3(path_grid(:,1), path_grid(:,2), path_grid(:,3), b-o, LineWidth, 2, MarkerSize, 4); % 绘制平滑路径 plot3(path_smooth(:,1), path_smooth(:,2), path_smooth(:,3), r-, LineWidth, 2.5); % 绘制起点终点 scatter3(start_world(1), start_world(2), start_world(3), 100, g, filled); scatter3(goal_world(1), goal_world(2), goal_world(3), 100, r, filled); xlabel(X (m)); ylabel(Y (m)); zlabel(Z (m)); legend(障碍物体素, A星原始路径, 平滑路径, 起点, 终点); view(45, 30); end障碍物数量大的时候一个个scatter3画散点效率很低图也显得杂乱。更好的做法是用isosurface提取障碍物边界面网格化显示后既省资源又漂亮。角度上固定在一个视角容易遮住路径全貌可以加上rotate3d开启交互旋转评审的时候能直观展示路径如何绕过障碍物。我这里还有一个增强表现力的习惯把路径按高度着色用一个彩色渐变表示不同海拔段图和论文的对比度瞬间拉高。做法也不复杂把z坐标映射到colormap色值plot3的每条线段单独设置颜色就行。6. 写在最后的小经验这套三维A星代码从前到后跑了三周我自己收获最大的一点是不要小看基础算法。网上各种新算法炒得火热但真正到了工程现场A星这种可解释、可控性强的老算法反而最让人放心。把环境建模、代价函数、启发函数这些底层的逻辑吃透了后面你再去学RRT、学强化学习都会觉得思路清晰得多。如果后续想扩展建议从三个方向入手一是给路径加动力学约束比如让B样条曲线满足最大转弯半径限制二是把地图从静态改成动态用传感器实时更新障碍物实现动态重规划三是把代价函数丰富起来融合能耗模型、威胁模型让路径规划变成真正的多目标优化问题。这是三维A星在工程应用里最有价值的延伸。最后分享一个实操小技巧调参的时候不要全凭感觉一遍遍试把网格分辨率、启发权重、安全距离设计成变量用批处理脚本循环跑自动记录路径长度、搜索时间、碰撞次数这几个评价指标。跑完一轮表格一看什么参数组合最优一目了然。这种“参数扫描自动评测”的方式能帮你少熬好几个夜。
返回列表