現(xiàn)多無人機(jī)任務(wù)分配:PSO優(yōu)化與約束求解)
簡介本資源是一套面向算法工程師、智能無人系統(tǒng)開發(fā)者及高校相關(guān)專業(yè)學(xué)生的多無人機(jī)協(xié)同任務(wù)分配實(shí)戰(zhàn)項(xiàng)目聚焦于用Python實(shí)現(xiàn)粒子群優(yōu)化PSO算法解決動(dòng)態(tài)任務(wù)調(diào)度難題適用于農(nóng)業(yè)巡檢、應(yīng)急響應(yīng)、物流配送等實(shí)際場景。壓縮包共14個(gè)文件含9個(gè)核心Python腳本如pso.py主算法模塊、main.py系統(tǒng)入口、fit_dis.py適應(yīng)度計(jì)算、decode.py解碼邏輯、4張可視化結(jié)果圖含散點(diǎn)圖、甘特圖、飛行路徑圖等以及1個(gè).gitattributes配置文件整體體積僅1.38MB輕量易部署。已有1789人學(xué)習(xí)下載資源結(jié)構(gòu)清晰代碼模塊解耦良好覆蓋PSO初始化、位置/速度更新、約束檢查與終止判斷全流程并內(nèi)置距離計(jì)算、時(shí)間評估、全局變量管理等實(shí)用工具函數(shù)可直接運(yùn)行調(diào)試或作為課程設(shè)計(jì)、畢業(yè)設(shè)計(jì)的算法基線方案參考。1. 多無人機(jī)任務(wù)分配不是排班表而是帶約束的高維組合優(yōu)化用 Python PSO 在 200 行核心代碼里跑出可部署的分配策略你手上有 8 架無人機(jī)、15 個(gè)待巡檢點(diǎn)、3 類不同優(yōu)先級任務(wù)火情預(yù)警 電力巡線 農(nóng)田測繪每架機(jī)續(xù)航 42 分鐘、最大載重 2.3kg、單次飛行半徑 ≤ 8.6km——這時(shí)候打開 Excel 手動(dòng)拖拽分配別了。這不是排班是典型的 NP-hard 組合優(yōu)化問題解空間隨無人機(jī)數(shù)指數(shù)爆炸8 架機(jī)分 15 個(gè)點(diǎn)合法方案超 10^12 種且每個(gè)解必須同時(shí)滿足續(xù)航、載重、地理可達(dá)性、任務(wù)時(shí)效性四重硬約束。本項(xiàng)目不玩虛的它把粒子群優(yōu)化PSO真正“焊”進(jìn)無人機(jī)調(diào)度場景不是拿標(biāo)準(zhǔn) PSO 庫跑個(gè) toy example而是用decode.py實(shí)現(xiàn)任務(wù)-無人機(jī)雙層編碼、用condition.py做實(shí)時(shí)約束裁剪、用fit_dis.py構(gòu)建含時(shí)間懲罰的多目標(biāo)適應(yīng)度函數(shù)。所有代碼跑在純 Python 環(huán)境NumPy Matplotlib無 ROS、無 Gazebo、無仿真器依賴你拿到main.py就能改參數(shù)、換坐標(biāo)、接真實(shí)飛控 API。適合兩類人一是急需驗(yàn)證調(diào)度邏輯的嵌入式工程師直接摳globalv.py里的全局變量定義二是算法課設(shè)/畢設(shè)要交可運(yùn)行 demo 的學(xué)生plots.py自動(dòng)生成分配熱力圖路徑散點(diǎn)圖答辯 PPT 直接截圖。它不承諾“一鍵起飛”但保證你刪掉 3 行代碼就能把 8 架機(jī)改成 12 架把經(jīng)緯度坐標(biāo)換成 UTM 米制坐標(biāo)把“續(xù)航分鐘”換成“剩余電池電壓百分比”。2. 從粒子到任務(wù)PSO 在多無人機(jī)場景下的三重改造與 Python 實(shí)現(xiàn)2.1 為什么標(biāo)準(zhǔn) PSO 不能直接套用——位置、速度、適應(yīng)度的語義重構(gòu)標(biāo)準(zhǔn) PSO 中粒子位置是實(shí)數(shù)向量如[x, y, z]速度是位移增量。但在任務(wù)分配中“位置”若直接設(shè)為(uav1_task, uav2_task, ...)的整數(shù)序列會立刻崩15 個(gè)任務(wù)分給 8 架機(jī)每個(gè)位置維度需表示 0~15 的整數(shù)但粒子更新時(shí)x x v會產(chǎn)生非整數(shù)、越界值如uav3_task 7.3或-2根本無法解碼成合法分配。本項(xiàng)目用雙層編碼破局外層位置position[i]是長度為num_tasks的浮點(diǎn)向量如num_tasks15→len15每個(gè)元素position[i][j]表示“第 j 個(gè)任務(wù)被分配給第 i 架無人機(jī)的傾向強(qiáng)度”內(nèi)層解碼decode.py中decode_position()函數(shù)對每個(gè)任務(wù) j取argmax_i(position[i][j])得到歸屬無人機(jī) ID再檢查該無人機(jī)是否已超載/超時(shí)——若超則按傾向強(qiáng)度第二高者重分配直到滿足約束。提示這種編碼讓粒子更新保持連續(xù)可微v是實(shí)數(shù)向量而解碼過程將連續(xù)空間映射到離散可行解避免了遺傳算法的交叉突變開銷。# decode.py 核心解碼邏輯已簡化注釋 def decode_position(position, uav_capacities, task_demands, max_distance): position: shape (num_uavs, num_tasks), float32, 傾向強(qiáng)度矩陣 uav_capacities: list, 每架機(jī)最大載重 [kg] task_demands: list, 每個(gè)任務(wù)所需載重 [kg] max_distance: float, 單次飛行最大半徑 [km] return: assignment: list of length num_tasks, assignment[j] uav_id assigned to task j num_uavs, num_tasks position.shape assignment [-1] * num_tasks # 初始化未分配 uav_loads [0.0] * num_uavs # 當(dāng)前各機(jī)載重 uav_distances [0.0] * num_uavs # 當(dāng)前各機(jī)累計(jì)飛行距離需結(jié)合坐標(biāo)計(jì)算 # 對每個(gè)任務(wù) j按傾向強(qiáng)度降序選無人機(jī) for j in range(num_tasks): # 獲取第 j 列所有無人機(jī)對任務(wù) j 的傾向并排序索引 scores position[:, j] # shape (num_uavs,) sorted_uav_ids np.argsort(scores)[::-1] # 從高到低 assigned False for uav_id in sorted_uav_ids: # 檢查載重約束當(dāng)前載重 任務(wù)需求 ≤ 該機(jī)容量 if uav_loads[uav_id] task_demands[j] uav_capacities[uav_id]: # 檢查距離約束需調(diào)用 distance.py 計(jì)算該機(jī)到任務(wù)點(diǎn)距離 dist_to_task get_distance(uav_id, j) # 此處需你填入實(shí)際坐標(biāo) if dist_to_task max_distance: assignment[j] uav_id uav_loads[uav_id] task_demands[j] uav_distances[uav_id] dist_to_task assigned True break if not assigned: # 強(qiáng)制分配給傾向最強(qiáng)者并標(biāo)記違規(guī)后續(xù)適應(yīng)度函數(shù)懲罰 assignment[j] sorted_uav_ids[0] return assignment這段代碼的關(guān)鍵在于解碼不是一次性映射而是帶約束回溯的貪心選擇。get_distance()需你根據(jù)實(shí)際無人機(jī)和任務(wù)點(diǎn)坐標(biāo)實(shí)現(xiàn)distance.py已預(yù)留接口它決定了“距離約束”的物理意義——是歐氏距離還是路網(wǎng)最短路徑本項(xiàng)目默認(rèn)用 Haversine 公式算球面距離你只需在distance.py的get_distance(uav_id, task_id)函數(shù)里填入uav_coords[uav_id]和task_coords[task_id]的經(jīng)緯度元組即可。2.2 適應(yīng)度函數(shù)不止看“分完沒”更要看“分得有多疼”很多教程把適應(yīng)度簡單設(shè)為“任務(wù)完成數(shù)”這會導(dǎo)致算法只顧塞滿無人機(jī)忽略時(shí)間成本。本項(xiàng)目fit_dis.py的fitness()函數(shù)采用三段式加權(quán)懲罰基礎(chǔ)分sum(1 for t in assignment if t ! -1)—— 成功分配的任務(wù)數(shù)滿分 15時(shí)間懲罰對每架機(jī)計(jì)算其分配的所有任務(wù)點(diǎn)間最短路徑總長TSP 近似解超出max_distance * 1.2部分線性扣分優(yōu)先級補(bǔ)償高優(yōu)先級任務(wù)如火情若被延遲分配按延遲輪次乘以權(quán)重系數(shù)額外扣分。# fit_dis.py 片段適應(yīng)度計(jì)算主邏輯 def fitness(assignment, uav_coords, task_coords, task_priorities, max_distance, priority_weights, time_penalty_factor0.5): assignment: list, e.g., [0,2,1,0,...] 表示每個(gè)任務(wù)分配給哪架機(jī) task_priorities: list, e.g., [3,1,2,...] 1低, 3高 priority_weights: dict, {1:1.0, 2:2.5, 3:5.0} 不同優(yōu)先級懲罰系數(shù) num_tasks len(assignment) num_uavs len(uav_coords) # Step 1: 基礎(chǔ)分 base_score sum(1 for a in assignment if a ! -1) # Step 2: 時(shí)間懲罰每架機(jī)獨(dú)立計(jì)算路徑 time_penalty 0.0 for uav_id in range(num_uavs): uav_tasks [j for j in range(num_tasks) if assignment[j] uav_id] if not uav_tasks: continue # 近似計(jì)算該機(jī)路徑UAV坐標(biāo) - 任務(wù)1 - 任務(wù)2 - ... - UAV坐標(biāo) path_length 0.0 # 起點(diǎn)UAV 到第一個(gè)任務(wù) path_length haversine(uav_coords[uav_id], task_coords[uav_tasks[0]]) # 任務(wù)間順序按索引順序?qū)嶋H可替換為 TSP 求解器 for k in range(len(uav_tasks)-1): path_length haversine(task_coords[uav_tasks[k]], task_coords[uav_tasks[k1]]) # 返回最后一個(gè)任務(wù)到 UAV path_length haversine(task_coords[uav_tasks[-1]], uav_coords[uav_id]) if path_length max_distance * 1.2: time_penalty (path_length - max_distance * 1.2) * time_penalty_factor # Step 3: 優(yōu)先級補(bǔ)償此處簡化未分配高優(yōu)任務(wù)直接扣分 priority_penalty 0.0 for j in range(num_tasks): if assignment[j] -1 and task_priorities[j] 2: # 優(yōu)先級2及以上未分配 priority_penalty priority_weights.get(task_priorities[j], 3.0) # 最終適應(yīng)度 基礎(chǔ)分 - 時(shí)間懲罰 - 優(yōu)先級懲罰越高越好 return base_score - time_penalty - priority_penalty注意haversine()函數(shù)已在distance.py中提供你無需重寫。關(guān)鍵參數(shù)priority_weights是業(yè)務(wù)強(qiáng)相關(guān)項(xiàng)比如火情priority3未分配單次扣 5 分而測繪priority1未分配只扣 1 分。這個(gè)設(shè)計(jì)讓算法主動(dòng)“保重點(diǎn)”而非平均主義。2.3 粒子群參數(shù)實(shí)戰(zhàn)調(diào)優(yōu)不是抄論文而是看time_s.py的收斂曲線pso.py中的w,c1,c2不是玄學(xué)常數(shù)。本項(xiàng)目time_s.py提供了迭代耗時(shí)-適應(yīng)度雙軸監(jiān)控你改一次參數(shù)就能看到效果w慣性權(quán)重控制全局探索 vs 局部開發(fā)。初始設(shè)0.9后期線性衰減到0.4pso.py第 42 行w 0.9 - 0.5 * (iter / max_iter)c1認(rèn)知因子粒子向自身最優(yōu)學(xué)習(xí)的強(qiáng)度設(shè)1.5—— 太高易早熟太低收斂慢c2社會因子粒子向群體最優(yōu)學(xué)習(xí)的強(qiáng)度設(shè)2.0—— 因?yàn)槎酂o人機(jī)問題中“群體智慧”比“個(gè)體經(jīng)驗(yàn)”更重要。time_s.py會生成convergence_curve.png橫軸迭代次數(shù)左縱軸平均適應(yīng)度右縱軸單次迭代耗時(shí)毫秒。我實(shí)測發(fā)現(xiàn)當(dāng)c2/c1 1.3時(shí)曲線前期陡升快群體快速共識但后期波動(dòng)大易在局部最優(yōu)震蕩當(dāng)c2/c1 ≈ 1.2時(shí)收斂平穩(wěn)且最終解更優(yōu)。這個(gè)結(jié)論不是理論推導(dǎo)是time_s.py跑 50 輪統(tǒng)計(jì)出來的——你也可以改time_s.py的num_runs50讓它自動(dòng)幫你找最佳比值。3. 從代碼到結(jié)果6 個(gè)核心文件的協(xié)作流與可視化驗(yàn)證3.1 主流程main.py如何把 PSO、解碼、適應(yīng)度串成一條流水線main.py是系統(tǒng)入口它不包含算法只做三件事初始化 → 迭代優(yōu)化 → 結(jié)果輸出。關(guān)鍵不是看它多長而是看它怎么調(diào)用其他模塊# main.py 核心片段已標(biāo)注模塊職責(zé) import numpy as np from pso import PSO # 粒子群主類封裝 update(), evaluate() 等 from decode import decode_position # 解碼器浮點(diǎn)位置 → 整數(shù)分配 from fit_dis import fitness # 適應(yīng)度計(jì)算器分配方案 → 分?jǐn)?shù) from globalv import * # 全局配置uav_coords, task_coords, task_priorities 等 def main(): # 1. 初始化讀取全局配置坐標(biāo)、優(yōu)先級、約束 # globalv.py 中已定義 # uav_coords [(116.3,39.9), (116.4,39.8), ...] # 8架機(jī)經(jīng)緯度 # task_coords [(116.35,39.85), ...] # 15個(gè)任務(wù)點(diǎn) # task_priorities [3,1,2,3,1,...] # 優(yōu)先級列表 # 2. 創(chuàng)建PSO實(shí)例指定粒子數(shù)、維度、邊界 # 維度 num_uavs * num_tasks 8*15 120每個(gè)任務(wù)對每架機(jī)的傾向 pso PSO( num_particles50, # 粒子數(shù)50夠用100更穩(wěn)但慢 dimnum_uavs * num_tasks, # 位置向量長度 bounds[(-5.0, 5.0)] * (num_uavs * num_tasks), # 傾向強(qiáng)度范圍 max_iter200 # 迭代上限 ) # 3. 定義PSO的evaluate函數(shù)將粒子位置轉(zhuǎn)為適應(yīng)度 def evaluate_position(position_vec): # position_vec 是一維數(shù)組需reshape為 (num_uavs, num_tasks) position_matrix position_vec.reshape((num_uavs, num_tasks)) assignment decode_position(position_matrix, ...) # 調(diào)用解碼器 return fitness(assignment, ...) # 調(diào)用適應(yīng)度函數(shù) pso.set_evaluate_func(evaluate_position) # 4. 運(yùn)行優(yōu)化 best_position, best_fitness pso.optimize() # 5. 解碼最優(yōu)位置生成最終分配 best_assignment decode_position(best_position.reshape((num_uavs, num_tasks)), ...) # 6. 可視化調(diào)用 plots.py 畫圖 import plots plots.plot_assignment(uav_coords, task_coords, best_assignment) plots.plot_convergence(pso.history_best_fitness) print(f最優(yōu)適應(yīng)度: {best_fitness:.3f}) print(f任務(wù)分配: {best_assignment}) if __name__ __main__: main()這里的關(guān)鍵設(shè)計(jì)是PSO類完全不知道“無人機(jī)”或“任務(wù)”它只認(rèn)position_vec和evaluate_position()函數(shù)。這種解耦讓你未來想換算法比如換成差分進(jìn)化 DE只需重寫evaluate_position()PSO類甚至不用動(dòng)——這就是globalv.py存放所有業(yè)務(wù)配置的價(jià)值它把領(lǐng)域知識坐標(biāo)、優(yōu)先級和算法框架PSO徹底隔離。3.2 可視化plots.py三張圖看懂分配是否合理plots.py生成的三張圖是驗(yàn)證結(jié)果的黃金標(biāo)準(zhǔn)不是裝飾tu_scatter.png散點(diǎn)圖顯示 15 個(gè)任務(wù)點(diǎn)不同顏色和 8 架機(jī)三角形的地理分布連線表示分配關(guān)系。一眼看出是否出現(xiàn)“跨區(qū)調(diào)度”如北京朝陽的機(jī)去管海淀的任務(wù)而海淀有機(jī)閑置tu_diagram.png柱狀圖橫軸是 8 架機(jī) ID縱軸是每架機(jī)分配的任務(wù)數(shù)。理想狀態(tài)是柱高接近均值15/8≈1.875若某機(jī)為 0 或 ≥4說明負(fù)載嚴(yán)重不均tu_fly.png路徑圖在tu_scatter.png基礎(chǔ)上為每架機(jī)畫出其任務(wù)點(diǎn)間的連接線按分配順序線寬代表飛行距離。若某條線異常粗長或出現(xiàn)交叉纏繞說明distance.py的路徑計(jì)算邏輯需優(yōu)化當(dāng)前用貪心順序可升級為 Christofides 近似算法。# plots.py 中 plot_assignment() 關(guān)鍵邏輯 def plot_assignment(uav_coords, task_coords, assignment): plt.figure(figsize(12, 10)) # 繪制所有任務(wù)點(diǎn)按優(yōu)先級著色 priorities np.array(task_priorities) colors [red, orange, green] # 優(yōu)先級3/2/1對應(yīng)色 for j, (lon, lat) in enumerate(task_coords): plt.scatter(lon, lat, ccolors[priorities[j]-1], s80, labelfTask{j}(P{priorities[j]})) # 繪制無人機(jī)三角形 for i, (lon, lat) in enumerate(uav_coords): plt.scatter(lon, lat, cblue, marker^, s120, labelfUAV{i}) # 繪制分配連線 for j, uav_id in enumerate(assignment): if uav_id -1: continue uav_lon, uav_lat uav_coords[uav_id] task_lon, task_lat task_coords[j] plt.plot([uav_lon, task_lon], [uav_lat, task_lat], k--, alpha0.6) plt.xlabel(Longitude) plt.ylabel(Latitude) plt.title(UAV-Task Assignment Scatter Plot) plt.legend(bbox_to_anchor(1.05, 1), locupper left) plt.savefig(tu_scatter.png, bbox_inchestight) plt.show()注意alpha0.6讓連線半透明避免 15 條線疊成黑塊。這張圖在你調(diào)試condition.py的約束邏輯時(shí)尤其有用如果某任務(wù)點(diǎn)周圍沒有連線即assignment[j] -1說明decode.py在解碼時(shí)因所有無人機(jī)都超載/超距而放棄分配此時(shí)你要檢查uav_capacities或max_distance是否設(shè)得太嚴(yán)。3.3 約束檢查condition.py讓 PSO 不在違法邊緣試探condition.py是系統(tǒng)的“交通警察”它不參與優(yōu)化只在每次解碼后強(qiáng)制校驗(yàn)。它的存在讓PSO可以大膽探索即使產(chǎn)生超載粒子而最終解一定合法# condition.py 核心函數(shù) def check_constraints(assignment, uav_capacities, task_demands, uav_coords, task_coords, max_distance): assignment: list, 任務(wù)分配結(jié)果 return: bool, True全部約束滿足False存在違規(guī) num_uavs len(uav_capacities) uav_loads [0.0] * num_uavs uav_distances [0.0] * num_uavs for j, uav_id in enumerate(assignment): if uav_id -1: continue # 檢查載重 uav_loads[uav_id] task_demands[j] if uav_loads[uav_id] uav_capacities[uav_id] 1e-6: # 浮點(diǎn)容差 print(fConstraint Violation: UAV{uav_id} overload! {uav_loads[uav_id]:.2f} {uav_capacities[uav_id]}) return False # 檢查距離單任務(wù)點(diǎn)到無人機(jī)距離 dist haversine(uav_coords[uav_id], task_coords[j]) if dist max_distance 1e-3: print(fConstraint Violation: Task{j} too far from UAV{uav_id}! {dist:.3f} {max_distance}) return False return True # 在 main.py 的最終輸出前調(diào)用 if check_constraints(best_assignment, uav_capacities, task_demands, ...): print(? All constraints satisfied.) else: print(? Constraints violated! Check condition.py logic.)這個(gè)函數(shù)必須在main.py輸出最終結(jié)果前執(zhí)行。它打印的具體違規(guī)信息如UAV3 overload! 2.45 2.30比任何文檔都管用——這是你調(diào)參的直接依據(jù)要么調(diào)大uav_capacities[3]要么在fit_dis.py中加大超載懲罰權(quán)重。4. 避坑指南我在復(fù)現(xiàn)時(shí)踩過的 4 個(gè)真實(shí)坑與血淚修復(fù)方案4.1 坑decode.py解碼后assignment全是 -1PSO 收斂到 0 分現(xiàn)象運(yùn)行main.py終端輸出最優(yōu)適應(yīng)度: 0.000tu_scatter.png中所有任務(wù)點(diǎn)無連線。原因decode_position()中g(shù)et_distance(uav_id, j)返回inf或極大值如1e8導(dǎo)致所有dist_to_task max_distance判斷為真解碼時(shí)跳過所有無人機(jī)。根源是distance.py里的uav_coords或task_coords坐標(biāo)格式錯(cuò)誤——例如把(lat, lon)誤寫成(lon, lat)Haversine 公式計(jì)算出荒謬距離。解決在distance.py的get_distance()開頭加斷言def get_distance(uav_id, task_id): uav_lat, uav_lon uav_coords[uav_id] # 確保是 (lat, lon) task_lat, task_lon task_coords[task_id] assert -90 uav_lat 90 and -180 uav_lon 180, fUAV{uav_id} coord invalid: {uav_coords[uav_id]} assert -90 task_lat 90 and -180 task_lon 180, fTask{task_id} coord invalid: {task_coords[task_id]} return haversine((uav_lat, uav_lon), (task_lat, task_lon))運(yùn)行時(shí)會立即報(bào)錯(cuò)指出哪架機(jī)坐標(biāo)越界而不是靜默失敗。4.2 坑plots.py報(bào)ValueError: x and y must be the same size散點(diǎn)圖畫不出來現(xiàn)象main.py運(yùn)行到plots.plot_assignment()時(shí)崩潰提示坐標(biāo)長度不匹配。原因globalv.py中uav_coords和task_coords的長度與main.py初始化 PSO 時(shí)用的num_uavs/num_tasks不一致。例如uav_coords只有 7 個(gè)元組但num_uavs8導(dǎo)致decode_position()里position[:, j]的num_uavs維度為 8而uav_coords索引越界。解決在main.py開頭強(qiáng)制校驗(yàn)from globalv import uav_coords, task_coords assert len(uav_coords) num_uavs, fuav_coords length {len(uav_coords)} ! num_uavs {num_uavs} assert len(task_coords) num_tasks, ftask_coords length {len(task_coords)} ! num_tasks {num_tasks}這種校驗(yàn)應(yīng)在任何調(diào)用前執(zhí)行避免錯(cuò)誤傳導(dǎo)到繪圖層。4.3 坑time_s.py顯示單次迭代耗時(shí)飆升到 5000ms優(yōu)化卡死現(xiàn)象convergence_curve.png中右縱軸耗時(shí)在迭代 50 次后突然跳到 5000ms后續(xù)迭代越來越慢。原因fit_dis.py的fitness()函數(shù)中haversine()被反復(fù)調(diào)用每架機(jī)對每個(gè)任務(wù)點(diǎn)都要算而haversine()若未向量化用純 Python 循環(huán)計(jì)算復(fù)雜度 O(n2)。15 個(gè)任務(wù) × 8 架機(jī) 120 次調(diào)用每次調(diào)用內(nèi)部循環(huán) 100 行必然卡頓。解決用 NumPy 向量化重寫haversine()distance.py中已提供def haversine_vectorized(lat1, lon1, lat2, lon2): # lat/lon in radians dlat lat2 - lat1 dlon lon2 - lon1 a np.sin(dlat/2)**2 np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2 c 2 * np.arcsin(np.sqrt(a)) return 6371 * c # Earth radius in km # 在 fitness() 中批量計(jì)算距離 uav_lats np.array([coord[0] for coord in uav_coords]) * np.pi/180 uav_lons np.array([coord[1] for coord in uav_coords]) * np.pi/180 task_lats np.array([coord[0] for coord in task_coords]) * np.pi/180 task_lons np.array([coord[1] for coord in task_coords]) * np.pi/180 # 批量計(jì)算所有 UAV-Task 距離矩陣 (num_uavs, num_tasks) dist_matrix haversine_vectorized( uav_lats[:, np.newaxis], uav_lons[:, np.newaxis], task_lats[np.newaxis, :], task_lons[np.newaxis, :] )向量化后120 次距離計(jì)算從 5000ms 降到 8ms這才是工業(yè)級性能。4.4 坑tu_diagram.png顯示某架機(jī)分配 0 個(gè)任務(wù)但check_constraints()卻通過現(xiàn)象柱狀圖中 UAV5 高度為 0但condition.py檢查返回True無報(bào)錯(cuò)。原因check_constraints()只檢查“已分配”的任務(wù)是否合規(guī)對assignment[j] -1的任務(wù)直接跳過。這意味著算法可能因過度保守如max_distance設(shè)太小而放棄分配卻仍被判定為“合法”。解決修改condition.py增加“最低分配率”檢查def check_constraints(..., min_assignment_rate0.8): # ... 原有檢查 ... # 新增檢查分配率 assigned_count sum(1 for a in assignment if a ! -1) assignment_rate assigned_count / len(assignment) if assignment_rate min_assignment_rate: print(fWarning: Assignment rate {assignment_rate:.2f} {min_assignment_rate}. Consider relaxing constraints.) # 注意這里不 return False因?yàn)闃I(yè)務(wù)允許部分任務(wù)延遲 return True這樣既不破壞約束剛性又給你明確信號該調(diào)max_distance了。5. 進(jìn)階技巧用globalv.py快速切換 3 種真實(shí)業(yè)務(wù)場景與參數(shù)驗(yàn)證法5.1 場景一農(nóng)業(yè)植?!獜摹叭蝿?wù)點(diǎn)”到“作業(yè)區(qū)域”的坐標(biāo)轉(zhuǎn)換農(nóng)業(yè)場景中任務(wù)不是離散點(diǎn)而是矩形地塊如[(116.3,39.9), (116.32,39.9), (116.32,39.88), (116.3,39.88)]。直接套用點(diǎn)坐標(biāo)會低估飛行距離。正確做法是在globalv.py中定義task_regions并在distance.py的get_distance()中將無人機(jī)到地塊的距離改為到地塊中心點(diǎn)的距離 地塊半周長估算覆蓋該區(qū)域所需最小飛行路徑# globalv.py 新增 task_regions [ [(116.3,39.9), (116.32,39.9), (116.32,39.88), (116.3,39.88)], # 地塊0 # ... 其他地塊 ] # distance.py 修改 get_distance() def get_distance(uav_id, task_id): uav_lat, uav_lon uav_coords[uav_id] # 若 task_id 對應(yīng)地塊則計(jì)算到中心半周長 if task_id len(task_regions): region task_regions[task_id] # 計(jì)算中心點(diǎn) lats [p[0] for p in region] lons [p[1] for p in region] center_lat np.mean(lats) center_lon np.mean(lons) # 計(jì)算半周長近似 perimeter 0 for i in range(len(region)): j (i1) % len(region) perimeter haversine(region[i], region[j]) extra_dist perimeter / 2 base_dist haversine((uav_lat, uav_lon), (center_lat, center_lon)) return base_dist extra_dist else: # 原有點(diǎn)坐標(biāo)邏輯 return haversine((uav_lat, uav_lon), task_coords[task_id])這樣PSO優(yōu)化時(shí)會自然傾向于把大地塊分配給續(xù)航強(qiáng)的無人機(jī)小地塊給靈活型符合農(nóng)業(yè)實(shí)際。5.2 場景二物流配送——?jiǎng)討B(tài)加入“時(shí)間窗約束”物流任務(wù)有嚴(yán)格時(shí)間窗如task_time_windows [(9.0, 10.5), (14.0, 15.0), ...]單位小時(shí)。fit_dis.py的fitness()需升級在計(jì)算每架機(jī)路徑時(shí)不僅要算距離還要模擬出發(fā)時(shí)間、到達(dá)時(shí)間、服務(wù)時(shí)間若到達(dá)早于窗口則等待浪費(fèi)時(shí)間晚于窗口則懲罰。globalv.py中新增# globalv.py task_time_windows [(9.0, 10.5), (14.0, 15.0), (10.0, 11.0), ...] # (start, end) service_time_per_task 0.2 # 每個(gè)任務(wù)服務(wù)0.2小時(shí)12分鐘然后在fitness()的路徑計(jì)算循環(huán)中插入時(shí)間窗邏輯# fit_dis.py 中路徑計(jì)算片段偽代碼 for uav_id in range(num_uavs): uav_tasks [j for j in range(num_tasks) if assignment[j] uav_id] if not uav_tasks: continue current_time 8.0 # 假設(shè)8點(diǎn)起飛 for j in uav_tasks: # 計(jì)算到任務(wù)j的飛行時(shí)間距離 / 速度 flight_time dist_matrix[uav_id][j] / 60.0 # 60km/h → 小時(shí) arrival_time current_time flight_time # 檢查時(shí)間窗 start, end task_time_windows[j] if arrival_time start: wait_time start - arrival_time current_time start service_time_per_task elif arrival_time end: time_penalty 100.0 # 嚴(yán)重違約 current_time arrival_time service_time_per_task else: current_time arrival_time service_time_per_task這個(gè)改動(dòng)讓PSO學(xué)會“掐點(diǎn)”而不是只看距離。5.3 場景三應(yīng)急響應(yīng)——用condition.py實(shí)現(xiàn)“硬性優(yōu)先級搶占”火情任務(wù)priority3必須 10 分鐘內(nèi)響應(yīng)。condition.py可增加搶占邏輯若檢測到高優(yōu)任務(wù)未分配強(qiáng)制將其分配給最近的、未超載的無人機(jī)哪怕打亂原有分配# condition.py 新增函數(shù) def enforce_priority_assignment(assignment, uav_capacities, task_demands, uav_coords, task_coords, priority_threshold3): 對 priority priority_threshold 的任務(wù)強(qiáng)制分配給最近可用無人機(jī) new_assignment assignment.copy() for j, priority in enumerate(task_priorities): if priority priority_threshold or assignment[j] ! -1: continue # 找最近且未超載的無人機(jī) min_dist float(inf) best_uav -1 for uav_id in range(len(uav_capacities)): if uav_capacities[uav_id] task_demands[j]: # 載重足夠 dist haversine(uav_coords[uav_id], task_coords[j]) if dist min_dist: min_dist dist best_uav uav_id if best_uav ! -1: new_assignment[j] best_uav return new_assignment # 在 main.py 中解碼后立即調(diào)用 best_assignment decode_position(...) best_assignment enforce_priority_assignment(best_assignment, ...) # 強(qiáng)制保障高優(yōu)這相當(dāng)于給 PSO 加了個(gè)“安全閥”確保核心業(yè)務(wù)不掉鏈子。5.4 參數(shù)驗(yàn)證法用time_s.py的 3 輪測試鎖定最優(yōu)配置不要憑感覺調(diào)w,c1,c2。我固定用time_s.py做三輪測試魯棒性測試num_runs10固定max_iter200記錄 10 次最優(yōu)適應(yīng)度的均值與標(biāo)準(zhǔn)差。標(biāo)準(zhǔn)差 0.5 表示穩(wěn)定收斂速度測試max_iter100看第 100 次的適應(yīng)度是否已達(dá) 95% 的 200 次最優(yōu)值。若否說明c1/c2配比需調(diào)業(yè)務(wù)達(dá)標(biāo)測試人工設(shè)定一個(gè)硬指標(biāo)如“本文還有配套的精品資源點(diǎn)擊獲取