
简介本资源是一套基于Python实现的粒子群优化PSO算法多无人机任务分配完整源码专为计算机、人工智能、自动化及通信类专业学生设计适用于毕业设计、课程大作业与科研入门实践。代码已通过实际调试与运行验证答辩评分高达98分兼顾理论可解释性与工程可用性小白可直接运行学习进阶者亦可基于模块化结构如pso.py任务调度核心、fit_dis.py适应度计算、plots.py可视化等进行功能扩展与算法改进。压缩包共15个文件含9个Python源码覆盖算法主流程、解码、距离计算、约束处理等关键环节、4张PNG图表含甘特图、飞行轨迹图、散点图等结果可视化、1份README.md说明文档及1个.gitattributes配置文件整体仅1.41MB轻量易部署。目前已有168人下载学习内容结构清晰、注释充分是理解智能优化算法在多智能体协同中落地应用的优质参考范例。1. 粒子群算法跑通多无人机任务分配不是调参玄学是坐标、距离、约束三要素闭环验证你手头有一组5架无人机、8个地面目标要求每架无人机至少分配1个任务、最多3个总航程最短——这不是考数学建模而是毕业答辩现场被导师当场追问“你这个PSO的适应度函数怎么防飞越约束怎么硬编码进粒子更新为什么迭代50次就停”。这份基于Python实现的粒子群算法PSO多无人机任务分配源码就是为这种真实答辩场景打磨出来的它不抽象讲“群体智能”而是把任务编码→距离建模→约束嵌入→可视化验证全链路写死在代码里。项目含完整可运行脚本main.py、7个核心模块pso.py/fit_dis.py/condition.py等、6张过程图tu_fly.png/tu_scatter.png等所有参数可调、所有中间结果可打印、所有约束条件可开关。适合计算机/自动化/人工智能专业学生做毕设、课程设计或竞赛原型开发——尤其当你被问到“你的算法怎么保证不分配重复任务”“怎么处理无人机续航差异”时这份代码能直接打开condition.py给你指行号。2. 从坐标建模到任务编码粒子如何表达“哪架无人机飞哪个点”2.1 坐标系统与任务空间定义用numpy数组固化地理语义项目默认采用二维笛卡尔坐标系建模所有位置数据存于globalv.py中统一管理# globalv.py 关键片段 import numpy as np # 无人机初始位置 (x, y)shape(5, 2) UAV_POS np.array([ [0.0, 0.0], # UAV0 起点 [10.0, 0.0], # UAV1 起点 [0.0, 10.0], # UAV2 起点 [10.0, 10.0], # UAV3 起点 [5.0, 5.0] # UAV4 起点 ]) # 任务点坐标 (x, y)shape(8, 2) TASK_POS np.array([ [2.0, 3.0], # Task0 [8.0, 2.0], # Task1 [1.0, 8.0], # Task2 [9.0, 9.0], # Task3 [4.0, 1.0], # Task4 [6.0, 8.0], # Task5 [3.0, 6.0], # Task6 [7.0, 4.0] # Task7 ])提示UAV_POS和TASK_POS必须严格保持(N_UAV, 2)和(N_TASK, 2)形状否则distance.py中向量化计算会报ValueError: operands could not be broadcast together。新手常误把经纬度直接填入——这里只接受平面直角坐标若需WGS84坐标必须先用pyproj转成UTM投影。2.2 任务分配编码整数编码 vs. 实数编码的实战取舍PSO传统用实数向量但任务分配本质是离散组合优化。本项目采用整数编码解码映射策略在decode.py中实现# decode.py 核心逻辑 def decode_particle(particle, n_uav, n_task): 将连续粒子向量解码为整数任务分配矩阵 particle: shape(n_uav * n_task,) 连续值向量 返回: assignment_matrix, shape(n_uav, n_task), 元素为0/1 # Step 1: 归一化到[0,1] normed (particle - particle.min()) / (particle.max() - particle.min() 1e-8) # Step 2: 按UAV分组每组取top-k最大值索引k3为最大任务数 assignment np.zeros((n_uav, n_task)) for uav_idx in range(n_uav): start_idx uav_idx * n_task end_idx start_idx n_task scores normed[start_idx:end_idx] # 取前3个最高分对应的任务索引 top3_idx np.argsort(scores)[-3:][::-1] # 降序取前三 assignment[uav_idx, top3_idx] 1 return assignment为什么不用one-hot编码因为one-hot会导致粒子维度爆炸5架×8任务40维PSO易早熟而整数编码将搜索空间压缩到8^532768种可能远小于2^40且decode.py中通过top-k机制天然满足“单架无人机最多3任务”的硬约束。2.3 距离计算模块向量化加速与单位一致性校验distance.py不调用scipy.spatial.distance而是手写向量化欧氏距离# distance.py def calc_distance_matrix(uav_pos, task_pos): 计算所有UAV到所有Task的欧氏距离矩阵 返回: dist_mat, shape(n_uav, n_task) uav_exp uav_pos[:, np.newaxis, :] # (5,1,2) task_exp task_pos[np.newaxis, :, :] # (1,8,2) diff uav_exp - task_exp # (5,8,2) return np.sqrt(np.sum(diff**2, axis2)) # (5,8) # 在main.py中调用示例 dist_mat calc_distance_matrix(globalv.UAV_POS, globalv.TASK_POS) print(距离矩阵形状:, dist_mat.shape) # 输出: (5, 8) print(UAV0到Task0距离:, dist_mat[0, 0]) # 直接查表毫秒级参数说明uav_pos和task_pos必须为float64类型否则np.sqrt在GPU加速下可能溢出距离单位与坐标单位一致如米若坐标是千米需在fit_dis.py中乘1000统一量纲calc_distance_matrix返回的是原始距离后续fit_dis.py会根据任务权重加权求和。3. 约束嵌入与适应度设计让PSO不飞越、不超载、不漏任务3.1 硬约束三重门condition.py中的不可协商条款condition.py是本项目的约束中枢所有不可违反的业务规则在此硬编码# condition.py def check_feasibility(assignment_matrix, max_tasks_per_uav3, min_tasks_per_uav1): 检查分配方案是否满足硬约束 assignment_matrix: shape(n_uav, n_task), 元素为0/1 返回: is_feasible (bool), penalty (float) n_uav, n_task assignment_matrix.shape penalty 0.0 # Constraint 1: 每架无人机任务数在[min, max]之间 uav_task_count np.sum(assignment_matrix, axis1) # shape(n_uav,) for i, cnt in enumerate(uav_task_count): if cnt min_tasks_per_uav: penalty 1000 * (min_tasks_per_uav - cnt) # 严重惩罚 if cnt max_tasks_per_uav: penalty 1000 * (cnt - max_tasks_per_uav) # 严重惩罚 # Constraint 2: 所有任务必须被分配全覆盖 total_assigned np.sum(assignment_matrix) if total_assigned n_task: penalty 5000 * (n_task - total_assigned) # 最高优先级惩罚 # Constraint 3: 无重复分配每任务仅1架无人机执行 task_assignment_count np.sum(assignment_matrix, axis0) # shape(n_task,) if np.any(task_assignment_count 1): penalty 2000 * np.sum(task_assignment_count[task_assignment_count 1] - 1) return penalty 0, penalty关键设计点惩罚值非线性递增1000×超限数确保PSO在进化中主动规避不可行解check_feasibility返回布尔值is_feasible供pso.py判断是否接受该粒子而非简单加罚分第3条“无重复分配”约束防止同一任务被多架无人机争抢——这是多无人机协同中最易翻车的逻辑漏洞。3.2 适应度函数航程最小化 约束惩罚的双目标平衡fit_dis.py将距离与约束耦合为最终适应度# fit_dis.py def fitness_function(particle, dist_mat, n_uav, n_task): 计算粒子适应度总航程 约束惩罚 # Step 1: 解码得到分配矩阵 assignment decode.decode_particle(particle, n_uav, n_task) # Step 2: 检查可行性并获取惩罚项 is_feasible, penalty condition.check_feasibility(assignment) # Step 3: 计算总航程仅对已分配任务求和 total_distance 0.0 for uav_idx in range(n_uav): assigned_tasks np.where(assignment[uav_idx] 1)[0] for task_idx in assigned_tasks: total_distance dist_mat[uav_idx, task_idx] # Step 4: 适应度 航程 惩罚不可行解适应度极大自然淘汰 fitness total_distance penalty return fitness为什么不用归一化航程因为约束惩罚值1000~5000远大于典型航程几十到几百归一化会削弱约束效力。本设计让PSO明确感知“宁可多飞100米也绝不能漏1个任务”。3.3 PSO核心参数配置pso.py中的收敛性调控pso.py采用标准PSO变体但针对任务分配问题做了关键调整# pso.py 关键参数 class PSO: def __init__(self, n_particles50, n_dimsNone, w0.7, c11.5, c21.5, max_iter100, bounds(-5, 5)): self.n_particles n_particles self.n_dims n_dims # n_uav * n_task self.w w # 惯性权重0.7保证收敛性非0.9避免震荡 self.c1 c1 # 认知因子1.5强调个体经验 self.c2 c2 # 社会因子1.5强调群体协作 self.max_iter max_iter self.bounds bounds def optimize(self, fitness_func, dist_mat, n_uav, n_task): # 初始化粒子位置和速度 pos np.random.uniform(self.bounds[0], self.bounds[1], (self.n_particles, self.n_dims)) vel np.random.uniform(-0.5, 0.5, (self.n_particles, self.n_dims)) # 初始化个体最优和全局最优 pbest_pos np.copy(pos) pbest_fit np.array([fitness_func(p, dist_mat, n_uav, n_task) for p in pos]) gbest_idx np.argmin(pbest_fit) gbest_pos pbest_pos[gbest_idx].copy() gbest_fit pbest_fit[gbest_idx] # 迭代优化 for it in range(self.max_iter): # 更新速度带边界反射 r1, r2 np.random.rand(2) vel (self.w * vel self.c1 * r1 * (pbest_pos - pos) self.c2 * r2 * (gbest_pos - pos)) # 边界处理超出则反向弹回非截断 pos vel for i in range(self.n_particles): for j in range(self.n_dims): if pos[i, j] self.bounds[0]: pos[i, j] self.bounds[0] (self.bounds[0] - pos[i, j]) vel[i, j] * -0.8 # 反弹衰减 elif pos[i, j] self.bounds[1]: pos[i, j] self.bounds[1] - (pos[i, j] - self.bounds[1]) vel[i, j] * -0.8 # 评估新位置 for i in range(self.n_particles): fit fitness_func(pos[i], dist_mat, n_uav, n_task) if fit pbest_fit[i]: pbest_pos[i] pos[i].copy() pbest_fit[i] fit if fit gbest_fit: gbest_pos pos[i].copy() gbest_fit fit if it % 20 0: print(fIter {it}: Best fitness {gbest_fit:.2f}) return gbest_pos, gbest_fit参数选择依据w0.7低于0.4易陷入局部最优高于0.8收敛慢c1c21.5任务分配问题中个体经验和群体信息同等重要max_iter100经实测50次迭代常未收敛100次足够稳定边界反射机制比简单截断更利于探索vel * -0.8模拟物理碰撞衰减避免粒子在边界震荡。4. 可视化验证与结果解读6张图说清算法到底干了什么4.1tu_fly.png无人机飞行轨迹热力图该图由plots.py生成叠加了所有无人机的路径# plots.py 中绘制飞行轨迹 def plot_flight_paths(uav_pos, task_pos, assignment_matrix, filenametu_fly.png): plt.figure(figsize(10, 8)) # 绘制任务点红色三角 plt.scatter(task_pos[:, 0], task_pos[:, 1], cred, marker^, s100, labelTasks) # 绘制无人机起点蓝色圆圈 plt.scatter(uav_pos[:, 0], uav_pos[:, 1], cblue, markero, s80, labelUAV Start) # 绘制每架无人机的飞行路径 colors [green, orange, purple, brown, pink] for uav_idx in range(len(uav_pos)): assigned_tasks np.where(assignment_matrix[uav_idx] 1)[0] if len(assigned_tasks) 0: continue # 起点 - 任务点连线 for task_idx in assigned_tasks: plt.plot([uav_pos[uav_idx, 0], task_pos[task_idx, 0]], [uav_pos[uav_idx, 1], task_pos[task_idx, 1]], colorcolors[uav_idx % len(colors)], linewidth2, alpha0.7) plt.xlabel(X Coordinate) plt.ylabel(Y Coordinate) plt.title(UAV Flight Paths to Tasks) plt.legend() plt.grid(True, alpha0.3) plt.savefig(filename, dpi300, bbox_inchestight) plt.close()看图要点若某架无人机连线交叉密集 → 说明其分配任务地理上分散可能增加总航程若某任务点无任何连线 →condition.py中“全覆盖”约束失效需检查penalty是否生效蓝色起点与红色任务点距离应明显小于连线长度否则坐标系单位错误。4.2tu_scatter.png粒子群收敛过程散点图该图展示PSO迭代中粒子分布演化# plots.py 中粒子分布快照 def plot_pso_convergence(history_positions, history_fitness, filenametu_scatter.png): fig, axes plt.subplots(2, 3, figsize(15, 10)) axes axes.flatten() # 取第0,20,40,60,80,100次迭代的粒子位置快照 snapshots [0, 20, 40, 60, 80, 99] for i, it in enumerate(snapshots): if it len(history_positions): continue pos history_positions[it] # shape(50, n_dims) # 只画前两维代表UAV0和UAV1的首个任务倾向 axes[i].scatter(pos[:, 0], pos[:, 1], alpha0.6, s20) axes[i].set_title(fIteration {it}) axes[i].set_xlabel(Dim 0 (UAV0-Task0)) axes[i].set_ylabel(Dim 1 (UAV0-Task1)) plt.tight_layout() plt.savefig(filename, dpi300, bbox_inchestight) plt.close()诊断价值初始迭代It0粒子均匀散布 → 初始化正常中期迭代It40粒子向左下角聚集 → 说明算法发现低航程区域末期迭代It99粒子高度集中 → 收敛性良好若It99仍大面积分散 →w过大或max_iter不足。4.3tu_diagram.png任务分配关系图该图用网络图形式展示分配结果# plots.py 中关系图 def plot_assignment_graph(uav_pos, task_pos, assignment_matrix, filenametu_diagram.png): import networkx as nx G nx.DiGraph() # 添加节点 for i in range(len(uav_pos)): G.add_node(fUAV{i}, pos(uav_pos[i, 0], uav_pos[i, 1]), colorblue) for j in range(len(task_pos)): G.add_node(fTask{j}, pos(task_pos[j, 0], task_pos[j, 1]), colorred) # 添加边UAV - Task for uav_idx in range(len(uav_pos)): for task_idx in range(len(task_pos)): if assignment_matrix[uav_idx, task_idx] 1: G.add_edge(fUAV{uav_idx}, fTask{task_idx}) pos nx.get_node_attributes(G, pos) node_colors [G.nodes[n][color] for n in G.nodes()] plt.figure(figsize(12, 8)) nx.draw(G, pos, with_labelsTrue, node_colornode_colors, node_size800, font_size10, arrowsTrue, connectionstylearc3,rad0.1) plt.title(UAV-Task Assignment Graph) plt.savefig(filename, dpi300, bbox_inchestight) plt.close()业务解读若某UAV节点出度为0 →condition.py中“最小任务数”约束未触发需检查min_tasks_per_uav是否设为0若某Task节点入度1 → “无重复分配”约束失效立即定位condition.py第3条逻辑弧线弯曲程度反映任务地理分布密度直线越多说明任务点越集中。5. 避坑指南调试时踩过的5个真实血泪坑5.1 现象main.py运行报错ValueError: shapes (5,2) and (8,2) not aligned原因distance.py中uav_pos和task_pos维度传反或globalv.py中数组形状错误。解决检查globalv.UAV_POS.shape必须为(5, 2)globalv.TASK_POS.shape必须为(8, 2)在distance.py开头添加断言assert uav_pos.ndim 2 and uav_pos.shape[1] 2若从CSV读取用pandas.read_csv(...).values.astype(float)而非.to_numpy()后者可能降维。5.2 现象PSO迭代100次后gbest_fit仍为inf或极大值如1e8原因condition.py中约束惩罚未生效或fitness_function未正确调用check_feasibility。解决在fit_dis.py的fitness_function中print(Penalty:, penalty)确认惩罚值非零检查condition.check_feasibility返回的is_feasible是否恒为False临时注释掉penalty计算只返回total_distance观察是否收敛——若收敛则证明约束逻辑有缺陷。5.3 现象tu_fly.png中某架无人机无任何连线但assignment_matrix显示其分配了任务原因decode.py中top-k逻辑错误导致assignment矩阵元素为浮点数如0.999而非严格0/1。解决在decode_particle末尾强制二值化assignment (assignment 0.5).astype(int)在plot_flight_paths中添加校验assert np.all(np.isin(assignment_matrix, [0,1]))decode.py中top3_idx索引后需用assignment[uav_idx, top3_idx] 1而非1。5.4 现象修改globalv.py中坐标后tu_scatter.png粒子分布异常全挤在右上角原因pso.py中粒子初始化范围bounds(-5,5)与坐标尺度不匹配导致粒子初始位置远超任务空间。解决动态设置boundsbounds (min_coord-10, max_coord10)其中min_coord min(uav_pos.min(), task_pos.min())或在main.py中标准化坐标UAV_POS (UAV_POS - mean) / std运行后再反标准化绘图永远不要手动改bounds而不同步调整坐标系。5.5 现象多运行几次main.pytu_diagram.png分配结果完全不同且无明显优劣差异原因PSO随机种子未固定导致每次初始化不同而问题存在多个近优解。解决在main.py开头添加np.random.seed(42)在pso.py中粒子初始化前加np.random.seed(seed)seed可作为参数传入若需对比算法固定种子后运行5次取gbest_fit均值而非单次结果。6. 进阶技巧从“能跑”到“可解释”的三步验证法6.1 步骤一人工构造极小规模案例穷举验证PSO输出当面对8任务5无人机的复杂场景时先退回到2任务2无人机的极小案例手工穷举所有可行解UAV0分配UAV1分配总航程是否可行Task0Task1d00d11是Task1Task0d01d10是Task0Task1—d00d01否UAV1未分配在globalv.py中设UAV_POS np.array([[0,0], [10,0]]) TASK_POS np.array([[1,0], [9,0]])此时理论最优解为UAV0→Task0距离1、UAV1→Task1距离1总航程2。运行PSO若gbest_fit≈2.0且assignment_matrix匹配则证明核心逻辑正确。这一步是后悔药——所有后续调试都以此为黄金标准。6.2 步骤二注入可控噪声测试鲁棒性边界在distance.py中临时加入噪声观察PSO容忍度# 在calc_distance_matrix末尾添加调试用 if os.getenv(NOISE_TEST) 1: dist_mat np.random.normal(0, 0.1, dist_mat.shape) # 加±0.1米噪声然后命令行运行NOISE_TEST1 python main.py。若gbest_fit波动5%说明算法对测量误差鲁棒若波动20%需增强fitness_function中距离项的平滑性如加L2正则。6.3 步骤三导出中间粒子状态定位收敛瓶颈pso.py中记录每代最优适应度到CSV# 在PSO.optimize循环内添加 history_fitness [] for it in range(self.max_iter): # ...原有逻辑... history_fitness.append(gbest_fit) # 新增每20代保存粒子位置快照 if it % 20 0: np.save(fpso_snapshot_it{it}.npy, pos.copy()) # 运行后生成pso_snapshot_it0.npy, pso_snapshot_it20.npy...用以下脚本分析收敛速度# analyze_convergence.py import numpy as np import matplotlib.pyplot as plt fitness_history np.load(fitness_history.npy) # 从pso.py中保存 plt.plot(fitness_history) plt.xlabel(Iteration) plt.ylabel(Best Fitness) plt.title(Convergence Curve) plt.grid(True) plt.savefig(convergence_curve.png) # 计算收敛率最后20%迭代的fitness下降幅度 last_20pct fitness_history[-len(fitness_history)//5:] improvement (fitness_history[0] - last_20pct[-1]) / fitness_history[0] * 100 print(f收敛率: {improvement:.1f}%)关键阈值若improvement 5%→ 算法早熟需增大w或c2若曲线长期平台期如It30~70无变化 →max_iter不足或n_particles过小若曲线锯齿状震荡 →w过大应降至0.5~0.6。从那以后我每次改完condition.py或fit_dis.py都强制走一遍这三步先跑极小案例看黄金标准是否守住再加噪声看鲁棒性是否达标最后导出收敛曲线确认没有隐性bug。这比盲目调参快十倍也让我在答辩时能指着convergence_curve.png说“老师您看这里第42次迭代后梯度下降趋缓说明算法已找到稳定解”。希望帮到你。本文还有配套的精品资源点击获取