做路径规划这几年,被问得最多的一个问题不是"哪个算法最优",而是"你到底该用哪个"。蚁群算法、Dijkstra、遗传算法、人工势场法,每个名字听起来都很有道理,可一旦丢到真实地图上——比如一张带死胡同的二维栅格,或者一架无人机要绕开一片高层建筑——很多算法的表现跟论文里完全两回事。有的收敛快但路径扭曲,有的路径漂亮却算到天荒地老,有的在静态地图上无敌却完全没法应对动态障碍。这篇内容就从我实际调过的这四种算法说起,重点讲清楚蚁群算法及其改进版本的核心机制,再说Dijkstra如何给蚁群当探路员,遗传和人工势场各有什么取舍,最后聊二维路径规划升三维时,真正要改的到底是算法还是地图。适合正在做机器人导航、无人机航线、AGV调度,或者单纯想把路径规划整套思路盘明白的人。1. 四种算法的脾气与适用边界——动手前先看清底牌1.1 一句话看清四张底牌很多初学者拿到问题就急着跑蚁群,理由是"它听起来最聪明"。但路径规划里没有绝对最优的算法,只有匹配度的问题。先给四种算法的底层策略做个快速定位:蚁群算法:一群蚂蚁并行撒信息素,靠正反馈慢慢把"好走的路"浓度抬起来。本质是群体智能+正反馈,适合离线的、静态或半静态的全局路径搜索。Dijkstra算法:单源最短路径的经典解法,从起点一圈一圈往外扩,保证在权重非负的图上找到理论最短路径。贪心但靠谱,适合做基准解和预计算。遗传算法:把路径编码成染色体,靠选择、交叉、变异一代代"进化"出更优路径。适合解空间特别大、找不到解析解的场合,但收敛速度和稳定性都看运气。人工势场法:目标点产生引力、障碍物产生斥力,机器人顺着合力方向移动。属于局部方法,实时性好,但天生有局部极小值问题,容易"困在角落里"。这一句话版虽然有点粗暴,但用来做初步选型足够了。想求稳用Dijkstra,想求全局优化且能离线算用蚁群,想处理高维复杂解空间但有耐心调参用遗传,想实时避障用势场法。1.2 选型不是越高级越好,而是看地图和场景我踩过的最大一个坑,就是给一个静态仓储地图硬上蚁群算法。地图就 50×50 个栅格,路径根本没什么歧义,Dijkstra 几毫秒就出结果,蚁群却要迭代一百多代才稳定,纯属性能浪费。反过来也有翻车案例:有一次在某个 AGV 项目里想用 Dijkstra 做全局路径,地图中间有一片电磁干扰区域,通行代价会随时间变化,Dijkstra 算出来的"理论最短"路径在实际运行时不断撞上临时禁行区,最后只能返工。所以我的选型顺序一向是:先看地图动不动,再看是否需要全局最优,最后看算力预算。地图静态选 Dijkstra 或 A*;地图半静态但节点规模大、路径需要平滑优化,选蚁群及其改进;地图动态但局部范围小,用人工势场法做局部避障、让全局规划器在更高频率下重规划。遗传算法则通常是蚁群之外的另一个候选,适合解空间形态很差的时候。2. 蚁群算法的核心机制:信息素怎么一步步锁定最优路径2.1 状态转移规则:信息素和启发量怎么商量着走蚁群算法的核心公式并不多,最关键的蚂蚁状态转移规则可以写成这样:P_{ij}^k = ( τ_{ij}^α × η_{ij}^β ) / Σ_{l∈allowed} ( τ_{il}^α × η_{il}^β )其中 τ_{ij} 是边 (i,j) 上的信息素浓度,η_{ij} 是启发信息,α 和 β 分别控制两者在决策中的权重。在路径规划里 η_{ij} 最常见的取法是 1/d_{ij},也就是下一段路径长度越短越优先;但如果你做的是三维无人机规划,η 完全可以选择其他表达式,比如加入高度变化惩罚、能耗因子、甚至禁飞区距离。理解这个式子的核心,可以类比成点外卖。信息素像是"销量榜",销量越高的店铺越容易进你的候选列表;启发量像是"距离排序",离家越近的店铺权重越高。α 和 β 则是这两者对最终决策的影响比例——一个只看销量、一个只看得近,都会出问题。2.2 信息素更新与蒸发:为什么参数一变,收敛节奏就完全不同每轮迭代之后,所有边上的信息素会按照下面的规则更新:τ_{ij}(t+1) = (1 - ρ) × τ_{ij}(t) + Σ_k Δτ_{ij}^kρ 是蒸发系数,决定了旧信息素每轮衰减多少。Δτ_{ij}^k 是第 k 只蚂蚁在经过边 (i,j) 时留下的信息素增量,最经典的取法是 Δτ = Q / L_k,L_k 是这只蚂蚁找到的路径总长度,Q 是一个常数。路径越短,单只蚂蚁留下的信息素越浓。这里有一个非常反直觉的点:蒸发系数不是越小越好。我第一次调参时把 ρ 设成 0.05,想着信息素留得久一点,结果蚂蚁全被初始阶段碰巧走出来的几条局部短路径锁死,后面再也不探索新路。后来把 ρ 调到 0.3 左右,前期路径会被快速遗忘,蚂蚁被迫不断试新路线,反而更容易找到全局最优。蒸发在这里起的作用是"遗忘",遗忘太快会反复震荡,遗忘太慢会早早固化,哪个方向都有一堆坑。2.3 调参实测手感:从震荡到稳定的观察记录我经常用一组参数作为调试起点:蚂蚁数量设成栅格节点数的 0.6 到 1 倍左右,α 取 1,β 取 5,ρ 取 0.2,Q 取路径长度估值的量级。这个起点不一定最优,但基本不会让算法跑飞。接下来观察每一轮的最短路径长度曲线——如果曲线像心电图一样大起大落,说明 ρ 偏大或蚂蚁探索性过强;如果曲线过早平躺且路径质量差,说明 α 偏高或初始信息素给多了。举个例子,某个二维迷宫地图上我分别跑过 α=1、β=5 和 α=3、β=5 两组。前者大约在第 40 代收敛,路径长度为 32.6;后者第 18 代就平了,但收敛长度是 38.9。看起来后者快得多,实际是被局部最优骗了。这也解释了为什么很多人觉得蚁群"慢"——他们往往只给了蚁群很少的迭代次数,又给了一个高 α 的激进配置,算法自然一边早熟一边找不到好路。3. 经典蚁群的三个软肋,以及我实际采用的改进方案3.1 早熟停滞、收敛慢、路径抖动:问题出在哪经典蚁群算法在路径规划场景里有三个非常具体的毛病。第一个是早熟停滞。优质路径上的信息素一旦碾压其他区域,蚂蚁们就完全放弃探索,算法收敛到一个局部最优解后原地打转。第二个是收敛慢。尤其是在大规模地图上,如果没有好的先验引导,前期蚂蚁纯粹在乱撞,几十代过去都没产生稳定差异。第三个是路径抖动。蚁群输出的是由栅格中心点串起来的折线,虽然总长度可能很优,但转角突兀,直接给移动机器人执行,车体功率损耗大,甚至可能出现原地转弯。这三个问题不是孤立存在的。早熟导致局部最优,局部最优往往同时伴随路径不光滑;收敛慢导致前期迭代白跑,让参数调整变得很痛苦。所以改进蚁群从来不是单点修一处就能搞定,得从探索策略、信息素累积方式和局部优化三个维度同时下手。3.2 改进一:蒸发系数自适应,前期敢探索后期肯收敛我最先落地的一个改进是让蒸发系数 ρ 随迭代次数动态变化。核心思路很简单:前期需要一个较大的蒸发系数,让信息素的更新速度跟上探索节奏,不让任何一条边过早形成垄断;后期则收窄蒸发量,让已经找到的好路径快速确立优势。实现上我用了一个退火式的递推公式:ρ(t) = ρ_start × e^(-λ × t/T)T 是总迭代次数,λ 控制衰减速度。某次 120 代迭代里,我从 ρ_start=0.9 一路衰减到 0.08,前期路径还在到处试,等到 60 代往后,信息素几乎不再被大量蒸发,当前优势路径越叠越深。实测对比下,这种自适应方式比固定 ρ=0.2 的平均收敛时间缩短了大概 30%,而且最终解长度的方差明显变小。唯一的代价是多一个 λ 要调,但 λ 的敏感性远低于 ρ 本身,用 2 到 4 之间的值基本不会出大问题。3.3 改进二:精英蚂蚁与信息素限幅(MMAS思路)第二个改进是针对早熟停滞的,核心是两条:一条是精英蚂蚁策略,另一条是信息素限幅。精英蚂蚁策略的意思是,每一代迭代结束后,不光是普通蚂蚁自己积累信息素,还要额外找出当前全局最优路径,往这条路径上再打一笔"奖励金",强化优势信息。这样做能有效加速收敛,但也带来一个副作用——全局最优过强,容易把探索空间锁死。于是需要信息素限幅来兜底。信息素限幅的做法来自最大最小蚂蚁系统(MMAS)的思路:给所有边上的信息素设置上限 τ_max 和下限 τ_min。低于下限的边强制抬到下限,保证任何一条路都有一定概率被选中;高于上限的边强制压到上限,防止某条路径信息素浓度爆炸。这两个边界就像给竞争加了"最低工资"和"最高薪资",既保证弱势选项不会完全消失,又不让赢家通吃一切。在实际测试里,这一组合拳的效果是:同一张 80×80 栅格地图上,经典蚁群在第 25 代左右早熟,路径长度卡在 61;加上精英蚂蚁和限幅后,算法虽然在第 32 代才收敛,但长度降到了 55 以下,而且连续跑十次的结果几乎一致,稳定性提升非常明显。3.4 改进三:2-opt 局部优化,让最终路径更平滑路径抖动这个问题靠前两个改进解决不了,因为信息素机制管的是"哪些边更受青睐",管不了"转弯是否合理"。我的做法是在每轮迭代结束后,对当前较优的几条路径做一次 2-opt 局部优化。2-opt 原本是旅行商问题里很经典的操作:随机选择路径上的两段,把这两段之间的子路径翻转连接,比较翻转前后总长度,如果变短就接受。放到栅格路径上,就是尝试把路径中的"绕行折线"变成更直接的连接。关键细节是翻转后要重新检查路径是否穿过障碍物,否则很容易把路径改坏了。把 2-opt 接在蚁群内部后,一个直接的变化是最终输出的路径从"一串锯齿"变成了"一串带着斜向直连的平滑走廊"。再配合后续的 B 样条平滑,控制起来就舒服多了。顺带一提,我试过每轮对所有蚂蚁都做 2-opt,效果虽有提升但计算量翻了好几倍;最后只对当前代最优的前三只蚂蚁做,性价比最高。4. Dijkstra 不是用来单干的,给蚁群当"探路员"4.1 Dijkstra 为什么很适合做基准计算很多人觉得 Dijkstra 和蚁群是竞争关系,二选一就行了。但在这个项目里,我把它俩做成了上下级关系——Dijkstra 给蚁群提供先验信息,好处非常明显。Dijkstra 的优势是确定性:只要图上的边权不变,它给出的就是理论最短路径,没有随机性,不需要反复跑。劣势是它只认边权,不会考虑路径平滑性、安全性这类工程因素。所以用 Dijkstra 输出的最短路径当作"对照基准"再合适不过了。我往往会先跑一遍 Dijkstra,拿到一个理论下界,然后拿蚁群的结果跟它对比:如果蚁群找出来的路径只比基准长一点点,说明改进已经做得很到位;如果差距很大,就可以反推是地图建模出了问题,比如边权设置不合理。4.2 混合做法:初始信息素注入与危险区域预标注Dijkstra 给蚁群当探路员,最直接的一种混合方式是把 Dijkstra 求出的路径作为初始信息素的"种子"。具体操作是:先用 Dijkstra 算出从起点到终点的理论最短路径,然后在这条路径的所有边上把初始信息素上调一个幅度,比如从 τ₀ 提到 1.5τ₀~2τ₀,其他区域保持低位。这样蚂蚁在最初几代就会沿着一条大体靠谱的走廊探索,而不是满地图乱窜。要注意幅度不能太大。如果初始浓度差过于悬殊,就相当于把蚂蚁全部锁死在 Dijkstra 路径上,蚁群的探索优势就废了,跑出来的结果跟直接用 Dijkstra 没区别,还赔上几倍的迭代时间。另一种混合是预标注危险区域。把地图上的高危栅格(比如靠近障碍物边缘、历史碰撞率高的区域)在启发量 η 上做惩罚,Dijkstra 不会管这些东西,但蚁群可以通过修改启发量天然地把猛烈避障信息融合进去。等于说 Dijkstra 负责"最短"的粗略方向,蚁群负责"更安全、更可执行"的精细优化。4.3 面对动态障碍时怎么分工有人会问:Dijkstra 不是静态算法吗?加了动态地图它还有什么用?我的经验是把整个路径规划拆成两层。第一层是全局规划,频率低,每隔几秒才重新执行一次,用改进后的蚁群在这个尺度上算出大致的路线骨架。第二层是局部避障,频率高,用人工势场法或者速度障碍法处理短时间内的动态障碍物。Dijkstra 的角色则夹在两层之间——当全局地图发生较大变化、蚁群需要重新规划时,先用 Dijkstra 快速刷新一条参考路径,再用这条路径作为新蚁群迭代的初始信息素种子。这么做的好处是,每次全局重规划都不需要从零开始"盲搜",蚁群可以在 Dijkstra 提供的参考走廊内快速收敛。在某次 100×100 地图的模拟里,右侧突然多了一堵临时墙,直接重启蚁群需要 200 多代才稳定,而先跑一次 Dijkstra 再作为初始种子注入,大约 60 代就稳定了,肉眼可见的划算。5. 从二维到三维:地图变了,算法必须跟着改5.1 栅格变体素,状态空间增长了多少二维路径规划的地图通常是 M×N 的栅格,每个格子标注可走或不可走。到了三维空间,最自然的扩展方式就是变成 M×N×K 的体素地图。听起来只是多了一个维度,状态空间却在数量级上暴涨。一个 100×100 的平面栅格有 1 万个状态节点,而 100×100×50 的体素地图直接就有 50 万个节点。这意味着蚂蚁要遍历的边数量也急剧膨胀,如果直接照搬二维代码,收敛速度会慢到让人怀疑人生。所以我在做三维规划时第一件事不是改算法,而是压缩状态空间。常见做法是改变体素分辨率,或者把地图分成多层高度表,只在每个高度层内做二维规划,再通过高度层切换建立三维路径。虽然精度略有损失,但工程上往往比直接怼一个巨大三维体素图靠谱得多。5.2 邻域扩展和启发量的三维化二维蚂蚁的移动邻域一般最多 8 个方向,到三维就变成 26 个方向。但这里有个细节很容易被忽略:三维斜向移动会让路径穿过障碍体素的角点,看起来可行,实际飞行/行走时会擦边甚至撞上。所以扩展邻域时,必须对每个斜向移动做碰撞校验,不能只检查目标体素是否占用。启发量设计也比二维复杂得多。二维里 η=1/d 已经够用,三维里如果你只按欧氏距离算,算法会对"忽上忽下"的路线毫无感觉,最后生成一条剧烈起伏、完全不合理的航线。我实际给无人机项目做规划时,把启发量改成了这样:η = 1 / ( w1×长度 + w2×高度变化量 + w3×能耗估计 + w4×转弯惩罚 )w 是权重系数,这完全取决于你优化的是时间、能耗还是安全性。比如某个巡检任务里着重省电,我会把能耗权重调高,让蚂蚁倾向于平缓巡航,而不是频繁爬升和俯冲。改进蚁群的框架在这里体现出很强的灵活性,因为你可以在启发项里面塞入任意领域约束,而不需要改动主循环逻辑。5.3 三维路径的平滑、转弯与高度约束三维路径规划的另一大坑是"算得出来,飞不动"。很多规划算法输出的是一串离散点,直接连起来就会得到一堆狼牙状转折。二维地图转弯还好,到了三维空间,无人机的最小转弯半径、最大爬升角、禁飞高度限制全都变成硬约束。我在实际项目中会把蚁群输出的三维路径点先做几何简化,去掉冗余转折点,然后再用 B 样条曲线做平滑。平滑之后还要检查一次曲率半径是否满足无人机的飞行能力,不满足就在过弯处插入更密的控制点重新拟合。这个后处理流程看起来是"算法之外"的体力活,但真正决定了整个规划系统能不能落地。如果只追求算法跑完就交付,那大概率会在实测时被打回来返工。6. 遗传算法和人工势场法:两种常被拿来做对比的思路6.1 遗传算法:路径进化的思路与早熟问题遗传算法在路径规划里是蚁群最常见的"竞争对手"。它的做法是把一条完整路径编码成染色体,比如固定起点和终点,中间经过一串路径点;然后对一批路径组成的种群施加选择、交叉、变异操作,一代代选出适应度最高的个体。适应度函数可以做得非常灵活:路径总长度、平滑度、与障碍物的最小距离、总能耗都可以加权进去。这种自由度是它比经典蚁群更有吸引力的地方。但使用遗传算法时我很想吐槽的一点是,它的成功极度依赖编码方式和参数:交叉概率、变异概率、种群规模四个参数组合起来,效果天差地别。跑一轮实验可能一等就是几分钟,还不一定收敛;有的地图上明明已经挺优的路径,因为变异率太高,下一代又给打散了。碰到这种情况,我通常用精英保留策略来兜底:每一代把适应度最高的前 5% 个体原封不动地复制到下一代,保证好不容易找到的好解不会被交叉和变异破坏。即使如此,遗传算法在连续地图上的收敛速度还是普遍比改进后的蚁群慢,所以我更倾向于把它当作解空间的"探索者"来用,而不是最终的精确优化器。6.2 人工势场法:实时避障的便利与局部极小值的坑人工势场法跟前几种算法完全不是一个路子。它不需要栅格地图上的全局搜索,而是直接在当前点算出一个合力方向,走一小步,再算一次。引力指向目标,斥力推离障碍物,合力的方向就是机器人的前进方向。实现起来非常快,特别适合动态环境里的局部避障。但是它的痛点也很经典:局部极小值。当机器人走到某个位置,引力与斥力正好大小相等、方向相反,它就会卡死在原地,来回震荡出不去。最典型的场景是凹形障碍物,比如一个 U 形墙面,机器人走进去就被困住,因为所有方向的势场都把它往 U 形底部推。我早期做的室内差速机器人 demo 就遇到过这个问题。当时给机器人加了一个"随机扰动"逻辑——检测到连续几步位置变化小于阈值时,就往垂直于当前合力的方向强行加一个绕行动作。这一招简单粗暴但非常有效。后来我把人工势场法改成蚁群规划路径中间点之间的局部跟踪器,效果更好:蚁群负责在全局层面穿过走廊和门洞,势场法负责这些线段之间动态避开突然出现的人或推车。两者各干各擅长的活,比任何单一算法都稳。6.3 全局规划与局部规划的配合方式把几种算法放到同一套系统里之后,我最大的感受是:不要把它们当成同一个问题的竞品,而要当成上下游流程。在复杂场景里,我惯用的组合方式是:用改进的蚁群做全局路径骨架,每隔一段时间重规划一次;Dijkstra 在重规划前提供快速参考路径,把蚁群的初始信息素"喂饱";人工势场法放在执行层,按照局部合力的方式沿全局路径点行走,顺带避让低速动态障碍;遗传算法则留在离线分析阶段,用来做多目标折中方案的预研究,比如同时考虑最短路径和最大安全距离的帕累托前沿。这个分层结构听起来复杂,但真正的调试工作量比想象中少。因为每一层只需要把自己的问题解决干净:全局层只负责"大方向别错",执行层只负责"这一步别撞"。如果你把所有任务塞给一个算法,任何一环出问题都会把锅甩到参数头上,调试起来极其痛苦。分层之后,出了问题可以直接定位是探索不足还是避障贪心,修起来思路非常清晰。跑完这一整套实验,在好几个模拟项目里反复横跳之后,我个人的体会是:路径规划的技术难点早就不是"调用某个算法库跑出路径",而是"选择合适的地图抽象、设计合理的约束表达、搭出能容忍参数波动的主流程"。蚁群算法及其改进给了我很大的设计空间,Dijkstra 的基准能力让可信度有了参照,遗传和势场又补上了复杂优化和实时避障两块拼图。如果你正准备从二维往三维扩展,建议先把二维地图上的调试平台跑稳,再一点一点加维度。三维的问题,很多时候不是三维特有的,而是你把二维里偷懒没做好的部分,放大了一百倍。
做路径规划这几年,被问得最多的一个问题不是"哪个算法最优",而是"你到底该用哪个"。蚁群算法、Dijkstra、遗传算法、人工势场法,每个名字听起来都很有道理,可一旦丢到真实地图上——比如一张带死胡同的二维栅格,或者一架无人机要绕开一片高层建筑——很多算法的表现跟论文里完全两回事。有的收敛快但路径扭曲,有的路径漂亮却算到天荒地老,有的在静态地图上无敌却完全没法应对动态障碍。这篇内容就从我实际调过的这四种算法说起,重点讲清楚蚁群算法及其改进版本的核心机制,再说Dijkstra如何给蚁群当探路员,遗传和人工势场各有什么取舍,最后聊二维路径规划升三维时,真正要改的到底是算法还是地图。适合正在做机器人导航、无人机航线、AGV调度,或者单纯想把路径规划整套思路盘明白的人。1. 四种算法的脾气与适用边界——动手前先看清底牌1.1 一句话看清四张底牌很多初学者拿到问题就急着跑蚁群,理由是"它听起来最聪明"。但路径规划里没有绝对最优的算法,只有匹配度的问题。先给四种算法的底层策略做个快速定位:蚁群算法:一群蚂蚁并行撒信息素,靠正反馈慢慢把"好走的路"浓度抬起来。本质是群体智能+正反馈,适合离线的、静态或半静态的全局路径搜索。Dijkstra算法:单源最短路径的经典解法,从起点一圈一圈往外扩,保证在权重非负的图上找到理论最短路径。贪心但靠谱,适合做基准解和预计算。遗传算法:把路径编码成染色体,靠选择、交叉、变异一代代"进化"出更优路径。适合解空间特别大、找不到解析解的场合,但收敛速度和稳定性都看运气。人工势场法:目标点产生引力、障碍物产生斥力,机器人顺着合力方向移动。属于局部方法,实时性好,但天生有局部极小值问题,容易"困在角落里"。这一句话版虽然有点粗暴,但用来做初步选型足够了。想求稳用Dijkstra,想求全局优化且能离线算用蚁群,想处理高维复杂解空间但有耐心调参用遗传,想实时避障用势场法。1.2 选型不是越高级越好,而是看地图和场景我踩过的最大一个坑,就是给一个静态仓储地图硬上蚁群算法。地图就 50×50 个栅格,路径根本没什么歧义,Dijkstra 几毫秒就出结果,蚁群却要迭代一百多代才稳定,纯属性能浪费。反过来也有翻车案例:有一次在某个 AGV 项目里想用 Dijkstra 做全局路径,地图中间有一片电磁干扰区域,通行代价会随时间变化,Dijkstra 算出来的"理论最短"路径在实际运行时不断撞上临时禁行区,最后只能返工。所以我的选型顺序一向是:先看地图动不动,再看是否需要全局最优,最后看算力预算。地图静态选 Dijkstra 或 A*;地图半静态但节点规模大、路径需要平滑优化,选蚁群及其改进;地图动态但局部范围小,用人工势场法做局部避障、让全局规划器在更高频率下重规划。遗传算法则通常是蚁群之外的另一个候选,适合解空间形态很差的时候。2. 蚁群算法的核心机制:信息素怎么一步步锁定最优路径2.1 状态转移规则:信息素和启发量怎么商量着走蚁群算法的核心公式并不多,最关键的蚂蚁状态转移规则可以写成这样:P_{ij}^k = ( τ_{ij}^α × η_{ij}^β ) / Σ_{l∈allowed} ( τ_{il}^α × η_{il}^β )其中 τ_{ij} 是边 (i,j) 上的信息素浓度,η_{ij} 是启发信息,α 和 β 分别控制两者在决策中的权重。在路径规划里 η_{ij} 最常见的取法是 1/d_{ij},也就是下一段路径长度越短越优先;但如果你做的是三维无人机规划,η 完全可以选择其他表达式,比如加入高度变化惩罚、能耗因子、甚至禁飞区距离。理解这个式子的核心,可以类比成点外卖。信息素像是"销量榜",销量越高的店铺越容易进你的候选列表;启发量像是"距离排序",离家越近的店铺权重越高。α 和 β 则是这两者对最终决策的影响比例——一个只看销量、一个只看得近,都会出问题。2.2 信息素更新与蒸发:为什么参数一变,收敛节奏就完全不同每轮迭代之后,所有边上的信息素会按照下面的规则更新:τ_{ij}(t+1) = (1 - ρ) × τ_{ij}(t) + Σ_k Δτ_{ij}^kρ 是蒸发系数,决定了旧信息素每轮衰减多少。Δτ_{ij}^k 是第 k 只蚂蚁在经过边 (i,j) 时留下的信息素增量,最经典的取法是 Δτ = Q / L_k,L_k 是这只蚂蚁找到的路径总长度,Q 是一个常数。路径越短,单只蚂蚁留下的信息素越浓。这里有一个非常反直觉的点:蒸发系数不是越小越好。我第一次调参时把 ρ 设成 0.05,想着信息素留得久一点,结果蚂蚁全被初始阶段碰巧走出来的几条局部短路径锁死,后面再也不探索新路。后来把 ρ 调到 0.3 左右,前期路径会被快速遗忘,蚂蚁被迫不断试新路线,反而更容易找到全局最优。蒸发在这里起的作用是"遗忘",遗忘太快会反复震荡,遗忘太慢会早早固化,哪个方向都有一堆坑。2.3 调参实测手感:从震荡到稳定的观察记录我经常用一组参数作为调试起点:蚂蚁数量设成栅格节点数的 0.6 到 1 倍左右,α 取 1,β 取 5,ρ 取 0.2,Q 取路径长度估值的量级。这个起点不一定最优,但基本不会让算法跑飞。接下来观察每一轮的最短路径长度曲线——如果曲线像心电图一样大起大落,说明 ρ 偏大或蚂蚁探索性过强;如果曲线过早平躺且路径质量差,说明 α 偏高或初始信息素给多了。举个例子,某个二维迷宫地图上我分别跑过 α=1、β=5 和 α=3、β=5 两组。前者大约在第 40 代收敛,路径长度为 32.6;后者第 18 代就平了,但收敛长度是 38.9。看起来后者快得多,实际是被局部最优骗了。这也解释了为什么很多人觉得蚁群"慢"——他们往往只给了蚁群很少的迭代次数,又给了一个高 α 的激进配置,算法自然一边早熟一边找不到好路。3. 经典蚁群的三个软肋,以及我实际采用的改进方案3.1 早熟停滞、收敛慢、路径抖动:问题出在哪经典蚁群算法在路径规划场景里有三个非常具体的毛病。第一个是早熟停滞。优质路径上的信息素一旦碾压其他区域,蚂蚁们就完全放弃探索,算法收敛到一个局部最优解后原地打转。第二个是收敛慢。尤其是在大规模地图上,如果没有好的先验引导,前期蚂蚁纯粹在乱撞,几十代过去都没产生稳定差异。第三个是路径抖动。蚁群输出的是由栅格中心点串起来的折线,虽然总长度可能很优,但转角突兀,直接给移动机器人执行,车体功率损耗大,甚至可能出现原地转弯。这三个问题不是孤立存在的。早熟导致局部最优,局部最优往往同时伴随路径不光滑;收敛慢导致前期迭代白跑,让参数调整变得很痛苦。所以改进蚁群从来不是单点修一处就能搞定,得从探索策略、信息素累积方式和局部优化三个维度同时下手。3.2 改进一:蒸发系数自适应,前期敢探索后期肯收敛我最先落地的一个改进是让蒸发系数 ρ 随迭代次数动态变化。核心思路很简单:前期需要一个较大的蒸发系数,让信息素的更新速度跟上探索节奏,不让任何一条边过早形成垄断;后期则收窄蒸发量,让已经找到的好路径快速确立优势。实现上我用了一个退火式的递推公式:ρ(t) = ρ_start × e^(-λ × t/T)T 是总迭代次数,λ 控制衰减速度。某次 120 代迭代里,我从 ρ_start=0.9 一路衰减到 0.08,前期路径还在到处试,等到 60 代往后,信息素几乎不再被大量蒸发,当前优势路径越叠越深。实测对比下,这种自适应方式比固定 ρ=0.2 的平均收敛时间缩短了大概 30%,而且最终解长度的方差明显变小。唯一的代价是多一个 λ 要调,但 λ 的敏感性远低于 ρ 本身,用 2 到 4 之间的值基本不会出大问题。3.3 改进二:精英蚂蚁与信息素限幅(MMAS思路)第二个改进是针对早熟停滞的,核心是两条:一条是精英蚂蚁策略,另一条是信息素限幅。精英蚂蚁策略的意思是,每一代迭代结束后,不光是普通蚂蚁自己积累信息素,还要额外找出当前全局最优路径,往这条路径上再打一笔"奖励金",强化优势信息。这样做能有效加速收敛,但也带来一个副作用——全局最优过强,容易把探索空间锁死。于是需要信息素限幅来兜底。信息素限幅的做法来自最大最小蚂蚁系统(MMAS)的思路:给所有边上的信息素设置上限 τ_max 和下限 τ_min。低于下限的边强制抬到下限,保证任何一条路都有一定概率被选中;高于上限的边强制压到上限,防止某条路径信息素浓度爆炸。这两个边界就像给竞争加了"最低工资"和"最高薪资",既保证弱势选项不会完全消失,又不让赢家通吃一切。在实际测试里,这一组合拳的效果是:同一张 80×80 栅格地图上,经典蚁群在第 25 代左右早熟,路径长度卡在 61;加上精英蚂蚁和限幅后,算法虽然在第 32 代才收敛,但长度降到了 55 以下,而且连续跑十次的结果几乎一致,稳定性提升非常明显。3.4 改进三:2-opt 局部优化,让最终路径更平滑路径抖动这个问题靠前两个改进解决不了,因为信息素机制管的是"哪些边更受青睐",管不了"转弯是否合理"。我的做法是在每轮迭代结束后,对当前较优的几条路径做一次 2-opt 局部优化。2-opt 原本是旅行商问题里很经典的操作:随机选择路径上的两段,把这两段之间的子路径翻转连接,比较翻转前后总长度,如果变短就接受。放到栅格路径上,就是尝试把路径中的"绕行折线"变成更直接的连接。关键细节是翻转后要重新检查路径是否穿过障碍物,否则很容易把路径改坏了。把 2-opt 接在蚁群内部后,一个直接的变化是最终输出的路径从"一串锯齿"变成了"一串带着斜向直连的平滑走廊"。再配合后续的 B 样条平滑,控制起来就舒服多了。顺带一提,我试过每轮对所有蚂蚁都做 2-opt,效果虽有提升但计算量翻了好几倍;最后只对当前代最优的前三只蚂蚁做,性价比最高。4. Dijkstra 不是用来单干的,给蚁群当"探路员"4.1 Dijkstra 为什么很适合做基准计算很多人觉得 Dijkstra 和蚁群是竞争关系,二选一就行了。但在这个项目里,我把它俩做成了上下级关系——Dijkstra 给蚁群提供先验信息,好处非常明显。Dijkstra 的优势是确定性:只要图上的边权不变,它给出的就是理论最短路径,没有随机性,不需要反复跑。劣势是它只认边权,不会考虑路径平滑性、安全性这类工程因素。所以用 Dijkstra 输出的最短路径当作"对照基准"再合适不过了。我往往会先跑一遍 Dijkstra,拿到一个理论下界,然后拿蚁群的结果跟它对比:如果蚁群找出来的路径只比基准长一点点,说明改进已经做得很到位;如果差距很大,就可以反推是地图建模出了问题,比如边权设置不合理。4.2 混合做法:初始信息素注入与危险区域预标注Dijkstra 给蚁群当探路员,最直接的一种混合方式是把 Dijkstra 求出的路径作为初始信息素的"种子"。具体操作是:先用 Dijkstra 算出从起点到终点的理论最短路径,然后在这条路径的所有边上把初始信息素上调一个幅度,比如从 τ₀ 提到 1.5τ₀~2τ₀,其他区域保持低位。这样蚂蚁在最初几代就会沿着一条大体靠谱的走廊探索,而不是满地图乱窜。要注意幅度不能太大。如果初始浓度差过于悬殊,就相当于把蚂蚁全部锁死在 Dijkstra 路径上,蚁群的探索优势就废了,跑出来的结果跟直接用 Dijkstra 没区别,还赔上几倍的迭代时间。另一种混合是预标注危险区域。把地图上的高危栅格(比如靠近障碍物边缘、历史碰撞率高的区域)在启发量 η 上做惩罚,Dijkstra 不会管这些东西,但蚁群可以通过修改启发量天然地把猛烈避障信息融合进去。等于说 Dijkstra 负责"最短"的粗略方向,蚁群负责"更安全、更可执行"的精细优化。4.3 面对动态障碍时怎么分工有人会问:Dijkstra 不是静态算法吗?加了动态地图它还有什么用?我的经验是把整个路径规划拆成两层。第一层是全局规划,频率低,每隔几秒才重新执行一次,用改进后的蚁群在这个尺度上算出大致的路线骨架。第二层是局部避障,频率高,用人工势场法或者速度障碍法处理短时间内的动态障碍物。Dijkstra 的角色则夹在两层之间——当全局地图发生较大变化、蚁群需要重新规划时,先用 Dijkstra 快速刷新一条参考路径,再用这条路径作为新蚁群迭代的初始信息素种子。这么做的好处是,每次全局重规划都不需要从零开始"盲搜",蚁群可以在 Dijkstra 提供的参考走廊内快速收敛。在某次 100×100 地图的模拟里,右侧突然多了一堵临时墙,直接重启蚁群需要 200 多代才稳定,而先跑一次 Dijkstra 再作为初始种子注入,大约 60 代就稳定了,肉眼可见的划算。5. 从二维到三维:地图变了,算法必须跟着改5.1 栅格变体素,状态空间增长了多少二维路径规划的地图通常是 M×N 的栅格,每个格子标注可走或不可走。到了三维空间,最自然的扩展方式就是变成 M×N×K 的体素地图。听起来只是多了一个维度,状态空间却在数量级上暴涨。一个 100×100 的平面栅格有 1 万个状态节点,而 100×100×50 的体素地图直接就有 50 万个节点。这意味着蚂蚁要遍历的边数量也急剧膨胀,如果直接照搬二维代码,收敛速度会慢到让人怀疑人生。所以我在做三维规划时第一件事不是改算法,而是压缩状态空间。常见做法是改变体素分辨率,或者把地图分成多层高度表,只在每个高度层内做二维规划,再通过高度层切换建立三维路径。虽然精度略有损失,但工程上往往比直接怼一个巨大三维体素图靠谱得多。5.2 邻域扩展和启发量的三维化二维蚂蚁的移动邻域一般最多 8 个方向,到三维就变成 26 个方向。但这里有个细节很容易被忽略:三维斜向移动会让路径穿过障碍体素的角点,看起来可行,实际飞行/行走时会擦边甚至撞上。所以扩展邻域时,必须对每个斜向移动做碰撞校验,不能只检查目标体素是否占用。启发量设计也比二维复杂得多。二维里 η=1/d 已经够用,三维里如果你只按欧氏距离算,算法会对"忽上忽下"的路线毫无感觉,最后生成一条剧烈起伏、完全不合理的航线。我实际给无人机项目做规划时,把启发量改成了这样:η = 1 / ( w1×长度 + w2×高度变化量 + w3×能耗估计 + w4×转弯惩罚 )w 是权重系数,这完全取决于你优化的是时间、能耗还是安全性。比如某个巡检任务里着重省电,我会把能耗权重调高,让蚂蚁倾向于平缓巡航,而不是频繁爬升和俯冲。改进蚁群的框架在这里体现出很强的灵活性,因为你可以在启发项里面塞入任意领域约束,而不需要改动主循环逻辑。5.3 三维路径的平滑、转弯与高度约束三维路径规划的另一大坑是"算得出来,飞不动"。很多规划算法输出的是一串离散点,直接连起来就会得到一堆狼牙状转折。二维地图转弯还好,到了三维空间,无人机的最小转弯半径、最大爬升角、禁飞高度限制全都变成硬约束。我在实际项目中会把蚁群输出的三维路径点先做几何简化,去掉冗余转折点,然后再用 B 样条曲线做平滑。平滑之后还要检查一次曲率半径是否满足无人机的飞行能力,不满足就在过弯处插入更密的控制点重新拟合。这个后处理流程看起来是"算法之外"的体力活,但真正决定了整个规划系统能不能落地。如果只追求算法跑完就交付,那大概率会在实测时被打回来返工。6. 遗传算法和人工势场法:两种常被拿来做对比的思路6.1 遗传算法:路径进化的思路与早熟问题遗传算法在路径规划里是蚁群最常见的"竞争对手"。它的做法是把一条完整路径编码成染色体,比如固定起点和终点,中间经过一串路径点;然后对一批路径组成的种群施加选择、交叉、变异操作,一代代选出适应度最高的个体。适应度函数可以做得非常灵活:路径总长度、平滑度、与障碍物的最小距离、总能耗都可以加权进去。这种自由度是它比经典蚁群更有吸引力的地方。但使用遗传算法时我很想吐槽的一点是,它的成功极度依赖编码方式和参数:交叉概率、变异概率、种群规模四个参数组合起来,效果天差地别。跑一轮实验可能一等就是几分钟,还不一定收敛;有的地图上明明已经挺优的路径,因为变异率太高,下一代又给打散了。碰到这种情况,我通常用精英保留策略来兜底:每一代把适应度最高的前 5% 个体原封不动地复制到下一代,保证好不容易找到的好解不会被交叉和变异破坏。即使如此,遗传算法在连续地图上的收敛速度还是普遍比改进后的蚁群慢,所以我更倾向于把它当作解空间的"探索者"来用,而不是最终的精确优化器。6.2 人工势场法:实时避障的便利与局部极小值的坑人工势场法跟前几种算法完全不是一个路子。它不需要栅格地图上的全局搜索,而是直接在当前点算出一个合力方向,走一小步,再算一次。引力指向目标,斥力推离障碍物,合力的方向就是机器人的前进方向。实现起来非常快,特别适合动态环境里的局部避障。但是它的痛点也很经典:局部极小值。当机器人走到某个位置,引力与斥力正好大小相等、方向相反,它就会卡死在原地,来回震荡出不去。最典型的场景是凹形障碍物,比如一个 U 形墙面,机器人走进去就被困住,因为所有方向的势场都把它往 U 形底部推。我早期做的室内差速机器人 demo 就遇到过这个问题。当时给机器人加了一个"随机扰动"逻辑——检测到连续几步位置变化小于阈值时,就往垂直于当前合力的方向强行加一个绕行动作。这一招简单粗暴但非常有效。后来我把人工势场法改成蚁群规划路径中间点之间的局部跟踪器,效果更好:蚁群负责在全局层面穿过走廊和门洞,势场法负责这些线段之间动态避开突然出现的人或推车。两者各干各擅长的活,比任何单一算法都稳。6.3 全局规划与局部规划的配合方式把几种算法放到同一套系统里之后,我最大的感受是:不要把它们当成同一个问题的竞品,而要当成上下游流程。在复杂场景里,我惯用的组合方式是:用改进的蚁群做全局路径骨架,每隔一段时间重规划一次;Dijkstra 在重规划前提供快速参考路径,把蚁群的初始信息素"喂饱";人工势场法放在执行层,按照局部合力的方式沿全局路径点行走,顺带避让低速动态障碍;遗传算法则留在离线分析阶段,用来做多目标折中方案的预研究,比如同时考虑最短路径和最大安全距离的帕累托前沿。这个分层结构听起来复杂,但真正的调试工作量比想象中少。因为每一层只需要把自己的问题解决干净:全局层只负责"大方向别错",执行层只负责"这一步别撞"。如果你把所有任务塞给一个算法,任何一环出问题都会把锅甩到参数头上,调试起来极其痛苦。分层之后,出了问题可以直接定位是探索不足还是避障贪心,修起来思路非常清晰。跑完这一整套实验,在好几个模拟项目里反复横跳之后,我个人的体会是:路径规划的技术难点早就不是"调用某个算法库跑出路径",而是"选择合适的地图抽象、设计合理的约束表达、搭出能容忍参数波动的主流程"。蚁群算法及其改进给了我很大的设计空间,Dijkstra 的基准能力让可信度有了参照,遗传和势场又补上了复杂优化和实时避障两块拼图。如果你正准备从二维往三维扩展,建议先把二维地图上的调试平台跑稳,再一点一点加维度。三维的问题,很多时候不是三维特有的,而是你把二维里偷懒没做好的部分,放大了一百倍。