简介:这份PDF来自“华为杯”第十三届全国研究生数学建模竞赛,以蚁群算法为背景,完整呈现多无人机协同任务规划赛题的研究与求解方案,适合数学建模参赛者、无人机路径规划研究者及相关专业学生参考。内容从协同侦查、通信约束下调度到打击任务规划逐步展开,先提出分层规划模型与全局规划模型,再将任务拆解为目标群路线规划、群内目标路线规划、航迹优化等层级,利用多岛遗传算法、非线性内点法等求解;后续还针对雷达威胁差异扩展为多层级模型,分析了算法复杂度与无人机参数对作战效能的影响,并给出提升作战能力的改进策略。压缩包仅含1个PDF文件,大小约13.28MB,适合直接阅读、对照赛题原题与复盘建模思路。资源目前已有222人学习,对想系统理解多无人机任务建模、优化算法落地细节的读者有较高参考价值。
1. 蚁群算法与多无人机协同任务规划:一页方案背后的完整技术决策
刚拿到《蚁群算法-多无人机协同任务规划》这个标题时,很多人默认它是“一篇讲蚁群的科普”,但实际上多无人机协同任务规划才是真正的主菜。标题把组合优化方法、无人机集群、航迹与时序三类问题绑在一起,核心要回答的是:几十个任务点摆在面前,几架无人机分别去哪些点、按什么顺序走、怎么保证不撞机不超时。蚁群算法在整个方案里扮演的,是把“先分任务、再排路线、再协调时间”这个大搜索问题缩小成一个能收敛的优化过程。你如果正在做仿真验证、毕业论文或系统预研,这页内容真正值钱的部分不在公式本身,而在“怎么把一个可行的多机规划问题,编码成蚁群算法能进化出结果的样子”。这篇就按这个逻辑,把建模、编码、调参、踩坑和动态重规划一次讲透。
2. 从任务点集到多机约束:为多无人机协同规划做数学建模
2.1 多无人机任务规划的决策变量:谁来、去哪、先做哪个
多无人机协同任务规划,第一步不是写算法,而是定义“解是什么”。我一般把所有可调的东西拆成三个逻辑层:任务分配层、航迹层、时间层。任务分配层回答“任务点 1 归无人机 A 还是 B”,航迹层回答“无人机 A 访问任务点需要经过哪些航路点”,时间层回答“每架飞机什么时候出发、什么时候到达、在哪里等待”。
这三个层映射到代码里,最常见的表达方式是一个“分段序列”:把所有需要访问的任务点排成一条长序列,再按无人机数量切段,段与段之间没有共享任务点。比如有 9 个任务点、3 架无人机,序列[2,5,1, | 4,8,7, | 3,6,0]就表示无人机 1 依次执行 2、5、1 三个任务,其它两机类似。这种表达简单、改动少、一辆蚂蚁构造起来快,但它有个隐含问题:没人保证切出来的每一段在空间上紧凑。于是就有了另一种更常见的做法——“分组优先,组内排序”。
所谓分组优先,就是先让每只蚂蚁决定“每个任务点归属哪架无人机”,再用传统蚁群对这个分组内部的访问顺序进行优化。这种方式把分配和排序分成两个阶段,工程上直观,但解空间大,蚂蚁数不够时很容易陷入局部最优。我的做法是第一轮先跑一次快速聚类给任务点分堆,堆数和无人机数一致,堆内再用蚁群排序;如果约束复杂,比如有时间窗或载荷限制,就直接用一体化序列表达,把约束写进代价函数。对初学来讲,分段序列是最容易跑通、也最容易排查错误的选择。
2.2 目标函数与约束条件:用数学语言描述“协同”
协同不是口号,它必须变成可计算的数字。最基本的目标函数是总代价最小,总代价一般包含飞行距离、总完成任务时间、惩罚项三部分。以飞行时间为例:
J = w1 * max(T_uav) + w2 * mean(T_uav) + w3 * Penalty其中T_uav是每架无人机完成自身任务的总时间,取最大值就是为了让整个机群“都能按时收工”,这是协同任务里最常用的负载均衡思想。如果不加max(T_uav),蚁群很聪明地会把任务堆给某一架无人机,另外两架闲着,整体路径总和很小,但实际部署毫无意义。
约束条件才是多机协同和单机路径搜索最大的区别。我至少会在建模时写这些入代价函数:禁飞区约束、最大航程约束、时间窗约束。禁飞区很好办,给经过禁飞区的路径加一个极大惩罚系数;最大航程约束就是判每一段任务序列的累计距离是否超过无人机续航;时间窗约束麻烦一点,比如任务点 3 必须在 10 到 15 分钟之间被访问,那么到达时间早于 10 分钟就要加惩罚。
这里有个很关键的习惯:所有约束都应该写进代价函数,而不是在解码时直接丢弃解。直接把不满足约束的解丢弃,会让搜索空间出现大量空洞,蚂蚁经常找不到可行解。写进代价函数之后,蚂蚁偶尔会生成违法解,但代价巨大,搜索方向自然被推向可行区域。这个过程有点慢,却稳,适合工程落地。
2.3 建模顺序与数据底座:把地图变成可计算的结构
我一般在建模时脑子里固定三层数据:地图层、任务层、方案层。地图层是数字网格地形或航路点网络;任务层记录每个目标点的坐标、价值、时间窗、载荷需求;方案层承载编码后的任务序列和解码后的航迹、时序。下面这个表格是我在设计仿真项目时常用的数据组织方式:
| 数据层 | 内容 | 典型字段 |
|---|---|---|
| 地图层 | 无人机可飞区域、禁飞区、威胁区 | 网格坐标、禁飞半径、威胁强度 |
| 任务层 | 需要执行的业务目标点 | 任务点编号、坐标、时间窗、优先级 |
| 方案层 | 一批蚂蚁进化出的调度结果 | 无人机编号、任务序列、预计到达时间 |
我习惯把地图层的威胁区和禁飞区预计算成一个“代价场”,和距离一起参与启发信息的计算。距离近不是唯一标准,还要考虑路径经过区域的威胁强度。很多初学者只在J里算直线距离,结果蚁群规划的路径看起来整齐,一旦放到真实地图上就穿过禁飞区,这就是数据底座没搭好。先画好代价场,再让蚁群跑,排出来的路径才是能直接给飞行控制系统用的。
3. 把蚁群算法放进多无人机航线规划:状态转移、信息素更新与参数整定
3.1 编码与解空间:一只蚂蚁的“一条长任务”如何变成多机方案
蚁群算法天然是解决排列问题的,它最擅长的是在一个任务序列上迭代。所以做多无人机协同规划时,最直接的编码是把所有任务点编成一条序列,一只蚂蚁出行时走完所有任务点,得到一个排列,然后按前面介绍的解码方式切成多段分给不同无人机。
这种编码方式把搜索空间从“两两组合爆炸”的分配问题,简化成传统排列问题,算法压力小。但它的缺点是:序列切段的位置如果不参与搜索,固定按顺序切就不合理。举例,如果有一段任务点位置很散,恰好被切给一架无人机,这架无人机的总航程会远大于其它机。要解决这个问题,我的办法是在目标函数里让max(T_uav)的权重足够高,算法在进化过程中会自动“诱导”出切完比较均衡的序列。
如果项目更复杂,就需要“双染色体”编码:第一条染色体表示任务归属,第二条表示每个无人机内部的任务访问顺序。这种编码下蚂蚁的构建分两步,状态转移也要区分“分配层”和“排序层”,代码量翻倍,但支持时间窗、任务价值加权、禁飞区等多维约束。更推荐的新人路径是先跑通序列编码,理解蚁群和代价函数的联动之后,再升级到双染色体编码。
3.2 状态转移规则:选择下一个任务点的概率从哪来
蚁群算法进行路径构建时,每一只蚂蚁在当前位置i选择下一个未访问任务点j的概率,由经典公式决定:
P(i, j) = (tau(i,j)^alpha) * (eta(i,j)^beta) / Σ (tau(i,k)^alpha) * (eta(i,k)^beta)这里tau是信息素浓度,反映历史求解过程中这条边有多好;eta是启发信息,一般是当前点到候选点距离的倒数;alpha控制信息素的权重,beta控制距离启发信息的权重。alpha太高,蚂蚁会拼命跟着之前的信息素走,很快陷入同一路线;beta太高,蚂蚁只看眼前局部距离,全局协同会变差。
我常用的初始值是alpha=1.0、beta=2.0。这个组合保证信息素能积累经验,同时启发信息不至于让蚂蚁太短视。多无人机场景里,我还会把代价场额外加进eta,把任务点优先级也加入。例如优先级越高的任务点,对应的eta乘以一个 1.2 的系数,让蚂蚁更早选择它。改动虽小,对业务价值导向的结果却很明显。
3.3 信息素更新策略:全局更新、局部更新与挥发系数
信息素更新通常分两个阶段:局部更新和全局更新。局部更新在每只蚂蚁构造完解之后立刻执行,目的是降低已走过的边上的信息素,保持探索多样性;全局更新则是在一轮迭代结束之后,只增强当前最优解路径上的信息素,帮助后续蚂蚁聚集到优质解附近。标准做法是:
tau(i,j) = (1 - rho) * tau(i,j) + delta_tau(i,j)rho是挥发系数,表示信息素随时间衰减的速度。全局更新时只有最优路径上的delta_tau为正,其它边只衰减不增强。rho太大会让历史记忆快速消失,算法容易震荡;rho太小会让信息素累积过强,快速陷入局部最优。多无人机规划中,rho我一般取值 0.05 到 0.15 之间,问题规模越大,rho越要偏向小值。
关于信息素初始化的一个坑:如果把初始信息素都设成 0,前几轮所有概率都来自启发信息,算法会退化成贪心搜索。初始值应该给一个统一正数,通常设 1.0。另一个容易踩的坑是delta_tau = 1/best_cost,当best_cost是很大一个数时,增量很小,算法收敛慢;如果加一个常量Q放大增量,收敛会快得多。Q通常取 1 到 100 之间,视目标函数的量级而定。
下面这张参数表是我在单篇规划场景下的经验值,属于“起步范围”,后续要依据任务规模和收敛曲线微调:
| 参数 | 含义 | 起步值 | 参数调大后的影响 |
|---|---|---|---|
alpha | 信息素权重 | 1.0 | 更容易陷入局部最优 |
beta | 启发信息权重 | 2.0 | 偏向贪心,全局性变差 |
rho | 信息素挥发系数 | 0.1 | 收敛变慢但多样性更好 |
Q | 信息素增量缩放 | 10 | 收敛加快,过早收敛风险同步上升 |
n_ants | 蚂蚁数量 | 20 | 搜索更充分,但耗时增加明显 |
max_iter | 最大迭代次数 | 80 | 迭代多,信息素积累更充分 |
增量Q经常被忽略,但它直接影响全局更新的强度,很多“跑了几百轮还没收敛”的问题其实就是Q太小,优质路径得不到有效放大。
4. 跑通多无人机蚁群规划的最小 Demo:工程结构、核心代码与收敛判据
4.1 工程结构:数据输入、求解器、可视化输出三段划分
我跑这个方向时,无论项目多小都会把工程分成三块:建图与任务输入、蚁群求解器、结果解码与可视化。第一块生成任务点和代价场,第二块只负责迭代,第三块把最优序列解码成每架无人机的航点和时序,并画出对比图。严格分开的原因是多无人机规划的调试非常依赖可视化,如果求解器和可视化耦合在一起,改任务点、调参数都很难快速定位问题。
求解器内部保存三个关键结构:距离矩阵dist、信息素矩阵pheromone、启发信息矩阵heru。dist是任意两个节点间的欧氏距离,heru取距离倒数,pheromone在迭代中持续更新。这三者全部是二维方阵,维度等于“任务点数量 + 1”,多出的一个节点代表所有无人机的起点。
4.2 核心代码:一只蚂蚁的构建循环与多无人机解码
下面这段是我常用于跑通最小方案的核心代码。它只依赖一个numpy,环境配置成本很低,适合做算法验证和技术摸底。
import numpy as np def euclid(p1, p2): """两点欧氏距离,这里直接把坐标元组转成数组再求范数""" return float(np.linalg.norm(np.array(p1) - np.array(p2))) class Task: """任务点:只保存编号和坐标,业务字段按需后续扩展""" def __init__(self, idx, x, y): self.idx = idx self.pos = (x, y) class MultiUavACO: def __init__(self, tasks, start=(0, 0), n_uav=3, n_ants=20, max_iter=80, alpha=1.0, beta=2.0, rho=0.1): self.tasks = tasks self.start = start self.n_task = len(tasks) self.n_uav = n_uav self.n_ants = n_ants self.max_iter = max_iter self.alpha = alpha self.beta = beta self.rho = rho # 距离矩阵包含任务点和起点,起点索引放在最后一位 self.n_node = self.n_task + 1 self.dist = np.zeros((self.n_node, self.n_node)) coords = [t.pos for t in tasks] + [start] for i in range(self.n_node): for j in range(self.n_node): self.dist[i, j] = euclid(coords[i], coords[j]) # 启发信息为距离倒数,加 1e-6 防止距离为零时除零 self.heru = 1.0 / (self.dist + 1e-6) self.pheromone = np.ones((self.n_node, self.n_node)) def pick_next(self, current, unvisited): """按信息素和启发信息的有偏概率选择下一个任务点""" candidates = list(unvisited) weights = [] for j in candidates: tau = self.pheromone[current, j] ** self.alpha eta = self.heru[current, j] ** self.beta weights.append(tau * eta) weights = np.array(weights) weights = weights / weights.sum() return np.random.choice(candidates, p=weights) def construct(self): """一只蚂蚁构建完整任务序列:从起点出发,每次有偏选择下一任务点""" current = self.n_task # 起点索引 unvisited = set(range(self.n_task)) tour = [] while unvisited: nxt = self.pick_next(current, unvisited) tour.append(nxt) unvisited.remove(nxt) current = nxt return tour def evaluate(self, tour): """将任务序列切段分给各无人机,返回最长单机航程作为协同代价""" base = self.n_task // self.n_uav extra = self.n_task % self.n_uav loop_times = [] pos = 0 for u in range(self.n_uav): size = base + (1 if u < extra else 0) seg = tour[pos:pos + size] pos += size if not seg: loop_times.append(0) continue # 每架无人机都从起点出发,访问本段任务点后返回起点 t = self.dist[self.n_task, seg[0]] + self.dist[seg[-1], self.n_task] for i in range(len(seg) - 1): t += self.dist[seg[i], seg[i + 1]] loop_times.append(t) return max(loop_times) def run(self): """迭代求解:每轮构造 n_ants 个解,用全局最优路径更新信息素""" best_cost = np.inf best_tour = None for _ in range(self.max_iter): for _ in range(self.n_ants): tour = self.construct() cost = self.evaluate(tour) if cost < best_cost: best_cost = cost best_tour = tour # 全局更新:所有边先挥发,最优路径上的边再增强 self.pheromone *= (1 - self.rho) for i in range(len(best_tour) - 1): a, b = best_tour[i], best_tour[i + 1] self.pheromone[a, b] += 10.0 / best_cost return best_cost, best_tour这段代码的关键逻辑有三个地方。第一,pick_next是经典的赌轮盘选择,它用信息素的alpha次幂乘以启发信息的beta次幂算出每条边的权重,再归一化成概率;第二,construct让所有任务点都在一只蚂蚁的序列里,保证所有任务都被访问;第三,evaluate按无人机数量平均切段计算协同代价,取最长单机航程作为整体代价,这比直接求总航程更能体现多机负载均衡。
需要注意,这里的信息素更新只增强全局最优路径,是“全局最优索引导向”的最简版本。10.0 / best_cost里的10.0就是Q,如果你的任务点坐标数值很大,best_cost可能上千,增量变得很小,收敛会很慢,可以把Q调大到 100 甚至更高。
4.3 参数表与收敛判据:什么情况下你可以停止迭代
跑这段代码时,我最常看的不是最终路径,而是收敛曲线:每一轮的best_cost画成折线,观察从第几轮开始不再下降。下面的逻辑可以加到run方法末尾用来判断是否提前终止:
history = [] last_best = np.inf unchanged = 0 for it in range(self.max_iter): # ... 上面的构造与更新逻辑 history.append(best_cost) if abs(best_cost - last_best) < 1e-4: unchanged += 1 if unchanged >= 20: break else: unchanged = 0 last_best = best_cost当 20 轮迭代best_cost都没变化,继续跑下去基本只会浪费时间。此时优先观察一个指标:如果每条航迹都很短但任务分配明显不均匀,说明max(T_uav)权重不够,或Q值过小导致信息素无法引导蚂蚁走向均衡方案。如果航迹混乱无序,说明alpha偏低、rho偏高,信息素积累不起来。参数整定从来不是孤立的,必须回到现象去反推原因。
5. 多无人机协同规划的常见问题与排查:死锁、震荡与约束不满足
5.1 现象:迭代在 20 轮就停下来,但任务点没规划完
明明设置了 9 个任务点,最终解里却只覆盖了 7 个。这类问题最容易被日志骗到,因为算法收敛了,你以为是约束太强,其实是代码逻辑有漏洞。
原因通常出在“未访问任务点”没有进入代价函数。如果目标函数只看累计距离,蚂蚁当然更愿意少走几个点;少一个点距离更短,但业务上不可用。另一个常见原因是任务点集合在unvisited移除时出了错,比如set和list混用,导致部分下标没有被正确标记访问。
解决方法是先做纪律性检查:解码后的序列数量必须等于任务点总数,每个任务点只能出现一次。把这套校验写进evaluate方法开头,当作断言使用。如果触发断言,优先检查construct里的访问集合维护逻辑,而不是急着调alpha和rho。
5.2 现象:多机航线交叉,时间窗冲突没有被约束拦住
飞出结果后把每架无人机的航迹画在地图上,发现两条线交叉得很厉害,甚至同一时刻会到达同一点。你明明没有见过时间窗约束,却总感觉跑了约束等于没跑。
这通常不是算法失效,而是约束只以“最终路径”为检查对象,没有把时序放进去。多机协同里的“撞机”往往不发生在同一条路径上,而是发生在“同时经过同一个空间点”。
解决方法是把时间维带入代价函数:每架无人机按段内每个任务点的到达时间累加,一旦两架无人机同属波次、且到达时间差小于最小安全间隔,就给整条解加一个非常大的惩罚。这个惩罚必须大到让该解不可能成为最优,否则蚂蚁会反复产生危险解。常见做法是J += 1e4 * conflict_count,并且用这个惩罚后的值参与信息素更新,而不是用纯距离。
5.3 现象:信息素浓度过高,算法陷入同一条局部路径
观察收敛曲线会发现它下降得很快,20 代以内就贴到一条固定路径,之后 60 代完全不动。
这本质是多机任务规划中“早熟收敛”。信息素矩阵里被反复增强的边,在概率公式里占据绝对主导,unvisited里就算有更优解,蚂蚁也选不到。beta超过 4 就容易出现这个现象。此外rho如果小于 0.02,历史信息素几乎不消失,更严重。
解决思路有三个:调高rho到 0.1~0.2,调低alpha到 0.8 左右,或者在信息素更新公式里引入“最大最小限制”,把信息素浓度钳制在[tau_min, tau_max]区间。最大最小蚁群是最直接的后悔药,实现只需在每次更新后加一行np.clip,却能显著延迟早熟。
5.4 现象:目标函数权重失衡,规划结果不符合业务预期
有时候算法收敛得很漂亮,每条路径也短,但任务价值不均衡:高价值任务全分配给一架无人机执行,其它无人机只飞了一小段路。
这是权重设置问题。如果目标函数基本上是总飞行距离最小,算法自然会把任务集中给“顺路”的无人机,业务上却要求高价值任务分散执行、保证可靠性。
解决方法是把“任务优先级覆盖”拆成一个独立惩罚项。比如高优先级任务必须在 30 分钟内完成,超时按每超过 1 分钟加 100 的惩罚。这种惩罚写清楚后,蚁群会优先安排高价值任务到早出发的无人机上,而不是被距离牵着走。另外,w1 * max(T_uav)的w1也要给足够权重,w1和w2的比例我一般从 1:1.5 起步。
5.5 一个实用排查顺序:从代价函数到参数曲线
碰到诡异结果,我的排查顺序一定是从代价函数开始,而不是先调参数。第一步,把evaluate里每一项单独打印出来,看看是不是某项惩罚项贡献了异常大的值;第二步,把最优解以纯文本形式输出,检查解码后的任务序列是否完整;第三步,画收敛曲线,判断问题是早熟还是震荡;第四步,再动参数,每次只动一个。
这个顺序能在 80% 的场景下快速定位问题。拿“解不合理但收敛很快”的场景举例,如果你先去调rho,可能连调三天都没用,因为根源在约束没进代价函数。先查业务约束是否完整表达,再查解码逻辑,最后查参数,是我在这个方向上的核心经验。
6. 从静态规划到动态重规划:把蚁群算法用在实战的三条扩展路径
6.1 滑动窗口式动态重规划
静态规划假设所有任务点在一开始就确定。实际部署里,任务点会不断增删,无人机也会遇到临时威胁。我的做法是引入一个滑动窗口:每次只规划往后 5 到 10 个任务点,蚂蚁跑完一段,机群执行,同时新任务点进入窗口,再触发新一轮蚁群规划。这样蚁群从离线求解器变成在线规划器,代价是每次重规划都要保留上一轮的部分解作为初始信息素,否则每轮结果之间跳跃会很大,飞行控制跟不上。
6.2 上层调度加下层避障
我的实战习惯不给蚁群太大责任。蚁群负责“宏观调度”:决定任务归属与访问顺序;动态撞机和地形障碍交给速度规划层。两层之间通过一条通讯协议交换航点和时间窗。这样蚁群迭代慢一点也没关系,因为它的输出只是一个任务序列,底层避障随时可以微调航路,不影响调度目标。
6.3 三个我坚持的实战小习惯
第一,保留上一轮的全局最优序列做热启动,能让动态重规划节省一半时间;第二,把禁飞区和时间窗都做成可配置的独立模块,兵棋推演时改参数不用动算法;第三,每次实验跑完把参数和收敛曲线一起存档,否则几个月后再看结果,根本不知道这组图用的是rho=0.02还是rho=0.2,这种“找不到当初参数”的感觉是我排查过程里踩得最深的坑。我后来把所有仿真结果统一按日期加参数命名,才彻底不用后悔。冷静下来看,蚁群算法在多无人机协同规划里确实不是最先进的方法,但它足够直观、容易调试、对任务类型变化有很强适应力,尤其适合中等规模的任务场景。希望帮到你。
本文还有配套的精品资源,点击获取