ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于合同网协议的多无人机任务分配:MATLAB实现与时间窗口约束优化

2026/8/8 5:33:06 拓冰建站 浏览量
基于合同网协议的多无人机任务分配:MATLAB实现与时间窗口约束优化 1. 项目概述当无人机集群遇上“拍卖会”最近几年无人机集群协同作业的场景越来越常见从农业植保、电力巡检到物流配送、应急搜救都能看到一群无人机有条不紊地工作的身影。但要让一群“空中机器人”高效合作而不是乱成一锅粥核心难题之一就是任务分配谁去哪个位置先做什么后做什么遇到突发情况怎么调整这背后是一套复杂的多智能体协同决策逻辑。我手头这个项目就是针对这类问题的一个经典且实用的解决方案基于合同网协议Contract Net Protocol, CNP实现多无人机任务分配与跟踪。这不仅仅是简单的“谁有空谁去”而是引入了时间窗口和优先级约束让分配更贴近现实需求。比如电力巡检中某个关键塔基的检测必须在下午2点前完成时间窗口而另一个疑似故障点需要优先处理高优先级。CNP算法就像一个高效的“内部拍卖会”让无人机们通过“投标”和“中标”来自主协商最终形成一个整体最优的任务执行方案。整个项目的实现和验证平台是MATLAB。选择MATLAB不是因为它只能做仿真而是因为它强大的矩阵计算、便捷的可视化以及丰富的算法工具箱能让我们快速搭建原型、验证逻辑并把复杂的协同过程直观地展示出来。这对于算法开发、教学演示乃至实际系统的前期验证都极具价值。接下来我就把这个项目的设计思路、实现细节、踩过的坑以及完整的代码逻辑给大家拆解清楚。2. 核心问题与算法选型为什么是合同网协议在深入代码之前我们必须先搞清楚要解决什么问题以及为什么CNP是合适的工具。多无人机任务分配不是一个新问题但其约束条件千变万化直接套用现成算法往往水土不服。2.1 问题定义带约束的协同任务分配我们面对的核心是一个多旅行商问题MTSP的变体。假设有M架无人机和N个任务点。每架无人机从各自的起始位置或共同的基地出发需要访问一系列任务点并最终返回或停在某处。但这不仅仅是路径最短那么简单我们加入了两个关键的现实约束时间窗口约束每个任务点都有一个允许被执行的时间范围[ET_i, LT_i]最早开始时间最晚开始时间。无人机到达时间早于ET_i需要等待晚于LT_i则任务失效或产生惩罚。这模拟了现实中的预约巡检、潮汐窗口作业等场景。优先级约束每个任务点有一个优先级权重P_i。高优先级的任务如故障报警点需要在调度中被优先考虑。这通常体现在目标函数中即优先完成高权重任务。我们的目标是为每架无人机规划一条任务序列在满足上述约束的前提下优化某个或多个指标例如总任务完成量最大、总飞行距离最短、总体任务完成时间makespan最小或者带权重的任务完成收益最高。2.2 算法选型集中式 vs. 分布式面对这个问题通常有两类思路集中式优化如混合整数线性规划MILP、遗传算法GA、粒子群算法PSO等。由一个“超级大脑”地面站收集所有信息计算全局最优解然后分发给所有无人机。优点是理论上能求得最优解但计算复杂度高通信负载大且缺乏鲁棒性——一旦中心节点或通信链路故障整个系统瘫痪。分布式协同如合同网协议CNP、共识算法、拍卖算法等。没有绝对的中心每个无人机都是一个自主的智能体通过局部通信和协商共同做出决策。优点是系统扩展性好、鲁棒性强更符合无人机集群的物理分布特性但通常得到的是次优解。为什么选择合同网协议CNPCNP是一种模仿市场经济中招标-投标-中标过程的分布式协商协议。它完美契合了无人机集群的特性角色清晰任务发布者Manager和任务执行者Contractor角色可以动态转换。通信高效只需交换“招标”、“投标”、“中标”三类消息通信量小。自主性强每架无人机基于自身状态位置、电量、负载独立计算投标值决定是否投标及投标多少体现了自主性。易于嵌入约束在计算投标值时可以很方便地融入时间窗口和优先级的计算使得最终的分配结果自然满足这些约束。动态响应当有新任务加入或无人机故障时可以很容易地启动新一轮招标实现动态重规划。因此对于需要快速响应、分布式部署、且带有复杂约束的无人机集群任务分配场景CNP是一个在效率、效果和实用性之间取得很好平衡的选择。我们的MATLAB实现就是要将这一套协商逻辑连同时间窗口和优先级检查完整地模拟出来。3. 合同网协议CNP的MATLAB实现框架理论讲完了我们进入实战环节。如何在MATLAB里构建一个CNP仿真环境关键在于对智能体无人机行为、消息传递和决策逻辑的建模。3.1 智能体与任务建模首先我们需要定义两个核心类在MATLAB中可以用结构体struct或更面向对象的classdef来实现这里为清晰起见用结构体说明。无人机智能体结构体% 示例初始化一架无人机 UAV(i).id i; % 唯一标识 UAV(i).position [x, y, z]; % 当前坐标 UAV(i).velocity v; % 巡航速度 UAV(i).battery 100; % 剩余电量 UAV(i).taskList []; % 已分配的任务序列存储任务ID UAV(i).schedule []; % 任务时间表对应每个任务的计划开始时间 UAV(i).status idle; % 状态idle, bidding, executing, returning UAV(i).role contractor; % 角色manager发布任务者, contractor任务结构体% 示例初始化一个任务 Task(j).id j; % 唯一标识 Task(j).position [x, y, z]; % 任务点坐标 Task(j).duration d; % 任务执行所需时间如悬停拍摄时间 Task(j).timeWindow [ET, LT]; % 时间窗口 [最早开始时间, 最晚开始时间] Task(j).priority p; % 优先级权重值越大越优先 Task(j).status unassigned; % 状态unassigned, announced, assigned, completed Task(j).managerID []; % 发布该任务招标的无人机ID Task(j).winnerID []; % 中标执行该任务的无人机ID Task(j).bidList []; % 存储所有投标信息 [投标者ID, 投标值]3.2 CNP协商流程的步骤化实现CNP的核心流程分为招标、投标、中标、确认四个阶段。我们在MATLAB中通过循环和条件判断来模拟这一过程。阶段一任务公告招标当一架无人机或一个虚拟的中心调度器拥有待分配的任务时它成为管理者Manager。function announceTask(managerUAV, task) % 1. 更新任务状态和信息 task.status announced; task.managerID managerUAV.id; task.bidList []; % 清空投标列表 % 2. 向通信范围内的所有无人机Contractors广播任务信息 % 这里简化处理假设所有无人机都在通信网内 broadcastMessage struct(type, announcement, ... taskID, task.id, ... taskPos, task.position, ... timeWindow, task.timeWindow, ... priority, task.priority); % 模拟广播将消息存入其他无人机的“收件箱” for i 1:numUAVs if i ~ managerUAV.id UAV(i).inbox{end1} broadcastMessage; end end end注意在实际仿真中需要模拟通信范围。只有距离管理者一定范围内的无人机才能收到公告这更符合真实情况。可以在广播循环内增加距离判断。阶段二投标每架作为承包商Contractor的无人机收到招标公告后需要独立决策是否投标并计算投标值。function bidValue evaluateTask(contractorUAV, taskAnnouncement) % 1. 检查自身是否具备投标资格状态是否空闲或轻闲电量是否充足 if ~strcmp(contractorUAV.status, idle) contractorUAV.battery 20 bidValue inf; % 用无穷大表示不投标或无效投标 return; end % 2. 计算将新任务插入当前任务序列后的成本增量投标值 % 这是CNP算法的核心决定了分配的质量 proposedSchedule contractorUAV.schedule; % 复制当前时间表 proposedTaskList contractorUAV.taskList; % 复制当前任务列表 % 尝试将新任务插入到当前序列的所有可能位置包括末尾 % 这里以“插入后总路径时间增长最小”为优化目标 minCostIncrease inf; bestInsertionIdx []; for k 0:length(proposedTaskList) % k0表示插入末尾 % 临时插入任务 tempList proposedTaskList; tempSchedule proposedSchedule; if k 0 tempList(end1) taskAnnouncement.taskID; else tempList [tempList(1:k-1), taskAnnouncement.taskID, tempList(k:end)]; end % 重新计算插入后的时间表并检查时间窗口约束 [feasible, newSchedule, totalTime] checkScheduleFeasibility(contractorUAV, tempList, TaskPool); if feasible costIncrease totalTime - contractorUAV.currentTotalTime; % 考虑优先级高优先级任务即使成本增加也多也应降低其投标值使其更容易中标 % 因此投标值 成本增量 / 任务优先级 bidValueCandidate costIncrease / taskAnnouncement.priority; if bidValueCandidate minCostIncrease minCostIncrease bidValueCandidate; bestInsertionIdx k; bestSchedule newSchedule; end end end if isinf(minCostIncrease) bidValue inf; % 没有可行的插入位置不投标 else bidValue minCostIncrease; % 可以在这里缓存最佳插入位置和计划中标后直接使用 contractorUAV.cachedInsertion{taskAnnouncement.taskID} struct(idx, bestInsertionIdx, schedule, bestSchedule); end end实操心得checkScheduleFeasibility函数是保证时间窗口约束的关键。它需要根据无人机速度、任务间距离、任务执行时长递归地计算每个任务点的到达时间、开始时间、结束时间并逐一比对是否在[ET, LT]内。如果某个任务无法满足则整个序列不可行。计算量会随着任务序列增长而增大需要优化算法。阶段三评标与中标管理者收集所有投标在一个预设的投标截止时间内选择投标值最小的承包商作为中标者。function [winnerID, winnerBid] evaluateBids(task) % 从任务的 bidList 中找出最优投标值最小的投标者 if isempty(task.bidList) winnerID -1; % 流标 winnerBid inf; return; end bids task.bidList; % 假设格式为 [UAVid, bidValue; ...] [minBid, minIdx] min(bids(:, 2)); winnerID bids(minIdx, 1); winnerBid minBid; % 向中标者发送中标消息向其他投标者发送失败消息 sendMessage(winnerID, struct(type, award, taskID, task.id)); for i 1:size(bids, 1) if bids(i,1) ~ winnerID sendMessage(bids(i,1), struct(type, reject, taskID, task.id)); end end end阶段四任务确认与更新中标者收到消息后正式将任务纳入自己的计划并更新自身状态和任务池状态。function acceptTask(contractorUAV, taskID) % 1. 从缓存中取出之前计算的最佳插入方案 insertionPlan contractorUAV.cachedInsertion{taskID}; % 2. 更新自己的任务列表和时间表 if insertionPlan.idx 0 contractorUAV.taskList(end1) taskID; else contractorUAV.taskList [contractorUAV.taskList(1:insertionPlan.idx-1), ... taskID, ... contractorUAV.taskList(insertionPlan.idx:end)]; end contractorUAV.schedule insertionPlan.schedule; % 3. 更新任务状态 TaskPool(taskID).status assigned; TaskPool(taskID).winnerID contractorUAV.id; % 4. 向管理者发送确认消息可选用于闭环 sendMessage(TaskPool(taskID).managerID, struct(type, confirm, taskID, taskID)); % 5. 清理缓存 contractorUAV.cachedInsertion(taskID) []; end至此一个完整的CNP协商周期就结束了。在实际仿真中这个过程会循环进行直到所有任务都被分配或者达到迭代上限。4. 时间窗口与优先级约束的深度融合策略仅仅实现CNP框架还不够时间窗口和优先级约束需要巧妙地融入到投标函数evaluateTask和调度检查函数checkScheduleFeasibility中这是项目成败的关键。4.1 时间窗口的严格校验与柔性处理在checkScheduleFeasibility函数中对时间窗口的处理逻辑如下function [feasible, schedule, totalTime] checkScheduleFeasibility(uav, taskSequence, taskPool) currentTime uav.currentTime; % 无人机当前时刻 currentPos uav.position; % 无人机当前位置 schedule zeros(length(taskSequence), 3); % 存储 [到达时间, 开始时间, 结束时间] for idx 1:length(taskSequence) taskID taskSequence(idx); task taskPool(taskID); % 计算飞行到该任务点所需时间 distance norm(currentPos - task.position); flightTime distance / uav.velocity; % 到达时间 arrivalTime currentTime flightTime; % 实际开始时间如果早于最早时间则等待晚于最晚时间则不可行 startTime max(arrivalTime, task.timeWindow(1)); if startTime task.timeWindow(2) feasible false; schedule []; totalTime inf; return; % 时间窗口冲突直接返回不可行 end % 结束时间 endTime startTime task.duration; % 记录到时间表 schedule(idx, :) [arrivalTime, startTime, endTime]; % 更新当前时间和位置用于计算下一个任务 currentTime endTime; currentPos task.position; end feasible true; totalTime currentTime - uav.currentTime; % 执行整个序列的总耗时 end注意事项这是严格的“硬”时间窗口处理。现实中可能存在“软”时间窗口即超时后产生惩罚而非绝对禁止。这时投标函数中的成本costIncrease就需要加入超时惩罚项例如penalty max(0, startTime - LT) * penaltyWeight。这会让算法在无法满足严格窗口时选择一个超时惩罚最小的方案。4.2 优先级的多维度影响策略优先级不能仅仅作为一个标签它应该从多个层面影响分配决策投标值计算如前所述在计算投标值时将成本增量除以优先级权重(costIncrease / priority)。这意味着对于高优先级任务即使插入它导致路径成本增加较多其“标准化”后的投标值也会变小从而在评标中更具竞争力更容易被分配出去。招标顺序任务发布的顺序本身就可以基于优先级。一个简单的策略是中心调度器或第一个发现任务的无人机按照优先级从高到低的顺序依次发布任务进行招标。这确保了高优先级任务优先进入分配流程抢占无人机资源。任务抢占机制高级对于极端高优先级的紧急任务可以引入抢占机制。即允许已分配的任务被更高优先级的任务打断或替换。在CNP中这可以通过管理者发布一个“紧急招标”并允许承包商在投标时考虑放弃或推迟已承接的低优先级任务来实现但逻辑会复杂很多需要谨慎设计违约成本。在我们的基础实现中采用第1点和第2点的结合已经能很好地体现优先级的作用。在初始化任务列表后先按优先级排序再依次进行CNP招标。5. 从任务分配到路径跟踪的闭环仿真任务分配完成后每架无人机都得到了一条有序的任务序列和对应的时间表。接下来的仿真是让无人机动起来去执行这些任务并可视化整个过程。这涉及到路径规划和简单的运动控制。5.1 基于分配结果的路径生成我们假设无人机在任务点之间以直线飞行二维或三维。根据分配结果UAV(i).taskList和UAV(i).schedule可以生成每架无人机的飞行路径点序列。% 生成第i架无人机的路径 uavPath{i} [UAV(i).startPosition]; % 起点 uavTime{i} [0]; % 时间点 for j 1:length(UAV(i).taskList) taskID UAV(i).taskList(j); taskPos TaskPool(taskID).position; planStartTime UAV(i).schedule(j, 2); % 计划开始时间 % 添加飞行段从上一点到任务点 uavPath{i} [uavPath{i}; taskPos]; % 计算到达时间计划开始时间减去任务执行时长不这里需要仔细计算 % 更准确的是记录每个路径点对应的“时间戳” % 假设schedule(j,1)是到达时间schedule(j,2)是开始时间schedule(j,3)是结束时间 uavTime{i} [uavTime{i}; UAV(i).schedule(j, [1,3])]; % 记录到达和离开该点的时间 end % 最后添加返回基地的路径如果规划了返航5.2 运动仿真与动态状态更新在一个主仿真循环中我们推进全局时钟并更新每架无人机的位置和状态。% 主仿真循环伪代码 maxTime max([UAV.schedule]); % 所有任务完成的最晚时间 dt 0.1; % 仿真时间步长 for currentSimTime 0:dt:maxTime for i 1:numUAVs % 1. 根据当前时间和该无人机的计划判断其状态 % 例如如果 currentSimTime 在某个任务的开始和结束时间之间状态为 executing % 如果 currentSimTime 刚过某个任务的结束时间则切换到前往下一个任务点的状态 traveling % 2. 根据状态更新位置 if strcmp(UAV(i).status, traveling) % 计算当前航线段从点A到点B % 根据速度、时间差线性插值计算当前位置 UAV(i).position updatePositionLinear(UAV(i).path, UAV(i).timeTable, currentSimTime); elseif strcmp(UAV(i).status, executing) % 位置保持在当前任务点 UAV(i).position TaskPool(currentTaskID).position; end % 3. 更新电量模型可选 UAV(i).battery UAV(i).battery - powerConsumptionRate * dt; end % 4. 实时绘图可视化所有无人机和任务点 plotUAVsAndTasks(UAV, TaskPool, currentSimTime); pause(0.01); % 控制动画速度 end5.3 MATLAB可视化技巧为了让仿真结果一目了然良好的可视化至关重要不同颜色区分用不同颜色表示无人机的状态空闲、旅行中、执行任务、任务状态未分配、已分配、已完成。轨迹线绘制每架无人机的历史飞行轨迹。时间窗口条在任务点旁用线段或色块表示其时间窗口无人机到达时间用竖线标记直观显示是否满足窗口。甘特图在另一个子图绘制任务分配的甘特图显示每架无人机的时间线哪些任务在何时执行非常利于分析调度结果。% 一个简单的二维轨迹动画框架示例 figure; hold on; grid on; xlabel(X (m)); ylabel(Y (m)); title(Multi-UAV Task Allocation and Tracking Simulation); % 绘制静态任务点 for j 1:numTasks plot(TaskPool(j).position(1), TaskPool(j).position(2), ks, MarkerSize, 10, LineWidth, 2); text(TaskPool(j).position(1)2, TaskPool(j).position(2)2, sprintf(T%d, j)); end % 初始化无人机位置绘图对象 for i 1:numUAVs hUAV(i) plot(UAV(i).position(1), UAV(i).position(2), bo, MarkerSize, 8, LineWidth, 2); hPath(i) plot(NaN, NaN, b-, LineWidth, 0.5); % 轨迹线 end % 在仿真循环中更新 for currentSimTime 0:dt:maxTime % ... 更新所有UAV.position ... for i 1:numUAVs % 更新无人机图标位置 set(hUAV(i), XData, UAV(i).position(1), YData, UAV(i).position(2)); % 更新轨迹线 oldX get(hPath(i), XData); oldY get(hPath(i), YData); set(hPath(i), XData, [oldX, UAV(i).position(1)], ... YData, [oldY, UAV(i).position(2)]); end drawnow; pause(0.05); end6. 代码实现中的常见问题与调试技巧在将上述理论转化为MATLAB代码的过程中我遇到了不少坑。这里把一些典型问题和解决方法记录下来希望能帮你节省时间。6.1 投标函数计算效率低下问题在evaluateTask函数中需要尝试将新任务插入当前任务序列的所有可能位置共n1种并对每种插入方案调用checkScheduleFeasibility进行时间窗口校验。当任务序列较长时计算量呈组合增长导致单次投标计算耗时过长影响仿真速度。解决方案剪枝优化在尝试插入前先进行快速可行性检查。例如计算无人机完成现有任务后最早可能到达新任务点的时间EAT以及最晚允许出发的时间LDT考虑后续任务。如果EAT LT最早到达时间都晚于最晚开始时间或LDT ET最晚出发时间都早于最早开始时间则该任务完全无法插入直接返回inf无需遍历所有位置。缓存机制无人机在投标时计算出的最佳插入位置和对应的时间表如果中标会被直接使用。但即使未中标这些计算也被丢弃了。可以考虑在无人机内部维护一个关于所有已公告任务的“成本表”缓存在一段时间内如果自身任务序列未变且同一任务被再次评估可能因流标重新招标可以直接读取缓存值。但要注意缓存失效的条件。简化时间窗口检查对于非严格时间窗口或窗口较宽的场景可以先用简单的启发式规则如最近插入法快速得到一个候选插入位置只对这个位置进行详细校验而不是遍历所有位置。但这可能牺牲解的质量。6.2 死锁与任务“饿死”问题在纯分布式CNP中可能出现多架无人机相互等待或者低优先级任务始终得不到分配的情况。场景A死锁任务T1需要技能A任务T2需要技能B。只有无人机U1具备技能A但U1正试图投标T2因为它离T2更近只有无人机U2具备技能B但U2正试图投标T1。两者都在等待对方放弃形成循环等待。场景B饿死一个地理位置偏远或优先级很低的任务所有无人机计算出的投标值都很高成本大每次招标都流标永远无法被分配。解决方案引入招标超时与重试机制为每个任务的招标阶段设置一个超时时间。如果超时后仍未收到任何有效投标则触发处理逻辑。例如管理者可以放宽任务要求如延长虚拟时间窗口或者直接指定一个距离最近的无人机“强制”执行转为集中式指令。设置任务最大流标次数为每个任务设置一个计数器。当流标次数超过阈值时自动提升其临时优先级或在系统层面将其标记为“需紧急处理”吸引无人机投标。在投标函数中引入随机扰动在计算出的投标值上增加一个小的随机数rand * epsilon。这可以打破对称性避免多架无人机因计算出的成本完全相同而陷入僵局也能让一些次优解有机会被选中增加探索性。6.3 仿真结果的可复现性问题由于算法中可能包含随机因素如初始化位置、上述的随机扰动每次运行仿真得到的结果可能不同不利于算法性能的稳定比较和调试。解决方案 在脚本开头固定随机数种子。rng(42); % 设置一个固定的种子例如42这能确保每次运行程序时生成的随机数序列完全相同从而使仿真过程完全可复现。在调试阶段尤其重要可以精准定位问题。6.4 可视化卡顿或混乱问题当无人机数量多、任务点多时实时绘图会造成仿真速度极慢或者图上的元素太多重叠看不清。解决方案降低绘图频率不要每个仿真步长都drawnow可以每隔10个或50个步长更新一次图形。if mod(stepCounter, 10) 0 drawnow; end简化绘图元素只绘制关键信息。例如轨迹线可以设为浅色细线只高亮显示当前活动的无人机。任务点只在状态改变时更新颜色。使用MATLAB的动画对象对于位置频繁更新的对象如无人机图标使用animatedline或更新图形对象的XData、YData属性比每次都重新plot要高效得多。分窗口显示将全局轨迹图和每架无人机的状态信息如甘特图分开在不同的图形窗口或子图中显示避免信息过载。7. 项目扩展与性能评估思路这个基础框架可以实现一个完整的、带约束的多无人机任务分配与跟踪仿真。但如果你想进一步深入这里有几个扩展方向7.1 引入更复杂的无人机模型能耗模型将电量消耗与飞行距离、负载、悬停时间关联在投标函数中考虑剩余电量是否足够执行新任务及返回基地。异构无人机无人机有不同的载荷能力如携带的传感器类型、飞行速度、续航时间。任务也可能有异构需求如需要红外相机巡检。这需要在招标公告和投标函数中增加能力和需求的匹配检查。动态障碍物在仿真环境中加入移动障碍物无人机需要实时局部避障。这要求路径跟踪层不能是简单的直线插值需要集成A*、D*或动态窗口法DWA等局部规划器。7.2 改进协商机制多任务同时招标管理者一次公告多个任务承包商可以对这些任务进行组合投标这能更好地探索任务间的协同效应但计算复杂度更高。迭代合同网进行多轮协商。第一轮分配后无人机可以交换或转包自己任务序列中的部分任务以寻求全局更优解。这类似于组合拍卖。融合其他分布式算法将CNP与一致性算法结合。无人机在局部协商CNP的同时也与邻居无人机交换负载信息平衡整体工作量避免部分无人机过载。7.3 如何进行定量性能评估要说明你的算法“好”需要有量化的指标。除了直观的动画建议在仿真中记录并对比以下数据任务完成率成功分配并满足时间窗口的任务数 / 总任务数。总飞行距离/总耗时所有无人机飞行距离之和或最后一个无人机完成任务的时间makespan。优先级满足度计算已完成任务的优先级权重之和并与所有任务的总优先级权重之比。这衡量了系统对高优先级任务的响应能力。计算时间完成整个分配过程所耗费的仿真时间或算法迭代次数衡量算法效率。通信开销模拟发送的招标、投标、中标消息的总数量。你可以设计不同规模的场景如无人机/任务数量比例变化、时间窗口松紧变化、优先级分布变化将你的CNP算法与一个简单的基准算法如最近邻分配进行对比用上述指标绘制图表从而客观地展示CNP在处理带约束的协同任务分配问题上的优势与不足。这个项目从理论到实践的链条很长但每一步拆解开来都是清晰可控的。最重要的是理解CNP“协商”的精髓以及如何将业务约束时间、优先级转化为算法中可以计算的“成本”。希望这份超详细的拆解能帮你顺利搭建起自己的无人机智能集群任务分配仿真系统。