☰
粒子群算法多无人机任务分配源码解析与Python实现
2026/10/26 15:56:13 网站建设 项目流程

简介:面向毕业设计、课程设计与期末大作业等场景,提供一套基于Python的粒子群算法多无人机任务分配实现,适合需要快速搭建智能优化算法演示项目的开发者。压缩包共15个文件,其中包含9个Python脚本,覆盖问题建模、距离与适应度计算、PSO主流程、结果解码及绘图等功能;另有4张PNG仿真结果图、1份Markdown说明文档,整体大小仅1.41MB,结构清晰、便于阅读和二次修改。目前已有72人学习下载,项目源码经过严格测试,可直接用于课程提交或二次开发。代码内含完整算法与可视化模块,既能帮助理解粒子群优化原理,也能直接作为多无人机任务分配课程的验收材料或毕业设计展示内容;README文档对模块功能做了梳理,能够在现有项目基础上扩展新的约束与求解策略。

1. 粒子群算法多无人机任务分配:拿到源码后第一步是看懂建模,而不是跑通代码

“基于 Python 开发的粒子群算法多无人机任务分配源码”这种标题,在毕业设计和课程设计里出镜率很高。多数人拿到压缩包第一件事就是跑 main.py,跑通了就以为完事,结果答辩时老师问一句“粒子维度为什么等于任务数”“续航惩罚系数怎么定的”就卡住。这个方向的实质不是粒子群算法本身——标准 PSO 主循环用不了 50 行——而是把“哪架无人机去执行哪个任务”变成粒子编码,再写目标函数、压住硬约束。它适合正在做毕设、课设或项目开发的从业者,先理解建模,后面代码和实验才有根基。这篇笔记会按建模、编码、主循环、改进、避坑、验证的顺序,把整个方案讲成可以直接落地的步骤。

2. 把任务分配“翻译”成粒子:目标函数、硬约束与三种编码方式

2.1 任务分配问题的数学形式:N 架无人机对 M 个任务,变量怎么定

多无人机任务分配的常见设定是:有 N 架无人机、M 个待执行任务,每架无人机从不同起点出发,速度、最大航程可能不同;每个任务有地理位置,部分任务带时间窗。要决策的是把任务分给哪些无人机,以及单架无人机内部按什么顺序执行。评价指标通常是总飞行距离最短、整体完成时间最小,或尽可能多地完成高优先级任务。毕设和课设里,最常用的是“总航程最短 + 续航约束”,因为这个组合最容易出图表,也最好讲解。

用数组表达是这样:无人机集合 U={1..N},任务集合 T={1..M},决策变量 x_{ij} 表示任务 j 是否分配给无人机 i。直接拿二维矩阵当粒子并不合适:矩阵会引入“一个任务同时被两架无人机选中”“某架无人机空着”这类大量无效组合,粒子群在黑匣子里搜索,效率会很难看。更常见的做法是把决策压成一维:每个任务 j 对应一个取值,这个值的含义是“任务 j 由编号几的无人机执行”。一维向量天然满足任务唯一性,也符合粒子群“每个粒子是一个解”的直觉。

目标函数一般写成所有无人机完成各自任务序列的总航程,也可在总航程上再加一个最大完成时间项,用权重平衡。用公式表达为 F = Σ_i sum(seq_i 上的相邻点距离),其中 seq_i 是第 i 架无人机的任务执行顺序。如果题目强调“整体完成时间最短”,就把所有无人机完成时间的最大值纳入目标,乘以一个权重。这里要特别留意:一维分配向量只决定“任务归谁”,每架无人机按什么顺序飞还需要二次求解。常见做法是最近邻贪心,从起点开始每次挑最近的任务,代码量小、运行快,视觉上也说得过去。如果题目要求同时优化分配和顺序,那就得换两段式编码,复杂度会上一个量级,对课程设计通常没有必要。

2.2 硬约束清单:续航、时间窗、任务唯一性,哪些必须进适应度

无论用什么编码,有三类约束会反复出现在题目和评分标准里。

任务唯一性:每个任务最多被执行一次。一维映射编码天然满足,这是选它的核心原因。续航约束:单架无人机累计航程不能超过最大航程,这条必须在适应度函数里处理,否则粒子会收敛出“一架无人机飞完全部任务”的极端解。时间窗约束:部分任务要求在某个时间区间内开始执行,时间窗一旦转成硬约束就很麻烦,因为标准粒子群是连续优化算法,处理离散的“必须在 10:00 到 10:30 到达”逻辑很别扭,通常也转成惩罚项。

这三条里,唯一性靠编码兜底,续航和时间窗靠惩罚或修复。注意不要把全部约束交给惩罚系数:罚太小,粒子无所谓;罚太大,粒子全部跑进“不做任务”的局部最优。这个平衡问题是后面第 5 章要展开的“玄学”现场。实际项目中,我一般把续航和完成率一起输出,报告里同时写“总航程 178.6,完成率 100%”,比只写一个无惩罚的 fitness 数字可信很多。

2.3 三种粒子编码方式对比:离散整数、连续映射和置换序列

在建模阶段就要决定粒子用什么编码。我见过的主流方案有三种。

第一种是直接离散整数编码,粒子每个维度直接取 0..N-1 的整数值,表示任务 j 被分配给无人机 i。最直观,但标准 PSO 的速度更新公式算出来是浮点数,必须取整离散化,很多教程直接 round,会导致大量粒子落在同一个整数上,种群多样性快速下降。

第二种是连续实数映射编码。粒子维度取 [0,1) 的实数,解码时乘无人机数量 N 后向下取整,得到无人机编号。这个方案保留了 PSO 连续搜索的优势,是一维编码里最值得照抄的形式。第三种是置换序列编码:把 M 个任务排成全排列,再用若干断点切分成 N 段,每段分给一架无人机。这种编码能同时表达“分给谁”和“按什么顺序做”,实现时又要额外维护断点位置,粒子维度不稳定,调试成本高,适合研究型课题。

三个方案放在一起对比,选择就清晰了:

编码方式粒子维度是否天然满足任务唯一性能否表达单机顺序适合场景
离散整数编码M需要额外约束修复否演示、快速原型
连续实数映射编码M是否毕业设计、课程设计首选
置换序列+断点M+N-1是是研究型项目、有充分调试时间

一句话记忆:粒子群算法擅长连续、光滑的浮点空间;整数空间会把 PSO 变成随机枚举。既然手里有连续映射这种稳妥方案,就不要用取整硬上。

2.4 距离矩阵怎么组织:一个索引约定牵动全部代码

无人机起点和任务点要放在同一个坐标系里计算距离,最终生成一个 (N+M) x (N+M) 的方阵,叫 dist_mat。索引约定必须统一:前 N 行是无人机起点,后 M 行是任务点。也就是说,任务 t 在 dist_mat 里的真实行号是 n_uav + t。这个约定贯穿整个解码、适应度和结果输出过程,一旦写错,程序不报错,但算出来的总航程完全错误。

很多翻车现场都出在这个索引上:把 dist_mat[cur][t] 写成了 dist_mat[cur][t],忘记了任务点从第 n_uav 行才开始。由于距离矩阵是对称的,某些小规模数据下结果只是偏大一点点,不容易被察觉;换数据或换规模之后,结果直接崩。项目开发时我习惯在 main 开头写一行调试输出,打印 dist_mat.shape 和 x.shape,几秒钟就能排除掉八成索引问题。这个习惯在第 5 章还会用到。

3. 用 Python 写出可复现的粒子群任务分配主循环

3.1 最小依赖环境:只用 numpy 和随机数,避免花哨库

这里给出的核心代码可以放进一个空工程直接跑,不需要除了 numpy 以外的第三方库。有的教程会引入 pyswarm、deap 或 matplotlib 做演示,但对毕设源码来说,外部依赖越少,答辩现场换机器跑崩的概率越低。装 numpy 的方式是 pip install numpy,如果你的环境里已经有 Anaconda,通常不需要再装。

在写代码之前,先固定随机种子。粒子群是一个随机优化算法,不固定种子的话,每次运行结果都会有波动;固定种子一方面方便你调参对比,另一方面避免老师现场跑两次结果不一致的尴尬。固定种子放主程序入口,而不是放在函数内部。

import numpy as np # 固定随机种子,保证每次运行结果可复现 np.random.seed(42)

固定种子之后,整个算法里所有 np.random 调用都会按同一套序列走。如果你想测试“不同初始种群的稳定性”,可以用 seed = 42 + run_id 的方式做多次独立运行,这个技巧在第 6 章还会用到。

3.2 初始化无人机与任务:生成距离矩阵的数据结构

先定义问题规模。为了演示,我通常生成一个 20 x 20 的二维平面,5 架无人机、10 个任务,并随机生成位置。在实际项目里,这个位置数据可以换成经纬度或园区坐标,只要替换生成逻辑,后面的距离计算不用动。

距离矩阵是这段代码的核心产物。它的形状是 (N+M, N+M),dist[i][j] 表示节点 i 到节点 j 的欧氏距离。为了单机内部排序,还需要任务到任务的距离,所以一次性生成完整方阵,再在计算时切片。

N_UAV = 5 # 无人机数量 N_TASK = 10 # 任务数量 MAP_SIZE = 20.0 # 地图边长 # 随机生成无人机起点和任务位置,第一段是无人机,后面是任务 nodes = np.random.rand(N_UAV + N_TASK, 2) * MAP_SIZE uav_pos = nodes[:N_UAV] # shape: (5, 2) task_pos = nodes[N_UAV:] # shape: (10, 2) # 全量距离矩阵:前 N_UAV 行是无人机起点,后 N_TASK 行是任务位置 dist_mat = np.zeros((N_UAV + N_TASK, N_UAV + N_TASK)) for i in range(N_UAV + N_TASK): for j in range(N_UAV + N_TASK): if i != j: dist_mat[i][j] = np.linalg.norm(nodes[i] - nodes[j])

这段代码里,dist_mat 的行列索引含义必须统一:索引 0..N_UAV-1 是无人机起点,N_UAV..N_UAV+N_TASK-1 是任务点。后面 decode 和适应度计算都靠这个索引约定。很多翻车现场就是索引写岔了,结果算出来单机航程为零但总距离很小,看起来“最优”,实际根本没有执行任务。

3.3 PSO 主循环:速度更新、边界裁剪与全局最优更新

标准粒子群速度更新公式不多解释:v = wv + c1r1*(pbest - x) + c2r2(gbest - x)。关键是几个参数在一开始就要给足:惯性权重 w 取大一点,保证前期探索;c1、c2 取 1.5 到 2.0 的常见区间。这里给一组稳定的默认值:w = 0.8,c1 = 1.8,c2 = 1.8,种群 60,迭代 200。

边界处理不用太复杂。因为粒子每个维度代表连续映射值,合法区间就是 [0, 1),越界直接 clip 回去即可。这样做虽然损失了一部分原 PSO 的“越界反弹”特性,但任务分配问题边界并不敏感,clip 最简单可靠。

def pso_task_alloc(dist_mat, n_uav, n_task, pop_size=60, max_iter=200, w=0.8, c1=1.8, c2=1.8): dim = n_task low, high = 0.0, 1.0 # 种群位置和速度 x = np.random.uniform(low, high, (pop_size, dim)) v = np.random.uniform(-0.1, 0.1, (pop_size, dim)) pbest = x.copy() pbest_fit = np.array([fitness(ind, dist_mat, n_uav, n_task) for ind in x]) gbest_idx = np.argmin(pbest_fit) gbest = pbest[gbest_idx].copy() gbest_fit = pbest_fit[gbest_idx] for it in range(max_iter): for i in range(pop_size): r1 = np.random.rand(dim) r2 = np.random.rand(dim) v[i] = w * v[i] + c1 * r1 * (pbest[i] - x[i]) + c2 * r2 * (gbest - x[i]) x[i] += v[i] x[i] = np.clip(x[i], low, high) fit = fitness(x[i], dist_mat, n_uav, n_task) if fit < pbest_fit[i]: pbest_fit[i] = fit pbest[i] = x[i].copy() gbest_idx = np.argmin(pbest_fit) if pbest_fit[gbest_idx] < gbest_fit: gbest = pbest[gbest_idx].copy() gbest_fit = pbest_fit[gbest_idx] return gbest, gbest_fit

提示:pbest 和 gbest 赋值务必使用 copy(),否则后续 x 更新时,gbest 的引用会跟着变,全局最优永远停在初始值附近。这是 PSO 实现里最常见的隐蔽坑。

几个细节是血泪经验:速度初始化不要太大,取 -0.1 到 0.1;位置在 [0,1) 区间内,太大的初速度会让粒子飞出去再被 clip 回来,浪费迭代次数。收敛判据这里用的是最大迭代次数,工程上可以加一个“连续 30 代 gbest 没变化就提前停”的逻辑,能在答辩演示时省时间。

3.4 解码与适应度:给每个任务找无人机,再算总飞行距离

前面提到连续映射解码:每个任务的维度实数乘无人机数量,向下取整得到无人机编号。这里要小心一个边界条件:当某个值恰好等于 1.0 时,乘以 N_UAV 后取整可能得到 N_UAV,而合法编号只能到 N_UAV-1,所以要 clip 一下。

def decode(x, n_uav): # x: (n_task,),每个值在[0,1)之间 assign = (x * n_uav).astype(int) assign = np.clip(assign, 0, n_uav - 1) return assign

解码之后得到一个长度为 N_TASK 的数组 assignment[j] = i,表示任务 j 分配给无人机 i。接下来的适应度计算分三步:第一步按无人机分组取出它负责的任务;第二步用之前生成的 dist_mat 片段做单机最近邻排序,算单机航程;第三步把所有单机航程加总,再对超续航的方案施加惩罚。

def fitness(x, dist_mat, n_uav, n_task): assign = decode(x, n_uav) total_dist = 0.0 uav_tasks = {i: [] for i in range(n_uav)} for j, uav in enumerate(assign): uav_tasks[uav].append(j) for i, tasks in uav_tasks.items(): if not tasks: continue route = [i] # 起点是无人机 i 自身 remain = list(tasks) # 最近邻:依次挑最近的任务点访问 while remain: cur = route[-1] dists = [dist_mat[cur][n_uav + t] for t in remain] nxt = remain[int(np.argmin(dists))] route.append(n_uav + nxt) remain.remove(nxt) # 最后飞回起点 route.append(i) single = sum(dist_mat[route[k]][route[k + 1]] for k in range(len(route) - 1)) total_dist += single return total_dist

注意 dist_mat 索引里,任务 t 在 dist_mat 中的真实行号是 n_uav + t。这一点非常容易漏:距离矩阵是无人机起点和任务点拼在一起的,直接写 dist_mat[cur][t] 会把任务号当成矩阵行号,得到完全错误的结果。最近邻排序是贪心,不是最优顺序,但对任务分配阶段的适应度排序足够;如果你希望更精确,可以把这一段替换成动态规划或可复用 TSP 求解器。

3.5 跑起来:主程序、随机种子与结果导出

把上面函数整合到一个 main 里,跑一次完整的分配并打印结果。这里再放一个结果导出逻辑,输出每架无人机的任务序列和总航程。对于毕设报告,这个输出可以直接粘进实验结果章节。

if __name__ == "__main__": np.random.seed(42) # 前面 3.2 已生成 N_UAV、N_TASK、dist_mat 等变量 best_x, best_fit = pso_task_alloc(dist_mat, N_UAV, N_TASK) assign = decode(best_x, N_UAV) print("最优适应度(总航程):", round(best_fit, 2)) for uav in range(N_UAV): tasks = [j for j, a in enumerate(assign) if a == uav] print(f"UAV{uav}: 任务序列 {tasks}")

主程序如果只想看最终结果,建议同时打印每次迭代的 gbest_fit 曲线数据。要实现这个,在 pso_task_alloc 里加一行 history.append(gbest_fit) 即可。曲线数据保存成 txt 或 csv,后面画图时直接读取,不用重跑算法。这是项目开发里常见的“先留证据,再补图表”的做法。

4. 从能跑到“敢答辩”:三种让分配结果更稳的改进策略

4.1 续航违约的惩罚函数:惩罚系数怎么定才不翻车

如果完全不管续航,PSO 很容易找到“让一架无人机飞完全部任务”的极端解——总距离可能确实最小,但航程远远超出实际能力。惩罚函数是最直接的硬约束处理:在 fitness 末尾加上超出部分乘系数。

PENALTY = 50.0 endurance = np.array([100.0] * n_uav) # 每架无人机最大航程 # 在 3.4 的 fitness 循环里,对每架无人机的 single 做检查 # for i, single in enumerate(...): if single > endurance[i]: total_dist += (single - endurance[i]) * PENALTY

惩罚系数最忌拍脑袋。如果距离量级是 50 到 200,而惩罚系数只有 0.1,那超出的 100 距离只罚 10,粒子完全不在乎;如果系数是 5000,那所有粒子都会优先找“少做任务”的方案,结果每架无人机都在家门口转圈。我一般先完全不罚跑一次,看 fitness 的平均量级,然后取量级的 2 到 5 倍作为惩罚系数。通过运行两三次就能落到稳定区间,这就是“先量纲、后数值”的调参流程。

4.2 修复优先于惩罚:超续航任务重新分配,保证方案合法

惩罚函数的问题是只能“引导”,不能保证最终解合法。如果你的评分标准要求“最终方案必须全部在续航内”,就需要在解码之后、算适应度之前做修复。

修复逻辑不复杂:先算每架无人机的航程,找到超续航的那架,把它航程末尾的任务拿出来,重新分配给离该任务最近且加装之后不超续航的另一架无人机;如果所有无人机都无法接收,就把这个任务标记为“未执行”。这样得到的解一定满足续航约束,缺点是每次修复都会改变分配,PSO 在连续空间中的梯度信息被破坏了,收敛速度会慢一些。

更工程化的做法是“惩罚 + 修复结合”:粒子群迭代阶段用惩罚函数引导速度方向,算法结束后对最优解做一次修复,保证展示时拿到的方案严格合法。这样既保证了收敛性,又保证最终结果能通过规则检查。这一招很多项目里都会用,但你几乎看不到有人写进论文。

4.3 惯性权重线性递减:别让一个 w 参数毁掉整个算法

标准 PSO 的 w 是常数,这有个问题:固定的大 w 让粒子一直高速探索,后期收不住;固定的小 w 收敛快,但很容易一头扎进局部最优。最实用的改进是对 w 做线性递减,让粒子前期到处飞,后期集中在最优解附近精修。

w_max, w_min = 0.9, 0.4 for it in range(max_iter): w = w_max - (w_max - w_min) * it / max_iter # 速度更新时使用当前 w

参数含义:w_max 取 0.9 是 PSO 文献里的经典值,能保证前期有足够探索能力;w_min 取 0.4 表示后期仍保留 40% 的惯性,避免过快地陷入停滞。若任务规模小于 10,可以把迭代上限减小到 150;当任务数大于 50,建议迭代 400 起步。注意 w 递减的范围不宜过窄,0.8 到 0.6 这种区间基本起不到“前期探索、后期开采”的分段作用。

4.4 差粒子变异与重启:最简单有效的防早熟手段

线性递减 w 还有一个短板:如果前 30 代粒子都已经聚集到某个局部最优附近,后面 w 再小也救不回来。一个简单粗暴的手段是,每过一定代数,找出适应度最差的一部分粒子,把它们重新初始化或做随机扰动。这个操作相当于给种群打强心针。

if it % 50 == 0: worst_idx = np.argsort(pbest_fit)[-int(0.2 * pop_size):] for idx in worst_idx: x[idx] = np.random.uniform(low, high, dim) v[idx] = np.random.uniform(-0.1, 0.1, dim) pbest_fit[idx] = fitness(x[idx], dist_mat, n_uav, n_task) pbest[idx] = x[idx].copy()

这段代码在每 50 代把最差的 20% 粒子重置。变异比例 20% 不要设太大,否则种群容易从稳定收敛变成随机搜索;也不要太频繁,建议每 30 到 50 代触发一次。注意重置后要立刻更新 pbest_fit,不然下一次循环 gbest 计算可能读到陈旧的适应度值。加了变异之后,迭代曲线会出现周期性的跳变,这是正常现象,不等于算法坏了。看到曲线终于不再“一路平”,反而说明这一点奏效了。

4.5 怎么判断改进真的有效:看收敛曲线的三种形态

改进是否有效,不是“跑一次结果变好了”能证明的。把每次迭代的 gbest_fit 存下来画成收敛曲线,常见三种形态:第一类是曲线在一二十代内陡降后立刻水平,说明种群迅速同质化,改进策略还没有发挥作用;第二类是曲线持续缓慢下降,直到迭代结束也没有明显水平,说明迭代次数不够,可以加长 max_iter;第三类是曲线下降中出现周期性小幅回升,这正是变异策略在重置差粒子,属于正常现象。判断改进有效,要看曲线最终平稳值是否明显低于原版 PSO,且在更换随机种子后仍然保持。

5. 粒子群多无人机任务分配源码的五个高频坑与排查

5.1 粒子维度设成无人机数量,解码越界

现象:一运行就报 index out of bounds,或者生成的分配结果里所有任务都堆到编号最大的无人机上。

原因:把 dim 设为 N_UAV,而正确的维度应当是任务数 N_TASK。粒子群每个维度对应一个任务,维度上的取值决定这个任务由哪架无人机执行。

解决:初始化 x = np.random.uniform(0, 1, (pop_size, n_task))。如果是从别人源码改来的,记得把解码函数里的乘 n_uav 和维度定义同时改掉,两个位置不一致时最容易出现“不报错但结果很怪”。

5.2 全局最优一直是初始值:变量引用没拷贝

现象:迭代了很多代,gbest_fit 纹丝不动,但单步打印 pbest 却在变。

原因:gbest = pbest[idx] 直接把 pbest[idx] 的引用赋给 gbest,之后 pbest[idx] 被更新成新位置,gbest 也同步变了,全局最优永远停留在初始值附近。

解决:统一用 gbest = pbest[idx].copy(),pbest[i] = x[i].copy()。这是 PSO 实现里最容易犯也最难排查的引用坑。排查方法是在速度更新前打印 gbest 的值,看它是否随迭代意外变化。

5.3 所有粒子收成同一个局部最优

现象:迭代曲线前 20 代下降很快,之后 100 代几乎不动,解的出现位置也很相似。

原因:初始化时全体粒子都挤在 [0,1) 中部,加上 c1、c2 偏大,社会学习分量太强,种群快速同质化。

解决:初始化时用 np.random.uniform(0.1, 0.9) 拉开间距,并把 c2 降到 1.5 左右,给个人认知更多分量。也可以叠加第 4 章的变异手段,定期重置差粒子。要判断是否真的早熟,可以打印每一代所有粒子的平均适应度:如果平均值和全局最优一样低,说明种群已经失去多样性。

5.4 惩罚系数拍脑袋,分配结果全是“不执行任务”

现象:最终方案里大量任务没有分配出去,总距离很小,但整体完成率低得没法看。

原因:惩罚系数过大,导致任何超续航方案都变成天价,粒子从不可行区域直接被推回“每架无人机都只做一个任务”的解。PENALTY 从 100 改到 10000,结果天差地别,这就是最常见的“玄学”现场。

解决:先统计一次不罚分时的平均航程和最大航程,惩罚系数设为平均航程的 2~5 倍。并且把完成率写入输出,最后报告的指标要同时看距离和完成率,不要只看 fitness 降了多少。

5.5 换了机器跑,结果全部错乱:numpy 广播与索引问题

现象:同一份源码在自己的电脑上正常,换台电脑或换 numpy 版本后,结果出现 NaN 或任务索引错位。

原因:常见有两类。一类是 dist_mat 索引写成 dist_mat[cur][t],而 t 是任务编号,没有加上 n_uav 偏移,当数据规模恰好某种特殊组合时,偏差不明显,但换数据就崩。另一类是 numpy 版本差异下,x * n_uav 对二维数组的广播行为不同,如果 x 的 shape 是 (pop_size, 1) 而不是 (pop_size, n_task),乘出来是 (pop_size, pop_size) 的诡异矩阵。

解决:在解码函数里加上形状断言 assert x.shape == (n_task,),对 dist_mat 访问统一用 n_uav + t。跑代码前,可以在 main 开头打印 dist_mat.shape 和 x.shape,几秒钟就能排除掉八成索引问题。

6. 验证分配结果可靠性的一个技巧:基线对比与 20 次独立运行

6.1 贪心基线:给 PSO 一个“下限”

粒子群是随机优化算法,答辩现场最怕被问“再跑一次还是这个结果吗”。我的习惯是每次实验都同时算一条贪心基线:依次把每个任务分配给当前最近的无人机,再把超续航的任务移除。贪心法没有搜索过程,结果通常比 PSO 差,但它是“下限”。真正说服人的不是“适应度 187.6”,而是“比贪心低 23%,20 次独立运行的标准差只有 3.1”。

def greedy_assign(dist_mat, n_uav, n_task): assign = np.zeros(n_task, dtype=int) for j in range(n_task): dists = [dist_mat[i][n_uav + j] for i in range(n_uav)] assign[j] = int(np.argmin(dists)) return assign

注意这段贪心不处理航程约束,所以只作为“不做搜索”的下限,对比时最好把 PSO 的完成率也统计出来。贪心基线的代码价值不在性能,在于提供一个可复现的比较对象。

6.2 多次独立运行:报告里最有说服力的三行数字

固定种子可以复现,但只给一个固定种子的结果会被质疑“是不是挑出来的”。正确做法是把种子从 100 到 119 跑 20 次,记录每次的最优适应度,再输出平均值、最优值和标准差。

best_list = [] for run in range(20): np.random.seed(100 + run) best_x, best_fit = pso_task_alloc(dist_mat, N_UAV, N_TASK) best_list.append(best_fit) print("20次平均:", round(np.mean(best_list), 2)) print("20次最优:", round(np.min(best_list), 2)) print("标准差:", round(np.std(best_list), 2))

标准差小说明算法稳定性好,平均最优值低说明搜索能力强,这两个数字放进论文比十页伪代码更有说服力。把每次运行的前期数据画成叠加收敛曲线,还能直观看到多次运行之间的波动范围,这是毕设报告里最出效果的一张图。

最后照例讲一个教训。我做这类项目时曾经花了一整天调参数,终于在某组数据上把结果刷得很漂亮,结果换成另一组随机生成的无人机起点后,方案直接退化得不如贪心。后来才明白,这不是算法的问题,而是我把参数“背”进了那组数据。现在每次调参完,我都会换两到三组随机地图重新验一遍,确认改进不是运气,才敢写进结论。这个习惯后来又帮我在项目交付里避免了至少一次返工。

希望这些建模、编码和排查的经验能帮到你;把粒子群多无人机任务分配源码真正跑通、跑稳,毕业设计、课程设计和项目开发里的这个方向就不会再绕远路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询