☰
多无人机集群仿真:博弈论自适应部署与CVACA固定路径策略
2026/10/17 4:04:42 网站建设 项目流程

做多无人机集群仿真这几年,我最深的感受是:部署和运动两个词看起来简单,合在一起却很容易打架。部署要解决“无人机应该去哪些位置”,运动要解决“怎么安全到达那里”,而把博弈论自适应策略和CVACA固定路径策略放在同一套框架里协同工作,则是这篇内容的主题。多无人机部署不是单纯画几个目标点,也不仅仅是让算法跑出一个漂亮动画,它需要把任务分配、路径生成、碰撞避免、运动控制串成一条完整链路。这篇文章我会把整套仿真思路、Matlab代码框架和调参经验都摊开讲,适合正在做集群算法验证、毕业设计或者工程预研的开发者,刚入门的人也能按着路径把代码跑起来。

1. 需求拆解与整体架构

1.1 多无人机部署到底在解决什么问题

多无人机部署本质上是给一组无人机分配期望位置或任务区域,在满足覆盖、连通、能耗等约束下让整个集群效能最大化。比如区域搜索、通信中继、物资投放,都需要在一开始或者任务动态变化时确定每架无人机应该待在哪里。传统做法可以用集中式优化,把全部位置信息收集到中心节点,统一求解一个大规模优化问题。但集群规模一大,集中式求解很慢,一旦中心节点故障,整个系统就瘫痪。

所以我在项目中选择了博弈论的自适应策略。每架无人机只跟邻居交互,通过局部效用函数迭代,最终收敛到一个系统可接受的部署状态。这种思路和现实中多智能体分布式决策很像,不需要中心节点,扩展性也好。它的核心优势不是单次计算多么精确,而是让集群在面对动态环境时能自发地重新分布。无人机掉线、新增目标、覆盖区域变化,这些事件都不需要重新启动全局求解,只需要让相关区域附近的无人机重新博弈几轮。

当然,分布式博弈也有代价,它不能保证严格全局最优,只能保证收敛到均衡。但对于大多数工程任务来说,局部最优且稳健的部署结果,已经比中心式全局最优但脆弱很多的实际体验要好得多。后面我会详细讲效用函数怎么设计,才知道这个均衡结果到底意味着什么。

1.2 为什么是“博弈论 + CVACA固定路径”组合

只有部署决策还不够,部署完之后无人机还要从当前位置移动到目标位置。这里就引出CVACA固定路径策略。我对它的定义是:CVACA(Cooperative Velocity Allocation and Collision Avoidance),协同速度分配与避碰,指的是先根据部署结果生成固定参考路径,然后无人机在跟随路径的过程中,用避碰算法实时修正速度,保证不会相撞。组合起来看,博弈论负责“做什么决策”,CVACA负责“怎么走安全”,一个偏任务层,一个偏运动层,分工非常明确。

这样分层的另一个好处是模块解耦。任务层换个算法不影响运动层,运动层换个避碰策略也不会推翻部署逻辑。实际调试时,我经常先单独把CVACA调好,再把它接到博弈论输出上,这样出了问题能很快定位是哪一层的问题。如果一上来就把所有逻辑混在一个脚本里,无人机乱飞的时候你根本分不清是目标点给错了,还是路径跟踪、避碰逻辑有bug。

1.3 系统运行流程

整体仿真流程可以分成四步。第一步初始化环境,包括地图大小、障碍物、无人机数量、通信半径、初始位置。第二步运行博弈论部署算法,每架无人机根据当前位置和邻居信息计算效用,更新目标点。第三步根据目标点生成固定路径,无人机用CVACA策略跟踪路径并避碰。第四步收集数据,统计覆盖率、能量消耗、冲突次数、收敛时间等指标。

这四个步骤并不是严格顺序执行,博弈层通常会周期性运行。比如任务区域变化、无人机掉线、新增任务,都会触发新一轮博弈更新,运动层在每次仿真时间步都要执行。这个“周期性决策 + 连续运动控制”的结构,就是整套仿真的骨架。我之前见过一些项目把博弈更新写在每一次运动控制里,结果目标点频繁变化,路径还没走完就重新规划,最后整个集群像无头苍蝇一样乱转。合理的做法是让博弈层以较粗的时间粒度运行,运动层以较细的时间粒度执行,两层之间隔着一条“生成固定路径”的中间环节。

2. 博弈论自适应策略设计与建模

2.1 把部署问题建模成势博弈

多无人机部署涉及多个决策者,每个决策者都会影响整体结果,天然适合用博弈论建模。我采用的是势博弈框架:存在一个全局势函数,每个无人机单方面改变自己的策略时,自身效用函数的变化量和全局势函数的变化量方向一致。这个性质保证只要所有无人机都追求自身效用最大化,最终会收敛到一个纯策略纳什均衡,而且这个均衡往往对应着全局势函数的局部极小值。

具体建模时,每架无人机是一个博弈参与者,策略是它的期望位置或目标区域编号。效用函数不能只看自己,要把对集群的影响也考虑进去。我的做法是把覆盖收益、通信代价、碰撞风险、能耗代价四部分加权组合。每个无人机在迭代时,会比较移动到不同候选位置的效用值,选择效用最高的策略,重复多次后整个系统进入均衡。

理解势博弈的关键点在于“单方面调整收益”。也就是说,当某一架无人机想换到另一个位置时,它只需要考虑自己切换后对全局势函数的影响方向是否和自己的效用变化一致。只要满足这个条件,分布式迭代就不会出现循环震荡。这比一般博弈要稳定得多,也是我选择它的主要原因。

2.2 效用函数设计:覆盖、通信、能耗都要折算

效用函数的选取直接决定部署效果。我常用的形式是:

U_i(s_i, s_{-i}) = w_c * C_i - w_l * L_i - w_e * E_i - w_a * A_i

其中C_i是覆盖收益,表示无人机在当前位置对任务区域的信息贡献,一般是距离任务中心的递减函数;L_i是通信代价,表示为保持与邻居通信所需功率折算;E_i是能耗代价,与飞行距离和悬停时间有关;A_i是避碰代价,防止无人机离得太近。权重w_c等根据任务需求调。

举个例子,在区域搜索场景里,我希望无人机覆盖不同子区域而非扎堆,于是把C_i设计成与“未覆盖面积”有关,哪块区域覆盖稀疏,无人机往那边移动的收益就大。在通信中继场景里,我会提高w_l的权重,让无人机保持合适的间距以维持链路。注意这里的效用函数不一定需要闭式表达式,也可以写成基于采样点统计的数值函数。只要候选位置能算出相对高低,博弈就能跑起来。

自适应体现在权重会随环境变化。比如任务区域内突然出现高价值目标,我可以在线增加w_c,引导无人机向目标区域集中;如果发现集群通信质量下降,就调大w_l。我还实验过让无人机根据邻居数量动态调整权重:邻居很多时增加避碰与通信代价,邻居较少时增加覆盖收益,这样能避免过度拥挤。这个自适应机制我放在整个博弈迭代的外层,每隔几个迭代周期更新一次权重,相当于在博弈模型里加入了一个慢变量。

2.3 纳什均衡求解与自适应迭代机制

严格求解纳什均衡在大规模博弈里很难,但势博弈有一个好处:可以使用更简单的分布式迭代。我这里用的是“异步部分最好响应”思路。每个仿真周期,无人机不要求同步更新全部决策,而是随机选择一部分无人机进行策略更新,选中的无人机会评估若干候选位置,选择使自身效用最大的位置。这种机制模拟了真实集群中通信和计算资源的有限性,也不容易因为所有无人机同时切换导致震荡。

具体迭代逻辑可以参考这段Matlab风格的伪代码:

for iter = 1:maxIter updateIdx = randperm(N, K); for i = updateIdx candidateTargets = generateCandidates(drones(i).pos, targetCatalog); for c = 1:size(candidateTargets, 1) U(c) = computeUtility(candidateTargets(c, :), neighborsInfo, params); end [~, best] = max(U); drones(i).target = candidateTargets(best, :); end globalPotential = computePotential(drones, params); if abs(globalPotential - lastPotential) < eps break; end end

这里的K是每次迭代的更新数量,通常取无人机总数N的20%~40%。更新过少收敛慢,更新过多容易产生局部振荡。我后面会在参数调优里给出经验值。另外一个细节是候选点的生成范围,不能离当前目标太远,否则每次迭代都像重新规划任务,集群运动会被打乱;也不能离太近,否则效用变化太小,迭代半天没有进展。我一般以当前目标为中心,在半径约为通信半径三分之一的圆内做少量采样。

3. CVACA固定路径策略与运动仿真实现

3.1 固定路径从哪来:航路点生成与平滑

部署层给出的是目标位置,CVACA的第一步要把这些位置变成可跟踪的航路点序列。我的做法是:先用基于采样的方法在地图上生成关键航路点,再用贝塞尔曲线处理平滑,避免出现无人机无法急转弯的路径。固定路径里的“固定”,指的是航路点和曲线参数在每次部署决策更新之间保持不变,无人机只需要按路径参数运动。

航路点生成不能只考虑距离最短。真实飞行中还要避开障碍物、避免大角度转向。所以我会把航路点分为两类:任务航路点和避障航路点。任务航路点来自部署层目标位置,避障航路点由地图信息生成。如果目标是动态变化的,只需要重新生成任务点后面的路径段,前面的路径不用推倒重来。这个设计在仿真里看起来省不了几步,但对代码组织和计算量很有帮助。

贝塞尔曲线平滑这块,经验是控制点数量不要太多,否则曲线会出现不必要的波动。我通常用三阶贝塞尔曲线,起点和终点方向由进出航路段的航向决定,中间两个控制点取在沿航向方向约三分之一距离的位置。这样既保证了曲率连续,也不会明显偏离原航线。如果直接拿折线路径给无人机跟踪,无人机到航路点时会突然转向,仿真动画里能看到明显的“抖动”,这是很多人忽略的问题。

3.2 速度障碍法如何避免机间碰撞

固定路径只解决了“走哪条路”的问题,还没解决“同一条路遇到其他无人机怎么办”。CVACA中的避碰我用的是速度障碍法的改进版,核心思想非常简单:把相对速度映射到以对方无人机为圆心的障碍圆上,如果速度方向落在危险区域内,就调整速度方向或大小,使其离开危险区域。

具体到Matlab实现,我维护每一架无人机的期望速度向量,计算与邻居的相对位置和相对速度,判断是否存在碰撞风险。如果存在,就在期望速度周围搜索一个可行速度,使得相对速度不指向对方的安全圆。这个可行速度还要满足无人机自身的最大速度、最小转弯半径约束。

避碰机动不能太激进,不然无人机会在密集编队里反复避让。我给避碰修正量设置了一个上限,并且只有当预测碰撞时间小于某个阈值时才启动。这个“预测碰撞时间”很关键,太大会导致频繁避让,太小则会来不及。我通常把阈值设为2到3秒,对应着无人机以当前速度接近对方时,还有足够反应时间,又不会因为远处无人机顺路就误判为碰撞风险。

3.3 固定路径跟踪与局部避让的切换逻辑

无人机既要沿固定路径走,又要在必要时避让,这里需要一套切换逻辑。我的做法是:正常情况下,期望速度由路径跟踪控制器给出,也就是沿参考路径切线方向、按期望速度大小前进;当检测到碰撞风险时,把期望速度切换为避碰修正后的速度;风险解除后,再通过一个恢复项引导无人机回到参考路径上。

这个“恢复项”我在项目里叫路径回归力,大小和无人机偏离路径的距离成正比。相当于每架无人机被路径拴住,离得越远越要往回拉。这样做的好处是避让只是短暂的偏离,整体仍然保持固定路径的秩序,不会越躲越乱。

代码上,路径跟踪和避碰都是独立的函数,主循环里顺序调用即可,层级清晰。这里有一点容易忽略:避碰修正后的速度不能直接丢弃,至少要保留一两个控制周期,否则下一帧路径跟踪又会把速度拉回到危险方向。后面我在问题排查里会专门讲这个案例。

3.4 CVACA核心函数代码示例

下面这段是我仿真里的关键函数框架,重点展示速度障碍避碰的逻辑:

function v_new = cvaca_avoidance(drones, i, v_des, dt) v_new = v_des; pos_i = drones(i).pos; r_i = drones(i).radius; for j = 1:length(drones) if j == i, continue; end rel_pos = drones(j).pos - pos_i; rel_vel = v_new - drones(j).vel; dist = norm(rel_pos); % 安全距离:两机半径和 + 制动余量 safe_dist = r_i + drones(j).radius + drones(i).brakeDist; if dist < safe_dist % 相对速度方向是否指向对方? if dot(rel_pos, rel_vel) < 0 % 构造分离速度:沿相对位置方向修正 dir_sep = rel_pos / dist; correction = dir_sep * drones(i).maxAccel * dt; v_new = v_new + correction; % 限制速度幅值 if norm(v_new) > drones(i).maxSpeed v_new = v_new / norm(v_new) * drones(i).maxSpeed; end end end end end

这段代码是简化的速度障碍避碰,适合作为仿真验证。真实工程里建议用完整的速度障碍锥判断,还要把邻居的预测位置考虑进去。我后面在问题排查里会讲一个这样写容易踩的坑。函数里的correction直接和加速度、步长相关,逻辑简单,但缺点是只有当距离已经小于安全距离时才触发修正,反应偏晚。如果步长太大或者无人机速度太快,一个时间步内就可能穿越对方的安全圆。

4. Matlab仿真环境搭建与核心代码实现

4.1 仿真参数如何初始化

Matlab仿真的第一步不是写算法,而是把参数定义清楚。我会用结构体或者脚本变量统一管理参数,避免硬编码散落在各个函数里。常用参数包括:地图范围、无人机数量、通信半径、最大速度、碰撞半径、仿真步长和单次仿真时长。建议刚开始验证时先把无人机数量控制在8到30架之间,数量太少看不出集群效应,数量太多又会被调试问题淹没。

初始化代码可以这样写:

params.N = 10; params.map = [0 100 0 100]; params.commRange = 25; params.maxSpeed = 5; params.safeRadius = 1.5; params.dt = 0.1; params.T = 60; drones = initDrones(params.N, params.map, params); targets = initTargets(params.N, params.map);

initDrones里要随机放置无人机并给初始速度清零,同时给每架无人机的路径数据预分配矩阵。这里预分配很重要,因为后面每一帧都要记录位置和速度,不预分配会让Matlab越来越慢。我见过不少同学在循环里用history = [history; newPos],仿真跑到一半明显卡顿,就是这个原因。

4.2 主循环:把博弈决策、路径生成和运动控制串起来

主循环是整套仿真的发动机。我按下面这个结构组织:

t = 0; idx = 1; maxSteps = floor(params.T / params.dt); history.pos = zeros(params.N, 2, maxSteps); history.targets = zeros(params.N, 2, maxSteps); while t < params.T % 每5秒触发一次博弈层决策 if mod(t, 5) == 0 targets = gameTheoryUpdate(drones, targets, params); % 根据新目标重新生成固定路径 drones = generateFixedPath(drones, targets, params); end for i = 1:params.N v_des = pathFollowing(drones(i), t, params); v_safe = cvaca_avoidance(drones, i, v_des, params.dt); drones(i).vel = v_safe; drones(i).pos = drones(i).pos + drones(i).vel * params.dt; end history.pos(:, :, idx) = reshape([drones.pos], 2, params.N)'; history.targets(:, :, idx) = targets; t = t + params.dt; idx = idx + 1; end

这里在每5秒执行一次博弈更新,也可以做成事件触发,比如通信链路中断时触发。固定路径的重新生成频率和博弈更新频率保持一致,因为只有目标点变化时才需要改路径。主循环的分工非常清楚:上层决策改变目标点,中间层生成路径,底层运动控制和避碰每步执行。如果你想加入障碍物避障,只需要在cvaca_avoidance之前再加一个静态障碍物检测函数即可。

4.3 数据处理与实时可视化

仿真跑起来之后,光看数据很难发现问题,我习惯在Matlab里开两个图:一个显示无人机实时位置、目标点、路径曲线和探测范围,另一个显示效用值、最小间距、平均速度。实时可视化能立刻暴露路径穿插、目标震荡、避碰失效等问题。

简单的可视化工具有plot、quiver、viscircles,例如:

figure(1); clf; hold on; axis equal; for i = 1:params.N plot(drones(i).path(:, 1), drones(i).path(:, 2), '--'); quiver(drones(i).pos(1), drones(i).pos(2), ... drones(i).vel(1), drones(i).vel(2), 'r'); viscircles(drones(i).pos, drones(i).radius); end

保存数据我用mat文件或者直接把history输出,方便事后重放和统计。重放比实时看更好用,因为可以放慢速度逐帧检查。我一般在代码里加一个pause(0.01)控制动画刷新,数据记录则完全不受显示影响。事后重放时,只需要遍历history中的位置数据,调用同一套绘图函数,逐帧绘制即可。

5. 仿真结果分析与参数调优

5.1 我怎么评估一套部署加运动方案好不好

仿真不是为了跑个动画,是要有量化指标。我关注四个维度:收敛时间、覆盖代价、碰撞次数、通信链路质量。收敛时间是博弈层达到均衡所需的迭代次数或仿真时间;覆盖代价是未覆盖区域面积或连续空洞数量;碰撞次数是仿真全程最小间距小于安全距离的次数;链路质量是相邻无人机保持通信的比例。

这四个指标经常互相矛盾。比如提高速度会减少到达时间但增加碰撞风险;增大通信半径会改善链路但也会让博弈更新时邻居信息更多,计算变慢。调参时要明确当前场景更看重什么。如果做搜索任务,我会优先看覆盖代价;如果做编队飞行演示,我会优先看碰撞次数和路径保持误差。没有统一的最优,只有针对场景的平衡点。

5.2 几组典型对照实验

我做过的对照实验里,比较有参考价值的是这几组。第一组,固定权重vs自适应权重。固定权重下,10架无人机在100x100地图上容易扎堆,因为覆盖收益权重不够大;自适应权重下,集群会更快分散到不同区域,覆盖代价低了约20%。第二组,博弈更新频率设置1秒、5秒、10秒。1秒更新时目标点频繁变化,路径刚生成就要改,能耗增加;10秒更新时任务响应慢。5秒在多数场景下比较稳。第三组,避碰阈值偏大偏小的对比。阈值过大时,无人机在稀疏场景频繁绕行,路径偏离严重;阈值过小时,密集编队会出现危险逼近。

这些结果不一定能直接搬到所有场景,但至少说明一个问题:任何参数都要和任务场景绑定,不能盲目固定一组“万能参数”。我见过有人把别处论文里的参数直接搬过来跑,结果完全不对,原因就是地图尺度、无人机速度、通信半径都不一样。

5.3 参数调优的一点实战心得

调参我有两个习惯。第一个是用网格搜索扫关键参数,把覆盖代价和碰撞次数画成热力图,一眼能看出哪些参数敏感。第二个是固定随机种子。Matlab里用rng(0)固定随机数,否则每次初始化不同,调参时会分不清是参数影响还是随机性影响。

另外一个容易被忽略的点是博弈权重和运动速度要匹配。如果博弈层希望无人机快速响应新目标,但运动层最大速度很低,目标点变化再快也没用。反过来,运动层速度很快,博弈层更新太慢,无人机会在旧目标上来回飞。让两层的“时间尺度”匹配,这是整个系统稳定运行的关键。我的做法是先固定运动层参数,让路径跟踪和避碰表现稳定,再去调博弈层的更新周期和权重,避免同时调太多变量。

6. 常见问题与排查技巧实录

6.1 问题速查表

我把仿真中经常遇到的问题整理成表,方便大家对照。

现象可能原因排查思路
无人机目标点来回震荡博弈更新频率过高,或候选策略间距太小降低更新频率,增大候选点间距
固定路径形变严重贝塞尔控制点参数不合适检查控制点位置,减少曲率突变
避碰频繁误触发预测碰撞时间阈值太大调小阈值,或只在相对距离小于某值时才检测
密集编队仍然相撞避碰修正只用了dt内位移,来不及改用速度障碍圆锥,提前一个时间窗口预测
仿真越来越慢历史数组未预分配预先分配history矩阵,避免动态扩维
收敛后覆盖仍有空洞覆盖收益权重太低提高w_c,或调整覆盖函数半径

表格里的问题基本我都踩过,有些是因为模型理解不足,有些则是代码习惯造成的。比如历史数组未预分配,这个和算法本身没关系,但会影响你调参效率,仿真跑一秒卡十秒,根本没法做参数扫描。

6.2 从一次“路径穿越”问题说开去

有一次仿真,我发现两架无人机的固定路径交叉,但避碰系统没有阻止。查了很久才发现,问题出在路径跟踪器的期望速度上:当无人机已经在路径交点附近时,避碰函数确实修正了速度,但下一帧路径跟踪器又把期望速度拉回原切线方向,而且修正量太小,最终导致穿越。

这个问题的本质是固定路径跟踪和避碰之间的优先级切换不够平滑。后来我在避碰函数里加入了一个“碰撞时间预测”的滞回逻辑:只有预测碰撞时间持续小于阈值时才强制修正速度,并且修正后的速度会保留一小段时间,避免被路径跟踪立即覆盖。这样处理之后,穿越问题基本消失。

还有个细节是避碰函数里的安全距离不能只算当前帧。因为无人机有速度,当前帧没碰不代表下一帧不碰。必须用相对速度和相对位置外推一下,预测未来0.5到1秒内是否进入安全圆。这个外推逻辑很便宜,但对可靠性提升很大。

6.3 可视化调试和随机性控制的技巧

最后一个建议是重视可视化调试。我一般在仿真中画三样东西:无人机安全圆、目标点编号、路径跟踪误差。安全圆能直观看出是否接近碰撞;目标点编号能确认博弈结果是否正确;路径跟踪误差则能判断避让后是否还能回到参考路径。

调试策略的时候,把rng固定住,让每次初始布局一样,只改算法逻辑。算法调稳定了,再放开随机种子,跑多组实验验证鲁棒性。这个顺序能帮你减少80%的无效调试时间。我自己的习惯是先在10架无人机、无障碍物的简单场景里把博弈和避碰跑通,再逐渐增加无人机数量、加入障碍物、引入通信丢包。每一步只引入一个新的复杂度因素,出了问题能立即定位。

我自己在实际调试中最大的体会是:多无人机部署与运动仿真真正难的不是某一个算法,而是让两层策略在时间尺度、信息粒度上相互匹配。博弈论给你全局视角的决策,CVACA给你局部安全的运动,二者缺一不可。后续想扩展的话,可以考虑把通信时延和数据丢包塞进仿真里,让博弈更新在动态不确定的环境下做决策,这会比现在的理想信道模型更接近实际。希望这篇内容能让你少走一点我之前走过的弯路。

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

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

立即咨询