搞无人机三维路径规划,最头疼的不是把起点到终点连一条线,而是要在一个有山包、楼宇、禁飞区,甚至还有移动障碍物的三维空间里,找出一条既安全、又相对好飞、还能实时响应的轨迹来。这篇文章要聊的这套基于启发式算法的动态避障方案,解决的就是这个事。先交代一下这套东西的实际构成:用启发式搜索为主干,在三维栅格地图上做路径搜索,再加上对移动威胁的动态检测和局部重规划,整个框架用 Matlab 实现。给飞行器做任务规划、搞集群编队路径预研,或者是在做比赛仿真验证的人,可以把这一整套直接拿去改。下面我按“问题拆解 → 核心原理 → 代码实现 → 踩坑实录”这个顺序把它讲透,不绕弯子。1. 项目需求与整体设计思路1.1 先搞明白“三维 + 动态”到底难在哪很多做二维路径规划的人,第一次转到三维时都会吃一个暗亏:以为只是把坐标从 (x, y) 变成 (x, y, z),算法逻辑抄一遍就行。实际上三维后算法的搜索空间从二维的网格面一下子变成了体素空间,节点数呈立方级增长。先说静态部分。城市峡谷、山地地形、风电场这些场景里面,无人机不能像平面导航那样“绕着走就行”,它需要同时考虑前后、左右、上下六个方向的通行性。比如一座山包,二维投影可能只是一块禁区,但三维里它可能是可爬升通过,也可能是必须绕行的陡坡,这个区别只能在三维空间里体现。再说动态部分。动态避障的难点不是探测到障碍物就行,而是要处理“运动预测 + 时间协同”。一个移动障碍物,比如另一架无人机或者一个高空吊装物,按什么轨迹移动、速度多少、什么时候和本机到达同一空间点,这些都是动态避障要回答的问题。如果把动态障碍物当作静态障碍物处理,规划出的路径会过于保守,甚至出现无解。所以整体设计上,我把问题拆成两层:全局规划层:用启发式算法在已知静态地图上找一条粗粒度路径,负责整体最优性。局部重规划层:在飞行过程中检测进入探测半径的动态威胁,局部调整路径,负责安全性。这两层在 Matlab 里分别对应两个模块,后面讲代码时会详细说。1.2 为什么选启发式算法(而不是图搜索或强化学习)这个问题经常被人问:三维路径规划明明可以用 RRT、A* 这类采样搜索法,也可以用深度强化学习端到端训练,为什么偏要选启发式算法?我的观点是,看场景看约束。RRT 和它的变体 RRT* 在三维空间里确实搜索效率不错,但生成的路径随机性太强,平滑性和重复性差,尤其在动态避障里,同样的起始条件跑两次,路径差异可能非常明显。A* 在栅格地图上确定性好,但代价是内存和搜索时间。深度强化学习很时髦,但需要大量训练样本、超参调试周期长,而且泛化能力在真实环境里很难保证。启发式算法的优势在于它的“场景适应性”。比如粒子群算法 PSO 可以通过种群迭代快速收敛到一条可行路径,它的编码方式天然适合处理连续坐标;遗传算法 GA 擅长全局寻优,不容易陷入局部最优;蚁群算法 ACO 的自组织特性在静态图搜索里表现很好。这套方案里,我主要用的是粒子群和遗传算法的混合策略,静态全局搜索交给遗传算法保障全局性,在局部动态避障时用粒子群的快速迭代能力把路径抢出来。关键点是:启发式算法不依赖整个地图的全局梯度信息,只要把代价函数定义好,它就能跑。这一点在三维体素空间里特别重要,因为真实的三维环境往往有大量不规则障碍物,数学上很难写成凸函数。1.3 方案的整体技术路线这套方案按下面五步走:地图预处理:读取地形高程数据或随机生成三维体素栅格,标记障碍物。航迹编码:把三维路径表示为一系列航迹点序列,使用 B 样条或线性插值转成平滑曲线。代价函数设计:综合路径长度、安全距离、飞行高度一致性、转弯约束。启发式寻优:遗传算法全局搜索 + 粒子群局部优化。动态避障模块:实时检测动态威胁,触发局部重规划。这里要特别强调第 3 步,因为很多论文的代价函数写得天花乱坠,实际代码里只用了路径长度一个指标。代价函数的设计直接影响启发式算法能不能收敛到一条真正好飞的路径,后面我会拿一条实际跑过的 MATLAB 代码来说。2. 核心原理与关键细节2.1 三维路径的表达与代价函数设计一条三维路径,不管抽象成什么形式,最后都要变成无人机执行时能用的一组航迹点。在这套代码里,我采用的是“节点序列 + 插值”的结构:先用 N 个离散点表示路径的粗骨架,再用三次 B 样条做平滑。假设起点为 P_start,终点为 P_end,中间插入 M 个路径点,整条路径就是 M+2 个点的序列。每个点有三个坐标分量 x、y、z,所以一个粒子或一条染色体在算法里就是一个长度为 3×(M+2) 的向量。这种编码方式很直观,也方便后续加约束。代价函数我建议至少包含四个部分:路径长度代价:所有相邻点之间的欧氏距离累加,这是最基本的要求。安全距离代价:判断路径点与障碍物体素的最小距离,小于安全阈值时就施加一个大的惩罚项。高度变化代价:无人机频繁爬升下降既费电又不好飞,对相邻点高度差的绝对值做累计。转弯角代价:相邻三个点构成的空间夹角如果小于最小转弯限制,说明飞行方向变化太急,也要惩罚。一个常用的代价函数形式是:J = w1 * L_path + w2 * f_safe + w3 * H_change + w4 * T_anglew1 到 w4 是权重系数。我的实测建议是:安全项比重最大,至少占总权重的 50%,因为算法搜索过程中会出现大量贴着障碍物飞行的“看似短实则危险”的路径,如果不把安全权重提上去,它根本不会主动找绕行的路。2.2 动态障碍物建模与避障触发条件动态避障在 Matlab 仿真里常用的做法是给移动障碍物一个运动模型。我在代码里规定每个动态威胁用一组参数描述:初始位置 center0速度向量 vel影响半径 R运动时间 t在 t 时刻,障碍物的球心位置是 center = center0 + vel * t。判断无人机路径点与动态障碍物是否冲突,就看该时刻无人机的坐标与障碍物球心距离是否小于 R。但这里有个很容易踩的坑:动态障碍物是移动的,你规划出来的路径点数量有限,如何判断整条路径是否会撞上移动障碍物?我采取的方法是时间戳采样法。为了让判断足够可靠,我在路径点之间做细分,每隔 dt 秒计算一次飞行器位置和障碍物位置的相对距离。飞行器位置通过路径点插值得到,障碍物位置按运动模型推算,然后逐点检测。避障触发条件很简单也很实用:只要检测到未来某个时间窗口内存在距离小于安全距离的时刻,就启动局部重规划。注意,重规划不是把整条路径全局重算,那样太慢。我是以当前点为起点,以全局路径上位于障碍物后方的某个航迹点为终点,用粒子群快速迭代一小段局部路径,把动态障碍物绕开后再回到全局路径上去。2.3 为什么需要平滑处理与末端约束先说一下平滑处理,这是最容易被人质疑“你怎么保证你的路径无人机真的能飞”的地方。搜索算法输出的原始路径是折线,折线转折处无人机如果直接执行,会产生极大加速度,姿态响应根本跟不上。所以平滑处理其实承担了两件事:第一是把路径变成曲线,让无人机能平稳跟踪;第二是保证起点和终点的航向约束,不让曲线在两端产生异常的偏转。我用了 B 样条曲线做平滑,具体是三次 B 样条。原因有两个:一是它对局部修改敏感度低,改动一个控制点不会让整条曲线扭曲,这正好配合局部重规划;二是它的数学计算简单,Matlab 里直接用 spapi 或自己写基函数都行。还有一点,末端约束不能省。如果你的任务是让无人机降落到指定点,路径末端必须保证位置精度和姿态精度。我在代码里把终点固定为一个硬约束,不管怎么迭代优化,终点坐标不参与变异和速度更新,这样就保证最后一定能落得到目标点。3. Matlab 代码实现与实操过程3.1 地图生成与参数初始化第一步先把地图生成工具写好。我这里用两种方式:一种是加载真实地形的 DEM 数据,另一种是随机生成三维栅格地图。随机地图的好处是方便做多次蒙特卡洛测试,验证算法稳定性。% 基本参数设置 map_range = [0 1000; 0 1000; 0 300]; % 地图范围 [xmin xmax; ymin ymax; zmin zmax] grid_res = 10; % 栅格分辨率 safe_dist = 15; % 安全距离阈值 % 随机生成圆柱形或立方体障碍物 obs_num = 20; obs_list = zeros(obs_num, 4); for i = 1:obs_num obs_list(i,1) = rand * (map_range(1,2)-map_range(1,1)) + map_range(1,1); obs_list(i,2) = rand * (map_range(2,2)-map_range(2,1)) + map_range(2,1); obs_list(i,3) = rand * (map_range(3,2)-map_range(3,1)) * 0.3 + 20; obs_list(i,4) = 20 + rand * 40; % 障碍物半径 end常用固定障碍物数据,也可以用栅格法:% 三维栅格图 grid_map = zeros(100,100,30); for i = 1:100 for j = 1:100 for k = 1:30 if is_obstacle(i,j,k) grid_map(i,j,k) = 1; end end end end在实际代码里我建议把地图做成结构体或者类,例如env.Map3D、env.obs_list、env.dyn_obs,这样后面的算法函数调用时不用反复传参。这个问题很小但很提升代码可读性。3.2 遗传算法实现全局路径搜索遗传算法的核心步骤是:种群初始化 → 适应度评估 → 选择 → 交叉 → 变异 → 迭代。种群初始化这块有一个注意点:不能全是纯随机点,否则初始种群适应度太差,收敛极慢。我习惯在初始化时加入一条直线插值路径作为种子个体,其它个体在直线附近做随机扰动。population_size = 50; chrom_length = 8; % 中间路径点个数 start_point = [100 100 80]; end_point = [900 900 120]; % 直线插值初始路径 ref_path = [linspace(start_point(1), end_point(1), chrom_length+2)', ... linspace(start_point(2), end_point(2), chrom_length+2)', ... linspace(start_point(3), end_point(3), chrom_length+2)'];适应度函数就是前面说的代价函数 J,不过要转换成“越小越好”的形式,所以 GA 里也用同样的函数,直接取倒数或者取负值作为适应度。交叉操作我采用的是均匀交叉和单点交叉结合。单点交叉逻辑简单:随机选一个交叉点,交换两条染色体交叉点后面的基因片段。但三维路径有一个特殊问题,就是直接交换片段后,路径点可能出现不连续的大跳跃,因此交叉后要做一次越界修正检查,把超出地图范围的坐标拉回来。变异操作要更精细。普通变异是随机改变某个节点的坐标,但这样扰动太大,很容易让路径变成“之”字形来回摆动。我更推荐用一个服从正态分布的随机位移,即以原节点为中心,在周围一个较小半径内扰动:mutate_range = 30; new_pos = old_pos + randn(1,3) * mutate_range;每一步迭代后都记录当前最优路径,并在满足最大迭代次数或适应度不再明显变化时停止。我在代码里设定的终止条件是:迭代 200 次并且连续 30 次最优适应度变化不超过 1%。这个条件是为了防止算法在迭代后期空转,浪费时间。3.3 粒子群算法的局部重规划实现粒子群算法在这里的角色是动态避障重规划。它和遗传算法相比,迭代速度快、参数少,适合处理时间敏感性高的局部规划任务。粒子群的核心公式是三件套:速度更新、位置更新、个体/全局最优更新。w = 0.7; % 惯性权重 c1 = 1.5; % 个体学习因子 c2 = 1.5; % 社会学习因子 max_iter = 60; particle_num = 30; dim = 3 * local_node_num; % 局部路径节点数 * 3 % 速度与位置初始化 v = zeros(particle_num, dim); x = repmat(ref_local_path, particle_num, 1) + randn(particle_num, dim) * 10; pbest = x; gbest = x(1,:);这里有个细节:dim = 3 * local_node_num。有些初学者会直接把局部路径的所有坐标都作为粒子维度,结果维度太高,粒子群搜索效率很差。经验值是局部路径只取 3~5 个中间点就够了,然后用三次样条插值把路径补密。这样既保留了搜索自由度,又大幅缩减了优化维度。动态避障重规划的触发逻辑如下:在每一段路径执行前,计算当前位置,更新动态障碍物位置。检查与动态障碍物的最小距离,如果小于安全距离,进入重规划。以当前点为起点,以全局路径上距离障碍物后方两到三个节点处的点为终点,利用粒子群生成平滑的绕飞路径。粒子群的代价函数要额外加入一个动态威胁项:对所有采样时刻,无人机位置与动态障碍物位置的距离都必须大于安全阈值。这项的惩罚权重要定得很高,我一般设成安全距离违例时施加 50000 的固定惩罚,防止粒子群为了长度最短而硬穿障碍。3.4 可视化与结果输出三维路径规划如果不做可视化,代码跑完了你根本没法判断路径合理不合理。我在项目里做了一个简单的三维场景展示函数,它会把地形表面、障碍物、规划路径、动态障碍物轨迹一次性画出来。figure; plot3(path(:,1), path(:,2), path(:,3), 'b-o', 'LineWidth', 2); hold on; % 障碍物绘制 for i = 1:size(obs_list,1) [X,Y,Z] = sphere(20); X = X * obs_list(i,4) + obs_list(i,1); Y = Y * obs_list(i,4) + obs_list(i,2); Z = Z * obs_list(i,4) + obs_list(i,3); surf(X,Y,Z, 'FaceColor', [0.6 0.6 0.6], 'EdgeColor', 'none', 'FaceAlpha', 0.7); end xlabel('X/m'); ylabel('Y/m'); zlabel('Z/m'); grid on; axis equal;可视化输出除了看路径形状外,更重要的是看距离曲线。我每次仿真结束会额外输出最小安全距离、路径总长度、平均高度变化、转弯次数这几个指标,用来判断算法调参后效果是否有提升。对比实验时这些指标是唯一可量化的标准,光靠肉眼看图不靠谱。4. 常见问题与调参经验4.1 两条最容易踩的坑:陷入局部最优与路径抖动遗传算法和粒子群算法是随机算法,所以陷入局部最优简直是家常便饭。我遇到过最典型的情况:地图上有一条绕远但平坦的走廊,算法死活不肯走,一直沿着直线方向贴着一片障碍物边缘生成锯齿状路径。想解决这个问题不能盲目增加迭代次数,那只会让时间白白浪费。我更推荐动态调整变异率和惯性权重。具体操作是:迭代前 60% 的阶段,变异率保持 0.2 的高位,增加种群多样性;到了后期,变异率降到 0.05,让算法专心收敛。粒子群的惯性权重 w 也应该从 0.9 线性降到 0.4。路径抖动和局部最优是两回事,它的典型表现是:整体大方向正确,但每段路径都有一两米的微小波浪。这个不是随机噪声,而是变异操作导致的局部坐标摆动。解决办法有两个,一个是把变异范围减小,另一个是在路径平滑时提高 B 样条的平滑权重。我实测后推荐首选第二个,因为平滑处理不会干扰搜索性能。4.2 动态避障模块的三大隐藏问题第一个隐藏问题是时间同步。重规划后的局部路径长度变了,到达时间也变了,这时候再用旧时间轴去预测动态障碍物位置,会全部错位。我处理的办法是:每次重规划后重新预估局部路径的飞行时间,然后把时间轴重置。第二个隐藏问题是障碍物速度过快。如果动态障碍物的速度远超本机的机动能力,任何重规划算法都无济于事。这类情况在仿真阶段就应该筛掉。我在初始化动态障碍物参数时,要求它的速度不超过无人机巡航速度的一半,否则直接报错提醒数据不合理。第三个隐藏问题比较阴间:动态障碍物群。多个移动障碍物同时出现在重规划窗口内时,按单一障碍物处理会顾此失彼。我的做法是重规划时一次性把视野范围内的所有动态障碍物都带入代价函数,统一评估。粒子群的优点在这里就体现出来了,它可以在一次迭代里同时避开多个运动威胁,而不需要像反应式避障那样一个一个处理。4.3 从仿真到实机部署的现实差距如果你只做仿真,那么以上内容基本够用了。但如果想往实机上移植,有三点必须提前想清楚。第一,Matlab 脚本算出的路径通常上传到飞控后要经过路径平滑模块的二次处理,因为飞控不会吃你给的点,它只会把点作为导航引导点,内部再做轨迹生成。你要确保你的路径给到飞控后不会因过度拟合而出现偏航。第二,动态障碍物的参数在真机里不可能像仿真那样精确。视觉或雷达估计出的位置速度都有噪声,所以仿真里设置安全距离的时候不能只按机体物理尺寸设,至少还要加上两倍的传感器误差作为冗余。第三,Matlab 代码的实时性不够。真机上的局部重规划通常用 C++ 或者直接在飞控里用更精简的算法实现。我做移植时,会把 Matlab 仿真当成算法验证和参数整定的工具,跑通之后换成 C++ 的矩阵运算库重写核心函数。5. 改进方向与扩展建议这部分算是我自己后续打算做的一些扩展,写出来供大家参考。5.1 引入时间窗与多机协同避障现在这套方案是针对单机的动态避障。如果要做多机协同,就要把时间维度更显式地引入路径表达。也就是每个路径点不仅要有坐标,还要有到达时间。多机避障的核心从“避障碍物”变成了“避时-空冲突”,代价函数里增加与其他无人机的最小相对距离项即可。启发式算法处理这个问题有明显优势,因为粒子之间天然就是多个解,稍加改动就能变成分布式求解。5.2 结合风场与能量约束真实气象环境里,无人机三维路径规划要面对风场影响。顺风、逆风、侧风会直接改变飞行能耗和速度。可以考虑在地图模型中叠加一个风速矢量场,把“逆风飞行”的代价加权到路径长度代价里。这样做出来的路径,虽然空中距离可能更长,但续航实际更久,落地时间更准。5.3 与深度学习感知的前端融合端到端深度学习不好用于路径规划,但它很适合做障碍物检测和运动预测的前端。我在另一组实验里尝试过用一个小型卷积网络对机载相机图像做目标检测,输出动态障碍物的类别和位置,再送入本文这套启发式规划模块。效果不错,缺点是推理帧率受机载平台算力影响很大。如果算力允许,这种“前端深度学习感知 + 后端启发式规划”的混合架构值得做。关于算法调参的最后一点体会我在这儿尽我的经验说句掏心窝子的话:这套方案能跑通不难,但能跑出质量,九成取决于代价函数权重的门道。很多人复制代码跑出来的结果是能躲开障碍,但路径绕得离谱,或者贴着障碍物擦过去,都是因为权重没调对。安全项权重一定要压过路径长度项,宁可多绕 20% 的航程,也要保证安全距离至少超过一半的安全阈值,这样仿真和实机都能抗得住噪声。另一点是,每次改完参数千万别凭感觉说“看着变好了”,把最小距离、总长度、高度变化量三项指标打印出来,对比上一版数据,改参数才有方向。做实验记录这个习惯帮我把调参时间从每天瞎试变成了半天锁定方向。这篇东西算是我在这类项目上的实践实录,希望对正在做无人机路径规划和避障的人能有点实际的帮助。你要是自己也在 Matlab 里搭过类似的框架,欢迎拿自己的代价函数和参数跟我这个对比一下,互相查漏补缺。
搞无人机三维路径规划,最头疼的不是把起点到终点连一条线,而是要在一个有山包、楼宇、禁飞区,甚至还有移动障碍物的三维空间里,找出一条既安全、又相对好飞、还能实时响应的轨迹来。这篇文章要聊的这套基于启发式算法的动态避障方案,解决的就是这个事。先交代一下这套东西的实际构成:用启发式搜索为主干,在三维栅格地图上做路径搜索,再加上对移动威胁的动态检测和局部重规划,整个框架用 Matlab 实现。给飞行器做任务规划、搞集群编队路径预研,或者是在做比赛仿真验证的人,可以把这一整套直接拿去改。下面我按“问题拆解 → 核心原理 → 代码实现 → 踩坑实录”这个顺序把它讲透,不绕弯子。1. 项目需求与整体设计思路1.1 先搞明白“三维 + 动态”到底难在哪很多做二维路径规划的人,第一次转到三维时都会吃一个暗亏:以为只是把坐标从 (x, y) 变成 (x, y, z),算法逻辑抄一遍就行。实际上三维后算法的搜索空间从二维的网格面一下子变成了体素空间,节点数呈立方级增长。先说静态部分。城市峡谷、山地地形、风电场这些场景里面,无人机不能像平面导航那样“绕着走就行”,它需要同时考虑前后、左右、上下六个方向的通行性。比如一座山包,二维投影可能只是一块禁区,但三维里它可能是可爬升通过,也可能是必须绕行的陡坡,这个区别只能在三维空间里体现。再说动态部分。动态避障的难点不是探测到障碍物就行,而是要处理“运动预测 + 时间协同”。一个移动障碍物,比如另一架无人机或者一个高空吊装物,按什么轨迹移动、速度多少、什么时候和本机到达同一空间点,这些都是动态避障要回答的问题。如果把动态障碍物当作静态障碍物处理,规划出的路径会过于保守,甚至出现无解。所以整体设计上,我把问题拆成两层:全局规划层:用启发式算法在已知静态地图上找一条粗粒度路径,负责整体最优性。局部重规划层:在飞行过程中检测进入探测半径的动态威胁,局部调整路径,负责安全性。这两层在 Matlab 里分别对应两个模块,后面讲代码时会详细说。1.2 为什么选启发式算法(而不是图搜索或强化学习)这个问题经常被人问:三维路径规划明明可以用 RRT、A* 这类采样搜索法,也可以用深度强化学习端到端训练,为什么偏要选启发式算法?我的观点是,看场景看约束。RRT 和它的变体 RRT* 在三维空间里确实搜索效率不错,但生成的路径随机性太强,平滑性和重复性差,尤其在动态避障里,同样的起始条件跑两次,路径差异可能非常明显。A* 在栅格地图上确定性好,但代价是内存和搜索时间。深度强化学习很时髦,但需要大量训练样本、超参调试周期长,而且泛化能力在真实环境里很难保证。启发式算法的优势在于它的“场景适应性”。比如粒子群算法 PSO 可以通过种群迭代快速收敛到一条可行路径,它的编码方式天然适合处理连续坐标;遗传算法 GA 擅长全局寻优,不容易陷入局部最优;蚁群算法 ACO 的自组织特性在静态图搜索里表现很好。这套方案里,我主要用的是粒子群和遗传算法的混合策略,静态全局搜索交给遗传算法保障全局性,在局部动态避障时用粒子群的快速迭代能力把路径抢出来。关键点是:启发式算法不依赖整个地图的全局梯度信息,只要把代价函数定义好,它就能跑。这一点在三维体素空间里特别重要,因为真实的三维环境往往有大量不规则障碍物,数学上很难写成凸函数。1.3 方案的整体技术路线这套方案按下面五步走:地图预处理:读取地形高程数据或随机生成三维体素栅格,标记障碍物。航迹编码:把三维路径表示为一系列航迹点序列,使用 B 样条或线性插值转成平滑曲线。代价函数设计:综合路径长度、安全距离、飞行高度一致性、转弯约束。启发式寻优:遗传算法全局搜索 + 粒子群局部优化。动态避障模块:实时检测动态威胁,触发局部重规划。这里要特别强调第 3 步,因为很多论文的代价函数写得天花乱坠,实际代码里只用了路径长度一个指标。代价函数的设计直接影响启发式算法能不能收敛到一条真正好飞的路径,后面我会拿一条实际跑过的 MATLAB 代码来说。2. 核心原理与关键细节2.1 三维路径的表达与代价函数设计一条三维路径,不管抽象成什么形式,最后都要变成无人机执行时能用的一组航迹点。在这套代码里,我采用的是“节点序列 + 插值”的结构:先用 N 个离散点表示路径的粗骨架,再用三次 B 样条做平滑。假设起点为 P_start,终点为 P_end,中间插入 M 个路径点,整条路径就是 M+2 个点的序列。每个点有三个坐标分量 x、y、z,所以一个粒子或一条染色体在算法里就是一个长度为 3×(M+2) 的向量。这种编码方式很直观,也方便后续加约束。代价函数我建议至少包含四个部分:路径长度代价:所有相邻点之间的欧氏距离累加,这是最基本的要求。安全距离代价:判断路径点与障碍物体素的最小距离,小于安全阈值时就施加一个大的惩罚项。高度变化代价:无人机频繁爬升下降既费电又不好飞,对相邻点高度差的绝对值做累计。转弯角代价:相邻三个点构成的空间夹角如果小于最小转弯限制,说明飞行方向变化太急,也要惩罚。一个常用的代价函数形式是:J = w1 * L_path + w2 * f_safe + w3 * H_change + w4 * T_anglew1 到 w4 是权重系数。我的实测建议是:安全项比重最大,至少占总权重的 50%,因为算法搜索过程中会出现大量贴着障碍物飞行的“看似短实则危险”的路径,如果不把安全权重提上去,它根本不会主动找绕行的路。2.2 动态障碍物建模与避障触发条件动态避障在 Matlab 仿真里常用的做法是给移动障碍物一个运动模型。我在代码里规定每个动态威胁用一组参数描述:初始位置 center0速度向量 vel影响半径 R运动时间 t在 t 时刻,障碍物的球心位置是 center = center0 + vel * t。判断无人机路径点与动态障碍物是否冲突,就看该时刻无人机的坐标与障碍物球心距离是否小于 R。但这里有个很容易踩的坑:动态障碍物是移动的,你规划出来的路径点数量有限,如何判断整条路径是否会撞上移动障碍物?我采取的方法是时间戳采样法。为了让判断足够可靠,我在路径点之间做细分,每隔 dt 秒计算一次飞行器位置和障碍物位置的相对距离。飞行器位置通过路径点插值得到,障碍物位置按运动模型推算,然后逐点检测。避障触发条件很简单也很实用:只要检测到未来某个时间窗口内存在距离小于安全距离的时刻,就启动局部重规划。注意,重规划不是把整条路径全局重算,那样太慢。我是以当前点为起点,以全局路径上位于障碍物后方的某个航迹点为终点,用粒子群快速迭代一小段局部路径,把动态障碍物绕开后再回到全局路径上去。2.3 为什么需要平滑处理与末端约束先说一下平滑处理,这是最容易被人质疑“你怎么保证你的路径无人机真的能飞”的地方。搜索算法输出的原始路径是折线,折线转折处无人机如果直接执行,会产生极大加速度,姿态响应根本跟不上。所以平滑处理其实承担了两件事:第一是把路径变成曲线,让无人机能平稳跟踪;第二是保证起点和终点的航向约束,不让曲线在两端产生异常的偏转。我用了 B 样条曲线做平滑,具体是三次 B 样条。原因有两个:一是它对局部修改敏感度低,改动一个控制点不会让整条曲线扭曲,这正好配合局部重规划;二是它的数学计算简单,Matlab 里直接用 spapi 或自己写基函数都行。还有一点,末端约束不能省。如果你的任务是让无人机降落到指定点,路径末端必须保证位置精度和姿态精度。我在代码里把终点固定为一个硬约束,不管怎么迭代优化,终点坐标不参与变异和速度更新,这样就保证最后一定能落得到目标点。3. Matlab 代码实现与实操过程3.1 地图生成与参数初始化第一步先把地图生成工具写好。我这里用两种方式:一种是加载真实地形的 DEM 数据,另一种是随机生成三维栅格地图。随机地图的好处是方便做多次蒙特卡洛测试,验证算法稳定性。% 基本参数设置 map_range = [0 1000; 0 1000; 0 300]; % 地图范围 [xmin xmax; ymin ymax; zmin zmax] grid_res = 10; % 栅格分辨率 safe_dist = 15; % 安全距离阈值 % 随机生成圆柱形或立方体障碍物 obs_num = 20; obs_list = zeros(obs_num, 4); for i = 1:obs_num obs_list(i,1) = rand * (map_range(1,2)-map_range(1,1)) + map_range(1,1); obs_list(i,2) = rand * (map_range(2,2)-map_range(2,1)) + map_range(2,1); obs_list(i,3) = rand * (map_range(3,2)-map_range(3,1)) * 0.3 + 20; obs_list(i,4) = 20 + rand * 40; % 障碍物半径 end常用固定障碍物数据,也可以用栅格法:% 三维栅格图 grid_map = zeros(100,100,30); for i = 1:100 for j = 1:100 for k = 1:30 if is_obstacle(i,j,k) grid_map(i,j,k) = 1; end end end end在实际代码里我建议把地图做成结构体或者类,例如env.Map3D、env.obs_list、env.dyn_obs,这样后面的算法函数调用时不用反复传参。这个问题很小但很提升代码可读性。3.2 遗传算法实现全局路径搜索遗传算法的核心步骤是:种群初始化 → 适应度评估 → 选择 → 交叉 → 变异 → 迭代。种群初始化这块有一个注意点:不能全是纯随机点,否则初始种群适应度太差,收敛极慢。我习惯在初始化时加入一条直线插值路径作为种子个体,其它个体在直线附近做随机扰动。population_size = 50; chrom_length = 8; % 中间路径点个数 start_point = [100 100 80]; end_point = [900 900 120]; % 直线插值初始路径 ref_path = [linspace(start_point(1), end_point(1), chrom_length+2)', ... linspace(start_point(2), end_point(2), chrom_length+2)', ... linspace(start_point(3), end_point(3), chrom_length+2)'];适应度函数就是前面说的代价函数 J,不过要转换成“越小越好”的形式,所以 GA 里也用同样的函数,直接取倒数或者取负值作为适应度。交叉操作我采用的是均匀交叉和单点交叉结合。单点交叉逻辑简单:随机选一个交叉点,交换两条染色体交叉点后面的基因片段。但三维路径有一个特殊问题,就是直接交换片段后,路径点可能出现不连续的大跳跃,因此交叉后要做一次越界修正检查,把超出地图范围的坐标拉回来。变异操作要更精细。普通变异是随机改变某个节点的坐标,但这样扰动太大,很容易让路径变成“之”字形来回摆动。我更推荐用一个服从正态分布的随机位移,即以原节点为中心,在周围一个较小半径内扰动:mutate_range = 30; new_pos = old_pos + randn(1,3) * mutate_range;每一步迭代后都记录当前最优路径,并在满足最大迭代次数或适应度不再明显变化时停止。我在代码里设定的终止条件是:迭代 200 次并且连续 30 次最优适应度变化不超过 1%。这个条件是为了防止算法在迭代后期空转,浪费时间。3.3 粒子群算法的局部重规划实现粒子群算法在这里的角色是动态避障重规划。它和遗传算法相比,迭代速度快、参数少,适合处理时间敏感性高的局部规划任务。粒子群的核心公式是三件套:速度更新、位置更新、个体/全局最优更新。w = 0.7; % 惯性权重 c1 = 1.5; % 个体学习因子 c2 = 1.5; % 社会学习因子 max_iter = 60; particle_num = 30; dim = 3 * local_node_num; % 局部路径节点数 * 3 % 速度与位置初始化 v = zeros(particle_num, dim); x = repmat(ref_local_path, particle_num, 1) + randn(particle_num, dim) * 10; pbest = x; gbest = x(1,:);这里有个细节:dim = 3 * local_node_num。有些初学者会直接把局部路径的所有坐标都作为粒子维度,结果维度太高,粒子群搜索效率很差。经验值是局部路径只取 3~5 个中间点就够了,然后用三次样条插值把路径补密。这样既保留了搜索自由度,又大幅缩减了优化维度。动态避障重规划的触发逻辑如下:在每一段路径执行前,计算当前位置,更新动态障碍物位置。检查与动态障碍物的最小距离,如果小于安全距离,进入重规划。以当前点为起点,以全局路径上距离障碍物后方两到三个节点处的点为终点,利用粒子群生成平滑的绕飞路径。粒子群的代价函数要额外加入一个动态威胁项:对所有采样时刻,无人机位置与动态障碍物位置的距离都必须大于安全阈值。这项的惩罚权重要定得很高,我一般设成安全距离违例时施加 50000 的固定惩罚,防止粒子群为了长度最短而硬穿障碍。3.4 可视化与结果输出三维路径规划如果不做可视化,代码跑完了你根本没法判断路径合理不合理。我在项目里做了一个简单的三维场景展示函数,它会把地形表面、障碍物、规划路径、动态障碍物轨迹一次性画出来。figure; plot3(path(:,1), path(:,2), path(:,3), 'b-o', 'LineWidth', 2); hold on; % 障碍物绘制 for i = 1:size(obs_list,1) [X,Y,Z] = sphere(20); X = X * obs_list(i,4) + obs_list(i,1); Y = Y * obs_list(i,4) + obs_list(i,2); Z = Z * obs_list(i,4) + obs_list(i,3); surf(X,Y,Z, 'FaceColor', [0.6 0.6 0.6], 'EdgeColor', 'none', 'FaceAlpha', 0.7); end xlabel('X/m'); ylabel('Y/m'); zlabel('Z/m'); grid on; axis equal;可视化输出除了看路径形状外,更重要的是看距离曲线。我每次仿真结束会额外输出最小安全距离、路径总长度、平均高度变化、转弯次数这几个指标,用来判断算法调参后效果是否有提升。对比实验时这些指标是唯一可量化的标准,光靠肉眼看图不靠谱。4. 常见问题与调参经验4.1 两条最容易踩的坑:陷入局部最优与路径抖动遗传算法和粒子群算法是随机算法,所以陷入局部最优简直是家常便饭。我遇到过最典型的情况:地图上有一条绕远但平坦的走廊,算法死活不肯走,一直沿着直线方向贴着一片障碍物边缘生成锯齿状路径。想解决这个问题不能盲目增加迭代次数,那只会让时间白白浪费。我更推荐动态调整变异率和惯性权重。具体操作是:迭代前 60% 的阶段,变异率保持 0.2 的高位,增加种群多样性;到了后期,变异率降到 0.05,让算法专心收敛。粒子群的惯性权重 w 也应该从 0.9 线性降到 0.4。路径抖动和局部最优是两回事,它的典型表现是:整体大方向正确,但每段路径都有一两米的微小波浪。这个不是随机噪声,而是变异操作导致的局部坐标摆动。解决办法有两个,一个是把变异范围减小,另一个是在路径平滑时提高 B 样条的平滑权重。我实测后推荐首选第二个,因为平滑处理不会干扰搜索性能。4.2 动态避障模块的三大隐藏问题第一个隐藏问题是时间同步。重规划后的局部路径长度变了,到达时间也变了,这时候再用旧时间轴去预测动态障碍物位置,会全部错位。我处理的办法是:每次重规划后重新预估局部路径的飞行时间,然后把时间轴重置。第二个隐藏问题是障碍物速度过快。如果动态障碍物的速度远超本机的机动能力,任何重规划算法都无济于事。这类情况在仿真阶段就应该筛掉。我在初始化动态障碍物参数时,要求它的速度不超过无人机巡航速度的一半,否则直接报错提醒数据不合理。第三个隐藏问题比较阴间:动态障碍物群。多个移动障碍物同时出现在重规划窗口内时,按单一障碍物处理会顾此失彼。我的做法是重规划时一次性把视野范围内的所有动态障碍物都带入代价函数,统一评估。粒子群的优点在这里就体现出来了,它可以在一次迭代里同时避开多个运动威胁,而不需要像反应式避障那样一个一个处理。4.3 从仿真到实机部署的现实差距如果你只做仿真,那么以上内容基本够用了。但如果想往实机上移植,有三点必须提前想清楚。第一,Matlab 脚本算出的路径通常上传到飞控后要经过路径平滑模块的二次处理,因为飞控不会吃你给的点,它只会把点作为导航引导点,内部再做轨迹生成。你要确保你的路径给到飞控后不会因过度拟合而出现偏航。第二,动态障碍物的参数在真机里不可能像仿真那样精确。视觉或雷达估计出的位置速度都有噪声,所以仿真里设置安全距离的时候不能只按机体物理尺寸设,至少还要加上两倍的传感器误差作为冗余。第三,Matlab 代码的实时性不够。真机上的局部重规划通常用 C++ 或者直接在飞控里用更精简的算法实现。我做移植时,会把 Matlab 仿真当成算法验证和参数整定的工具,跑通之后换成 C++ 的矩阵运算库重写核心函数。5. 改进方向与扩展建议这部分算是我自己后续打算做的一些扩展,写出来供大家参考。5.1 引入时间窗与多机协同避障现在这套方案是针对单机的动态避障。如果要做多机协同,就要把时间维度更显式地引入路径表达。也就是每个路径点不仅要有坐标,还要有到达时间。多机避障的核心从“避障碍物”变成了“避时-空冲突”,代价函数里增加与其他无人机的最小相对距离项即可。启发式算法处理这个问题有明显优势,因为粒子之间天然就是多个解,稍加改动就能变成分布式求解。5.2 结合风场与能量约束真实气象环境里,无人机三维路径规划要面对风场影响。顺风、逆风、侧风会直接改变飞行能耗和速度。可以考虑在地图模型中叠加一个风速矢量场,把“逆风飞行”的代价加权到路径长度代价里。这样做出来的路径,虽然空中距离可能更长,但续航实际更久,落地时间更准。5.3 与深度学习感知的前端融合端到端深度学习不好用于路径规划,但它很适合做障碍物检测和运动预测的前端。我在另一组实验里尝试过用一个小型卷积网络对机载相机图像做目标检测,输出动态障碍物的类别和位置,再送入本文这套启发式规划模块。效果不错,缺点是推理帧率受机载平台算力影响很大。如果算力允许,这种“前端深度学习感知 + 后端启发式规划”的混合架构值得做。关于算法调参的最后一点体会我在这儿尽我的经验说句掏心窝子的话:这套方案能跑通不难,但能跑出质量,九成取决于代价函数权重的门道。很多人复制代码跑出来的结果是能躲开障碍,但路径绕得离谱,或者贴着障碍物擦过去,都是因为权重没调对。安全项权重一定要压过路径长度项,宁可多绕 20% 的航程,也要保证安全距离至少超过一半的安全阈值,这样仿真和实机都能抗得住噪声。另一点是,每次改完参数千万别凭感觉说“看着变好了”,把最小距离、总长度、高度变化量三项指标打印出来,对比上一版数据,改参数才有方向。做实验记录这个习惯帮我把调参时间从每天瞎试变成了半天锁定方向。这篇东西算是我在这类项目上的实践实录,希望对正在做无人机路径规划和避障的人能有点实际的帮助。你要是自己也在 Matlab 里搭过类似的框架,欢迎拿自己的代价函数和参数跟我这个对比一下,互相查漏补缺。