这个课题我前后折腾了大概三周时间,中间踩了不少坑,最后把整套A星算法三维路径规划的Matlab代码从地图建模一路做到了路径平滑和可视化。写这篇文章是想把这套东西完整复盘一遍,把我自己调试过程中遇到的问题、参数怎么选、代码结构怎么搭才不容易乱,都整理出来。如果你正在做无人机路径规划相关的选题,或者是准备拿这个方向做毕业设计、课程项目,这篇文章应该能帮你省掉不少弯路。文章默认你有一定Matlab基础,知道脚本和函数的基本写法,但算法部分我会讲得比较细,哪怕是第一次接触A星的读者跟着一步步看也能跑通。
1. 项目需求与整体思路拆解
1.1 无人机三维路径规划到底在规划什么
先说这个课题的本质。路径规划这件事,二维和三维虽然是"多了一个维度",但实际做起来差别非常大。二维平面里,你只需要考虑x和y两个方向,地图可以用一张普通的平面图表达,障碍物是二维多边形或栅格。但真实无人机飞行是三维的,它要在一个包含地形起伏、高楼、山峰、禁飞区等障碍物的立体空间里找一条从起点到终点的安全轨迹,同时这条轨迹还要尽量短、尽量平滑、符合无人机本身的飞行性能约束。
放到具体任务场景里看就很清楚了。比如一个山区电力巡检任务,无人机要从山脚停机坪起飞,飞到山顶的铁塔附近拍照,沿途有山坡、树林、高压线。二维规划只能告诉你"往东飞再往北转",但没法回答最关键的问题:这个山坡你该从左边绕还是从右边绕?飞行高度应该保持在海拔300米还是500米?遇到横亘在前方的山脊,是爬升翻过去还是横向绕行更划算?这些决策只有在三维空间里才能建模和求解。
所以这个课题的核心不是"把二维A星加一个z轴坐标就完事了",而是要解决三件事:一是在三维栅格地图里正确表达障碍物和可飞行空间,二是把A星算法从二维节点扩展逻辑推广到三维邻域搜索,三是让规划出来的路径能满足无人机的运动学约束,至少不能是那种忽上忽下、拐直角弯、贴障碍物边缘飞行的"理论路径"。
1.2 为什么选A星而不是RRT、PRM或者遗传算法
很多人在开题之前都会纠结算法选型的问题。我自己的建议是:如果做的是入门级研究或者教学演示性质的课题,A星基本是最稳的选择。我把市面上主流算法的特点整理成一个对照表,你们感受一下。
| 算法 | 完备性 | 最优性 | 实时性 | 工程难度 | 适用场景 |
|---|---|---|---|---|---|
| A星 | 完备(保证找到解) | 能保证最优(使用可采纳启发函数时) | 中等,节点多了会慢 | 低,逻辑直观 | 静态环境的确定性规划 |
| Dijkstra | 完备 | 最优 | 慢,无启发式引导 | 低 | 需要等代价搜索 |
| RRT/RRT* | 概率完备 | RRT*渐进最优 | 快,适合高维 | 中 | 高维空间、动态环境 |
| PRM | 概率完备 | 通过路线图可近似最优 | 预处理耗时,查询快 | 中 | 多次查询场景 |
| 遗传算法 | 不保证 | 不保证 | 慢且不稳定 | 高,参数太多 | 全局搜索近似解 |
A星最大的优势在于它的确定性和可解释性。同样的地图和起终点,每次跑出来的结果完全一致,不会像RRT那样每次结果都不一样,这对做实验对比、写论文、做毕设演示都是很友好的特性。另一个优势是A星的搜索过程是"有方向"的。它用了启发函数来估计当前点到终点的距离,这就像一个导航软件会优先朝着目标方向找路,而不是像Dijkstra那样一圈圈向外扩散,效率高很多。
相比之下,遗传算法这种启发式优化算法我也试过,但参数调起来非常头疼,种群数量、交叉概率、变异概率、适应度函数里每个项的权重,任何一个没调好,算法就可能收敛到一个非常离谱的"路径"上,而且你还很难解释为什么跑出这个结果。A星就朴素得多:每一步都是依据明确的代价公式选下一个节点,出了问题也好排查。
1.3 整体技术方案与代码框架设计
整体思路我拆成了五个模块:三维环境建模、栅格地图数据构造、A星核心搜索逻辑、路径回溯与平滑处理、三维可视化。这五个模块是依次依赖的关系,建议你也按照这个顺序来实现和测试,不要想着一次性把整个项目写完再调试,那会很难定位问题。
环境建模这步用来生成一个有起伏地形和凸起障碍物的三维空间,用栅格把连续空间离散化。栅格地图本质是一个三维数组,数组索引代表空间坐标,值代表该栅格是否可通行。A星核心搜索逻辑是整个项目的灵魂,输入是三维地图、起点、终点,输出是一条由相邻栅格节点组成的原始路径。路径平滑处理在上面基础上生成可执行的平滑曲线,可视化部分用Matlab的绘图函数把地图、障碍物、路径和关键节点都画出来。
Matlab这个平台说实话不是性能最优的选择,Python的路径规划生态更丰富,C++更是工程级的方案。但这个课题用Matlab有个无可替代的好处:矩阵运算和三维可视化是Matlab的强项,而且它天然支持在一个脚本里边计算边画图,用于算法原型验证和数据可视化交流非常合适。你不需要搭建复杂的工程环境,装好Matlab就能跑。
2. 核心原理与关键技术解析
2.1 A星算法核心公式与搜索流程详解
想把A星用好,先要把它的计算公式彻底吃透。算法的灵魂就一个式子:
[ f(n) = g(n) + h(n) ]
其中f(n)是节点n的综合代价值,g(n)是起点到当前节点的实际移动代价,h(n)是当前节点到终点的启发式估计代价。A星每一步要做的事情很朴素:维护一个开放列表(open list),存放待考察的节点;再维护一个封闭列表(closed list),存放已经考察完毕的节点。然后不断从open list中取出f值最小的节点来扩展,直到取出的节点恰好是终点为止。
可以这样理解:g值代表"我已经走过的路花了多少成本",h值代表"我还需要花多少成本的估计",A星每次都在所有已经发现但还没走的路口里选"已花成本+预估剩余成本"最小的那个方向往前走。这就兼顾了两个目标:既不是完全跟着感觉走(那叫贪心算法),也不是盲目地把每个方向都走一遍(那叫Dijkstra算法),而是在感觉和实际情况之间找到一个动态平衡。
要保证A星找到的路径一定是最优的,启发函数h(n)必须满足可采纳性,即h(n)的估计值不能超过从节点n到终点的真实最小代价。这是很重要的一个前提,后面我讲启发函数设计的时候会再展开。
2.2 从二维到三维:栅格地图与节点扩展机制的改造
二维A星里,一个节点最多有8个邻居(上下左右加四个对角),而在三维空间里问题就变得不一样了。三维栅格里一个中心节点周围最多有26个邻居,分别分布在三个维度的组合方向上:6个面邻居(上下左右前后),12个边邻居(两个坐标轴组合的对角方向),8个角邻居(三个坐标轴都有变化)。如果你的代码支持斜向移动,那就要考虑26邻域,如果只允许水平和垂直方向,那就是6邻域。
26邻域的具体坐标偏移可以这样生成:让dx、dy、dz分别取-1、0、1三个值,并且排除掉全为0的情况(那是自身),剩下的27-1=26种组合就是全部邻居偏移。用循环嵌套遍历,判断当时的dx、dy、dz是不是全部等于0,不是的话就累加进邻居集合。把这些偏移量加到当前节点坐标上,就得到了邻居节点的坐标。
分别对应到不同移动方向的代价也要算清楚。按栅格边长为1,面邻居的移动代价是1,边邻居对应的是对角线,距离是根号2,角邻居是三维对角线,距离是根号3。如果用欧氏距离作为代价,这正好是三个不同值。如果不做区分统一按1来算,虽然地图小的时候问题不大,但路径会不准,可能偏离最优解。
2.3 启发函数设计与无人机运动约束的融合
启发函数的选择直接影响A星的搜索效率和结果质量。三维空间里最常用也是最自然的启发函数就是三维欧氏距离,直接计算当前节点和终点之间的直线距离。这个函数天然可采纳,因为空间中任意两点的最短路径下界就是直线距离,任何路径都不可能比直线更短,所以用欧氏距离做h值一定不会高估。
但实际使用中我会给h加一个加权系数,这是工程上常用的做法。记作f = g + w * h,w的取值会影响算法行为。w=1时是标准A星,保证最优;w>1时算法会更激进地朝终点方向搜索,速度变快但可能牺牲最优性;w<1时算法会退化成接近Dijkstra,比较保守。我自己的测试经验是w取1.05到1.3之间是比较合适的区间,搜索速度提升明显,路径长度损失通常可以控制在3%以内。对无人机路径规划这种场景,3%的路径增代价完全是可以接受的。
无人机本身的飞行约束怎么融合进A星里?这一步很多初学的人都会忽略。A星搜索出的路径如果出现了太大的转折角度或者骤升骤降,无人机是飞不出来的。常用的做法是在节点扩展阶段加入约束判断:计算当前节点到邻居节点的爬升角,如果超过了无人机的最大爬升角/俯冲角限制,就直接把这个邻居从候选集合中剔除。这个约束判断相当于把你关心的硬性物理限制提前融入了搜索过程,搜出的路径天然满足这些约束,不用后处理阶段再去痛苦地修复。
3. Matlab实现过程与代码核心细节
3.1 三维栅格地图建模与障碍物设置
我先把地图部分的核心代码框架写出来。地图用三维数组mapData来存,x、y、z分别代表空间三个维度,数组值为0表示该栅格被障碍物占据,值为1表示可通行。地图大小我例子用的是100x100x20,即水平面100米乘100米,高度20米,栅格分辨率1米。
% 初始化三维栅格地图,全1表示初始状态全部可通行 mapData = ones(100, 100, 20); % 在随机位置生成若干个长方体障碍物 rng(42); % 固定随机种子,保证结果可复现 numObstacles = 15; for i = 1:numObstacles centerX = randi([10 90]); centerY = randi([10 90]); centerZ = randi([4 15]); sizeX = randi([4 8]); sizeY = randi([4 8]); sizeZ = randi([3 6]); xRange = max(centerX - fix(sizeX/2), 1) : min(centerX + fix(sizeX/2), 100); yRange = max(centerY - fix(sizeY/2), 1) : min(centerY + fix(sizeY/2), 100); zRange = max(centerZ - fix(sizeZ/2), 1) : min(centerZ + fix(sizeZ/2), 20); mapData(xRange, yRange, zRange) = 0; end更接近真实场景的地图是带地形起伏的。用几个山峰函数叠加生成数字高程模型,高度高的地方设置为障碍物,模拟真实地形,再用随机柱状障碍物模拟建筑物或者树木。演示的时候我会人为设置一个狭窄通道,看算法能不能找到通道穿过去,这个对验证算法有效性很有说服力。
固定随机种子这一点值得多说一句。至少我测试的时候遇到过这种尴尬:明明排名靠前的代码跑出来效果很好,一换机器或者换一次运行,随机生成的障碍物位置变了,结果跟着变,看起来算法好像不稳定。固定住种子,地图和实验结果都可复现,比较方便后续逐个调参。
3.2 节点数据结构与open list的实现方案
节点数据结构是A星代码里第一个要注意的点。每个节点至少要存储四类信息:三维坐标、起点到它的实际代价g值、它到终点的估计代价h值(或者直接存f值也行)、父节点索引。Matlab里没有C++那种struct数组和指针的天然语法,我用Matlab struct数组来存,父节点用线性索引来指向。
open list怎么管理,我试过三种实现方案:
第一种方案是直接用数组存所有待扩展节点,每次找f值最小节点就全数组扫一遍,这个实现最直观。我一开始也这么写的,后来把地图加大到200x200x40,扫描的时间开销变得非常可观,扩展开销直接卡在open list的查找上。第二种方案是维护一个按f值排序的数组,每次插入新节点时二分查找再插入,提高了取节点速度,但插入和删除需要数组挪动,整体效率还是不够高。第三种方案是用Matlab的Java库PriorityQueue,这算是Matlab性能优化的一个小技巧,Java优先队列的堆结构插入和取出都是对数级别,地图大一些也扛得住。但Java对象转型比较麻烦,代码可读性差一些。
我自己最终用的是方案一:数组加全遍历。原因很简单,100乘100乘20这种量级的地图节点数在10万量级,但A星实际探索的节点数远小于地图总节点数,通常只有几千到几万个,这个规模的数组遍历在Matlab里也就几十毫秒的量级,完全能接受。为了写得更快更稳,我还加了一个剪枝操作:如果待插入节点已经存在于open list中,并且新的f值更小,就更新它;如果节点已被closed list收录,就直接忽略。这样能防止重复扩展,内存占用也能控制住。
3.3 A星主循环、路径回溯与算法收敛判断
先把核心主循环的结构放这里,这是整个程序的关键部分,一行一行读下面解释。
% 记录起点和终点在三维数组中的索引 startIdx = sub2ind(size(mapData), startPos(1), startPos(2), startPos(3)); goalIdx = sub2ind(size(mapData), goalPos(1), goalPos(2), goalPos(3)); % 初始化g值和f值矩阵,先全部置为无穷大 gScore = inf([size(mapData), 1]); fScore = inf([size(mapData), 1]); gScore(startIdx) = 0; hStart = computeHeuristic(startPos, goalPos); fScore(startIdx) = hStart; % open list用数组保存,每个元素是一个节点索引 openList = startIdx; % parentMap用来回溯路径,记录每个节点的父节点索引 parentMap = zeros(size(mapData), 'uint32'); % 8个标准方向(二维主搜索)和18个三维方向标志位 % 这里我用二维主循环+三维扩展的写法便于理解 while ~isempty(openList) % 在openList中找出fScore最小的节点 [~, minIdx] = min(fScore(openList)); current = openList(minIdx); % 如果当前节点就是终点,停止搜索 if current == goalIdx disp('路径搜索成功'); path = reconstructPath(parentMap, startIdx, goalIdx); break; end % 从openList中移除当前节点 openList(minIdx) = []; % 并将当前节点放入closedList(用状态矩阵标记) closedList(current) = true; % 获取当前节点的邻居索引列表 neighbors = getNeighbors3D(current, mapData, closedList); for i = 1:length(neighbors) nb = neighbors(i); % 假设以欧氏距离作为当前节点到邻居节点的移动代价 tentativeG = gScore(current) + ... norm(getCoordFromIdx(nb) - getCoordFromIdx(current)); if tentativeG < gScore(nb) parentMap(nb) = current; gScore(nb) = tentativeG; fScore(nb) = tentativeG + computeHeuristic(getCoordFromIdx(nb), goalPos); if ~ismember(nb, openList) openList(end + 1) = nb; %#ok<AGROW> end end end end主循环里最关键的判断就是这个if tentativeG < gScore(nb)。它做的是"松弛"操作:如果发现走当前这个新方向到达邻居节点nb,比之前记录的到达nb的代价更小,就更新nb的父节点指针、g值和f值。整个A星的"聪明"之处就藏在这个小小的判断里——它保证每个被加入open list的节点,记录的始终是目前已知的最优到达方式。
路径回溯函数是从终点开始,通过parentMap一层层往回找父节点,直到回到起点,再把顺序反一下就是完整的路径。逻辑一行都不能错,错了很容易陷入死循环或者找回一条不在路上的"鬼路径"。回溯得到的path是栅格索引序列,后续转坐标用ind2sub分开得到x、y、z数组。
边界条件有两个值得提前写好防御性代码。第一种情况是起点或终点本身就落在障碍物栅格上,这种直接在函数开头返回错误即可;第二种情况是开阔区域里open list都搜空了还没找到终点,提前做一个判断,输出"路径不存在"的提示,不要硬跑到最后数组越界报错。
3.4 三维可视化:把地图和路径画出来
Matlab做三维可视化是真的很顺手。我用scatter3画障碍物的立体散点,用plot3配合LineWidth画规划路径,再用text标注起点和终点。再加上视角控制,输出图片既清晰又适合放进论文里当结果图。
% 障碍物可视化:用find找出三维数组中值为0的栅格位置 [ox, oy, oz] = ind2sub(size(mapData), find(mapData == 0)); scatter3(ox, oy, oz, 8, [0.4 0.4 0.4], 'filled', 'MarkerFaceAlpha', 0.3); hold on; % 起点与终点用不同颜色标记 scatter3(startPos(1), startPos(2), startPos(3), 120, 'g', 'filled', 'DisplayName', '起点'); scatter3(goalPos(1), goalPos(2), goalPos(3), 120, 'r', 'filled', 'DisplayName', '终点'); % 规划路径用粗蓝线绘制 plot3(path(:,1), path(:,2), path(:,3), 'b-', 'LineWidth', 2.5, 'DisplayName', 'A星规划路径'); xlabel('X / m'); ylabel('Y / m'); zlabel('Z / m'); legend('show'); grid on; axis equal; view(45, 30); % 设置三维视角这段代码跑出来基本就是论文里的效果图了。灰色半透明体素是障碍物,绿色星号是起点,红色星号是终点,蓝色粗线是算法规划出来的路径。
这里建议你加一个动态展示:循环遍历路径的每一个节点,每次用plot3画已经走过的路径段,并且用drawnow更新画面,就能做成一个无人机沿线飞行的动画效果。这功能对答辩演示特别有冲击力,做起来也就是几行循环的事,比静态图片好讲非常多。
4. 参数调优、结果分析与避坑实录
4.1 核心参数对规划效果的影响
参数完全是实验试出来的。我把我测过的情况整理一下,每个参数是怎么影响结果的,直接看图说话。
栅格地图的尺寸分辨率是第一个关键参数。栅格越细,理论上能找到更优质的路径,因为空间被离散化得更精细,窄缝也能穿过,但同时会带来计算量和内存的立方级增长。100x100x20的地图跑A星非常轻松,毫秒级到秒级出结果;升级到200x200x40,搜索节点数可能直接多一个数量级,运行时间有时会涨到几十秒。我自己常用的折中方案是:先用较粗的栅格(例如2米分辨率)快速得到一个大致路径确认算法是否连通,然后局部细化到0.5米再做一次精搜。分层的思路在路径规划里很实用,推荐试一下。
启发函数的权重w我在前面提到了,这里给一组我实测对比的数字。障碍物密度中等的地图,100x100x20尺寸,起点和终点处于地图对角的位置,w从1增加到1.2,节点扩展数能减少约40%,运行时间缩短超过一半,路径长度只增加了大约2.1%。所以如果不是论文需要论证最优性,默认用1.2的权重是最划算的。
邻居扩展方式影响也比较明显:6邻域速度快,但路径会非常僵硬,只有三个轴方向能走,zigzag严重,而且往往找不到最优解;26邻域效果好很多,但有些短斜线航向角变化频率非常高,后面平滑会很难处理。如果先用18邻域(去掉一些三轴都变化的角方向),在路径平滑度和搜索效率之间能取得更均衡的效果。这个要根据你自己的需求判断。
障碍物膨胀半径是工程上不能忽略的环节。A星是栅格级的,路径只保证栅格本身不撞障碍,但无人机是有物理体积的,需要预留安全距离。简单做法是在建地图时把障碍物周围半径r个栅格都标记为不可通行,r按无人机半径加安全余量除以栅格分辨率来取。比如无人机半径0.5米,安全余量0.5米,栅格分辨率1米,r就取1或者2。这个膨胀操作能避免路径贴着障碍物表面擦过去。
4.2 我实际踩过的几个坑
第一个坑是没做障碍物膨胀,路径视觉上看起来贴着障碍物飞。后来做路径平滑时加了个贝塞尔曲线拟合,结果曲线拐弯半径不够,直接插进障碍物里去了。这让我意识到路径规划不只是"找一条不撞栅格的线",而是要为后端的平滑和真实飞行留足安全余量,膨胀这个步骤不能省。
第二个坑是26邻域搜索导致的"穿角"问题。二维里有个经典问题叫"穿墙",说的是如果允许对角移动,路径可能穿过两个相邻障碍栅格的公共角点。三维里这个问题被放大了:26邻域允许直接从一个栅格移动到斜对角的栅格,但如果斜对角的路径正好穿过两个相邻障碍栅格共享的边或者角,就会产生视觉上和物理上都过不去的"穿模"路径。排查出来之后我在邻居扩展函数里加了一个碰撞穿透检测:对斜向移动额外判断它经过的中间栅格是否全部可通行,不可通行就不让走。
第三个坑是临时地图测试时起点或者终点误设成了障碍物内部。我把起点设在了一个虚拟机库里,然后整整花了很久排查"为什么A星跑出来这么奇怪",又是看启发函数又是查open list顺序,最后发现就是起点坐标选错。更无语的是,这种错误A星不会直接报错,因为它会把起点作为合法的可扩展节点继续往四周搜,搜索过程中会遇到各种奇怪的代价异常。所以第一步写一个输入合法性检查非常重要。
第四个坑是关于Matlab性能的。前面提到open list用数组遍历,地图尺寸小一点问题不大,但我中途做过一个较大规模测试,300x300x60的体素,open list里几万个元素,每轮都做全数组min查询,整个过程肉眼可见地卡。后来把数组查询改成了"矩阵化"写法,一次向量化操作取最小值,性能立刻提升好几倍。Matlab里尽量少写for循环多利用向量化运算,这是无数踩坑换来的经验。
4.3 常见问题速查表
我把做这个课题期间遇到的典型问题整理成了一张速查表,如果你在跑代码时碰上类似情况,直接对着查原因。
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 搜索很慢,几十秒都不出结果 | open list用循环遍历找最小值;地图栅格太细;h函数权重大小 | 用向量化min查询;调粗栅格分层规划;调节w权重在1.1~1.3 |
| 路径穿过了障碍物角点 | 邻居扩展允许斜向移动但缺少碰撞穿透检测 | 在getNeighbors3D里加"穿过栅格是否通行"判断 |
| 找不到路径(open list提前清空) | 起点/终点在障碍物内;地图存在不可达区域;膨胀半径太大导致通道全堵死 | 检查输入合法性;缩小膨胀半径;换更细栅格生成通道 |
| 路径锯齿感严重,拐弯点多 | 26邻域导致航向频繁变化;没有做路径平滑 | 改用18邻域;对路径做B样条或贝塞尔平滑 |
| 每次运行结果都不一样 | 地图随机生成但没有固定随机种子 | 用rng(固定数字)固定种子,让实验可复现 |
| 路径在三维视图里看起来高度剧烈起伏 | 没有加入爬升角约束 | 在邻居扩展阶段加最大爬升角/俯冲角判断 |
4.4 路径平滑:让规划结果真正可飞
最后一步平滑处理常常被忽略,但这恰恰是无人机能不能按路线飞的关键。A星直接输出的路径是折线,说白了就是相邻栅格中心连线,飞行时如果真按这个轨迹飞,每到一个拐点就要做一个急剧的转角操作,真实无人机完全做不到,会直接冲出航线。
我用的平滑方法是B样条曲线。原理可以理解成:拿规划出的折线路径当"控制点",让一条平滑曲线从起点走到终点,中间被控制点"牵引"但不一定要全部穿过。这样做的好处是路径变得平滑了,代价是有可能稍微偏离原始的安全路径,所以平滑之后要做一次重碰撞检测,如果有穿插障碍的情况,就减少平滑力度或者局部调整控制点再试。
Matlab里做B样条平滑可以直接调用spapi或csaps函数,也可以用三次样条cscvn,输入原始路径点,输出插值后的平滑轨迹。我给个最简的示例:
% 以三次样条插值为例,对原始路径做平滑 smoothPts = cscvn(path'); fnplt(smoothPts, 'r', 2);关键点在平滑后必须再次检查安全性。我会把平滑曲线的密集采样点和障碍物栅格逐一比对,确认没有碰撞再输出最终轨迹。这一步在论文里可以单独作为一小节结果展示,很有说服力。
5. 一些个人体会
这项目做下来,角角落落都体验了一遍,最后真正跑通、画出漂亮的四维轨迹图的时候,确实有一种松了口气的感觉。我自己最大的感受是:A星算法本身并不复杂,把原理搞懂的难度远没有把工程细节调好那么难。真正花费时间的都是"地图怎么建才合理、约束怎么融进搜索里、路径出来之后怎么让它平滑可飞"这些不上教科书的地方。
如果你也是拿这个题目做毕业设计或者课程项目,我建议按这个节奏来推进:先不要急着写大段代码,花两天时间把地图可视化和A星二维版本的代码跑通,确定正确性之后再加z轴,每一步都有可视化输出,遇到问题一眼就能看出来是哪一环出错。地图一开始不要设太大,30x30x10就够用,跑通之后再逐渐加大。
后续往哪个方向扩展,可以想想:现在规划的前提是环境完全已知、静态不变的,如果无人机的传感器在飞行过程中发现新障碍物,A星就需要重规划,那就引出了动态环境下的D星算法思路;如果还要考虑多台无人机协同,那就需要引入编队约束和多机防碰撞逻辑。这些方向都是从这个基础版本延伸出去的,目前这个代码框架已经给你搭好了地板,想往哪个方向走都有路可走。