多无人机系统这几年从实验室往实际场景迁移的速度比很多人预想得快,但真正落地时最头疼的往往不是单机飞控,而是多机之间的互相牵制。我在做区域覆盖部署仿真时,就遇到过两种典型矛盾:既想让每架飞机按最省事的固定路径飞,又想让它们在遇到突发障碍或队友偏离时能自己商量着调整。这次要分享的项目,就是围绕这个矛盾展开的——用博弈论自适应策略和CVACA固定路径策略做多无人机部署与运动仿真,整套逻辑用Matlab搭了起来,跑出了轨迹、碰撞次数、覆盖率和收敛时间这些指标。如果你正在做多机协同、路径规划或者集群编队相关的仿真,这篇内容可以从策略选型讲到代码实现,再讲到参数怎么调,可以直接拿来当参考。
1. 项目整体思路与策略选型
1.1 两种策略为何要搭配使用
无人机部署任务里,最朴素的做法是给每架飞机规划一条固定路径,让它沿着航点走完。这种CVACA固定路径策略的优势很直接:轨迹可预测,计算开销低,适合环境已知、任务重复性高的场景。但缺点也明显——一旦某架无人机因为风场偏移或临时避障偏离了航线,其他无人机还在沿旧路径飞,就可能形成连锁冲突;如果任务目标点临时变化,整组路径都作废。
博弈论自适应策略走的是另一个极端:每架无人机都是独立决策者,通过“当前状态+邻居状态”不断调整自己的速度和方向,目标是让整个集群收敛到大家都可接受的平衡点。它的好处是分布式、鲁棒、不需要中心节点重新规划,但代价是决策周期短、计算频繁,在场景简单时反而显得多余。
所以我在这套仿真里的核心思路是:让两者配合,而不是二选一。常态下用CVACA固定路径策略保证效率和确定性,一旦局部密度过高、偏离目标过多、或者检测到动态障碍,再触发博弈论自适应策略来做局部调整。这样可以同时保留固定路径的可预测性和自适应策略的应变能力。后面所有代码和参数调整,都是围绕这个“主路径+应急博弈”的框架展开的。
1.2 仿真任务场景与无人机运动模型
我设计的仿真场景是这样的:模拟区域设定为500米×500米的矩形区域,10架无人机从区域的同一侧起飞,目标是在任务时间内分散到区域的不同子区块,完成覆盖部署,并在后续巡逻中保持最小安全距离。没有设置复杂建筑物,但保留了一个代表突发威胁的圆形禁飞区,用来验证策略应对动态环境变化的能力。
无人机运动模型我用了简化的二阶积分模型:位置更新等于速度积分,速度更新等于加速度积分。虽然真实飞控做不到这么理想,但对策略层验证完全够用。每架无人机有最大速度限制和最大加速度限制,我分别设成25m/s和5m/s²。控制周期固定为0.1秒,也就是10Hz的决策频率,比真实飞控外环控制常见的50Hz低一些,但已经能看出策略层的收敛特性。
为什么不直接用旋翼动力学模型?原因是博弈策略和CVACA策略解决的是“往哪里飞”的问题,不是“电机给多少油门”的问题。把底层动力学拆出去,仿真代码更容易读,参数调起来也更快。如果你要往真实飞控迁移,再在这个策略层输出后面接一个姿态控制器就行。
初始部署环节也很重要。我让第一架无人机从起点出发,后面每架延迟0.5秒起飞,初始位置不是完全重叠,而是沿着起飞线方向均匀错开一小段距离。这么做是为了模拟现实编队起飞的场景,也能让CVACA固定路径从一开始就处在不同航线上,避免多机在续航段扎堆。
1.3 关键技术指标与验收口径
我最终输出的指标有四类:
- 覆盖率:目标区域内被无人机传感器覆盖的栅格比例,我把单机感知半径设为30米。
- 碰撞次数:任意两架无人机间距小于8米就算一次碰撞。
- 路径效率:所有无人机总航程和固定路径理论航程的比值。
- 收敛时间:覆盖率首次达到90%的时刻。
这几个指标对应到实际任务里就是:覆盖均匀程度、飞行安全性、能耗水平和任务完成速度。仿真不能只看动画,把这些指标记录下来,后面调参才有依据。我在代码里专门写了一个记录模块,每隔0.5秒把每架飞机的位置、速度、当前策略状态、局部邻居数追加到结构化数组中,等仿真结束后再统一绘图和分析。
2. 博弈论自适应策略的核心原理与Matlab建模
2.1 博弈模型的三要素
博弈论用在多无人机上,本质上就是把多机协同问题转换成分布式优化问题。每个无人机是博弈参与者,策略是它在当前控制周期内选择的加速度向量,收益函数则综合考虑几个互相冲突的目标。
我用的收益函数如下:
J_i = w_target * 目标接近收益 + w_collision * 碰撞风险惩罚 + w_energy * 能耗惩罚 + w_coverage * 覆盖均匀度收益。
目标接近收益用到目标点的距离;碰撞风险惩罚用邻居相对位置和速度来算;能耗惩罚用加速度大小的平方;覆盖均匀度收益用当前无人机的邻近区域覆盖缺失程度。每个权重不是写死的,而是根据局部环境自动调整,这就是“自适应”的意思。
在Matlab代码里,我先把每个无人机的收益函数定义成独立函数,然后在允许的加速度区间里离散采样,找使收益最小的那个加速方向。实际程序比理论模型多一些工程细节,但总体框架就是如此。这里有个关键点:目标并不一定是个点,而是一块子区域。所以targets矩阵里存的是子区域中心坐标,无人机的目标接近收益是到中心点的距离。这样写方便,但也容易让所有无人机都挤向中心点,所以覆盖均匀度收益必须足够强,否则会出现“中间密、两边空”的部署。
2.2 收益函数中的自适应权重
自适应权重是整个策略的亮点。固定权重的问题很好理解:如果碰撞权重设得太高,在开阔区域无人机也会互相躲闪,导致绕路严重;设得太低,在密集区域又反应不足。自适应权重通过实时感知局部邻居数来改变惩罚强度。
具体做法是:
w_collision(t) = w_collision0 * (1 + k * localDensity / maxDensity)
其中localDensity是当前无人机在冲突检测半径内的邻居数量,maxDensity是模拟中的最大邻居数。k是一个增益系数。同理,覆盖均匀度收益的权重也会根据当前无人机所在网格的覆盖缺失率提高,引导无人机走向覆盖稀疏的区域。
这样处理的好处是,当无人机集群稀疏时,碰撞权重自动降低,不会牺牲路径效率;当集群聚集时,碰撞权重自动升高,优先保证安全。我在代码里初始化时把基础权重设为:wTarget=1.0、wCollision0=1.2、wEnergy=0.4、wCoverage=0.8。这个组合在10架无人机场景下表现比较平衡,后面调参时会再细说。
2.3 关键Matlab代码:分布式迭代求解
下面这段是博弈决策的核心函数,我把它写成独立模块,方便替换测试。
function u = adaptiveGameControl(pos, targets, vel, dt, params) n = size(pos, 1); u = zeros(n, 2); for i = 1:n J_min = inf; best_u = [0 0]; for ux = -params.uMax:params.uStep:params.uMax for uy = -params.uMax:params.uStep:params.uMax acc = [ux uy]; newVel = vel(i,:) + acc * dt; newPos = pos(i,:) + newVel * dt; J = costFunction(i, newPos, newVel, pos, targets, params); if J < J_min J_min = J; best_u = acc; end end end u(i,:) = best_u; end end function J = costFunction(i, newPos, newVel, pos, targets, params) threatCol = 0; for j = 1:size(pos, 1) if j == i, continue; end dist = norm(newPos - pos(j,:)); if dist < params.detectDist threatCol = threatCol + (1 - dist / params.detectDist)^2; end end targetCost = norm(newPos - targets(i,:)) / params.normScale; energyCost = sum(newVel.^2) / params.vMax^2; coverageCost = coverageGap(newPos, params); J = params.wTarget * targetCost - params.wCollision * threatCol ... + params.wEnergy * energyCost + params.wCoverage * coverageCost; end这段代码的核心是离散采样。把每个无人机的加速度限制在一个正方形网格里,步长参数params.uStep决定搜索精度。步长太大容易错过最优解,步长太小会导致单步计算量爆炸。我最终把uMax设为5,uStep设为1,在10架无人机的情况下单次全周期计算时间大概在80毫秒左右,能跑出实时性。
注意,costFunction里用到了coverageGap函数,它统计在newPos附近30米覆盖半径内未覆盖的栅格数。为了让博弈策略知道往哪飞,这个函数必须提前算一次覆盖栅格图,后面会随着位置变化更新。我这里用的是全局地图的实时覆盖标记矩阵,每次计算时要先查询newPos周围栅格的状态,再加上一个极小偏移量防止除零。
还有一个细节很多人会忽略:每次调用costFunction时,其他无人机的位置用的是当前时刻的位置,而不是预测结果。严格来说这不完全等价于顺序博弈,但好在决策周期很短,0.3秒内位置变化有限,误差可接受。如果你追求更高的仿真保真度,可以用上一拍其他无人机的速度做一步线性预测,再代入收益函数。
2.4 为什么分布式博弈能避免死锁
很多人担心分布式决策会互相打架,比如两架无人机迎面相遇,如果各自只考虑自己的收益,可能会同时选择同一侧避让,形成镜像振荡,也就是你往左我也往左,始终躲不开。
实际仿真里我确实遇到过这种情况。解决办法不是取消博弈,而是在收益函数中加入对邻居上一步决策的惩罚。具体做法是在计算碰撞风险时,不仅看当前位置,还看邻居上一时刻的速度方向。如果邻居上一时刻已经向左偏,当前无人机的收益函数会规避继续向左偏的趋势,相当于隐式传递了一个策略信号。我把这个惩罚项写成:
symmetryPenalty = k_sym * dot(neighborVel, candidateAcc)
当候选加速度方向与邻居速度方向接近同向时,惩罚变大,以此打破镜像对称。从博弈论角度看,这就是最佳反应动态的一个变体。每个参与者选择对自己当前收益最优的策略,同时参考其他参与者的当前策略。只要收益函数连续且满足严格凸条件,基本能收敛到纳什均衡附近。实际中不要求严格凸,只要权重合适,仿真中就能看到集群逐步分散并稳定覆盖,而不是长时间振荡。
3. CVACA固定路径策略的设计与联合仿真
3.1 固定路径生成:从起点到任务子区的平滑曲线
CVACA固定路径策略里,“固定路径”不代表直线飞,而是任务开始前为每架无人机生成一组确定性航迹。为了让航迹平滑且满足最大转弯率约束,我用的是三阶贝塞尔曲线。
对第i架无人机,它的起点是初始位置,终点是使用博弈策略预分配的目标子区中心。贝塞尔曲线的两个控制点选取为:第一个控制点在起点沿当前速度方向前方80米处;第二个控制点在终点沿目标方向反向80米处。这样生成的曲线不会出现锐角转折,也不会穿越禁飞区。如果路径穿过禁飞区,我会重新选终点或调整控制点,保证生成的路径初始可行。
在Matlab里生成曲线的函数如下:
function path = makeFixedPath(p0, p1, nPoints) ctrl1 = p0 + [80 0]; ctrl2 = p1 - [80 0]; t = linspace(0, 1, nPoints).'; path = (1-t).^3 * p0 + 3*(1-t).^2 .* t * ctrl1 ... + 3*(1-t) .* t.^2 * ctrl2 + t.^3 * p1; end路径生成后,每架无人机保存一组航点。在每次控制周期,用最近航点作为临时目标,计算期望速度。这里我用了“航点索引推进”的方式:无人机当前航点索引只会增不会减,除非距离新航点已经小于5米。这样可以避免路径跟踪过程中出现来回抖动。
3.2 冲突矢量修正:3个必须优先调的参数
固定路径本身不会避碰,所以要叠加一个修正项。我把这个修正项叫作CVACA修正项。它的核心逻辑很直接:对每架无人机,计算它和其他无人机的相对位置矢量,如果在冲突检测范围内,就施加一个沿相对位置方向、大小与距离成反比的排斥速度。同时考虑相对速度,如果相对速度正在拉开距离,即使距离近也不要惩罚太狠。
具体的修正量可以写成:
avoidFix = kAvoid * (relPos / dist) * exp(-(dist / safeRadius - 1)^2)
这里最关键的两个参数是safeRadius和detectDist。我观察到,如果detectDist和safeRadius的比值小于2.5,无人机会在刚进入检测范围时就开始猛打方向,产生抖动。太大的detectDist又会让无人机在很远就绕行,浪费路径效率。我最终用safeRadius=8米、detectDist=20米、kAvoid=2.5的组合,在多数随机场景下能跑到碰撞次数小于等于1。
另外,修正量要限制在最大速度范围里,不能直接加到原速度上就超限。我单独写了一个velSaturate函数,把速度向量按最大速度截断,而不是简单乘系数,这样能保留方向信息。如果截断方向,无人机可能会原地转圈,这个坑踩过之后我就一直保留方向饱和逻辑。
3.3 固定路径与自适应策略的切换机制
两种策略怎么切换是关键。我的做法不是用一个简单阈值判断,而是用“状态机+迟滞”。
正常情况下,无人机处于固定路径模式,此时CVACA修正生效。每0.5秒计算一次以下三个值:
- 路径偏移残差:当前无人机位置到固定路径最近航点的横向距离,超过20米则认为路径失效。
- 邻居密度:检测半径20米内邻居数量,超过3则触发博弈策略。
- 威胁触发:禁飞区或动态障碍物位置发生变化,如果与固定路径的距离小于50米,则切换。
为了防止频繁切换,进入博弈状态需要任意两个条件同时成立,退出博弈状态则要所有条件都恢复正常且持续2秒以上。这个迟滞窗口很关键,否则无人机会在固定路径和自适应策略之间反复横跳,轨迹非常难看。我这里用了一个长度为10的控制周期计数器,计数器连续清零才允许退出。
3.4 Matlab仿真主循环与结果记录
主循环控制了整个仿真的节奏。我把它简化成下面这段伪代码,实际工程里加了日志和动画回放。
for t = 0:dt:T for i = 1:n desiredVel = pathController(paths{i}, pos(i,:), params.vMax); avoidFix = cvacaAvoid(pos, vel, i, params); vel(i,:) = desiredVel + avoidFix; [trigger, reason] = checkSwitch(pos, targets, paths{i}, params); if trigger deltaV = adaptiveGameControl(pos, targets, vel, dt, params) * dt; vel(i,:) = vel(i,:) + deltaV; switchState(i) = 1; else switchState(i) = 0; end pos(i,:) = pos(i,:) + vel(i,:) * dt; end recordCoverage(pos, coverageMap, t); recordTrajectory(t, pos, vel, switchState); end这个循环只看策略层,细节不少:pathController输出的是期望速度,CVACA修正直接加在速度上,自适应博弈输出的是加速度增量。这里有个容易踩的坑:固定路径期望速度加上CVACA修正后,可能会超出最大速度。我单独写了一个velSaturate函数,把速度向量按最大速度截断,而不是简单乘系数,这样能保留方向信息。
recordCoverage和recordTrajectory分别把覆盖率变化和每一帧位置记录下来。后面分析用的数据都从这两个函数来。recordCoverage内部会把500米×500米区域划分成5米分辨率的网格,每个无人机覆盖一个半径30米的圆形区域,新位置所在栅格以及与周围栅格的重叠范围都会被标记。覆盖率就是已标记栅格数占目标区域总栅格数的比例。
4. 仿真结果分析与参数调优实战
4.1 三种策略的对照结果
我把三种方案各跑了30次蒙特卡洛仿真,使用相同随机种子,表格里的数据是我记录的典型中位数。
| 策略 | 平均碰撞次数 | 覆盖率90%所需时间(秒) | 平均路径效率 |
|---|---|---|---|
| 仅CVACA固定路径 | 7 | 182 | 1.00 |
| 仅博弈自适应 | 2 | 208 | 1.35 |
| CVACA+博弈联合 | 1 | 156 | 1.12 |
从结果看,联合策略在碰撞次数上最安全,收敛时间最短,路径效率介于两者之间。这个结果说明,固定路径保证了任务的主线推进,自适应策略只在出事的时候介入,既没有丢掉效率,也提升了安全性。
需要说明的是,这只是我仿真环境里的统计特征,不代表所有算法都这样。图表的数值和地图的障碍布置、无人机数量强相关。你复现时不必追求一模一样,重点看趋势。我自己的结论是:如果任务环境高度动态、威胁区域经常变,联合策略的优势会更明显;如果环境完全静止,固定路径加一个足够好的避碰修正其实就够用。
4.2 常见问题与排查:5类高频bug
仿真过程中我整理了高频问题,列成表格,方便你对照。
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 多架无人机在目标点附近重叠静止 | 收益函数陷入局部极小,碰撞权重过低 | 增大碰撞权重,在收益函数中加一点随机扰动 |
| 路径跟踪处出现高频抖动 | CVACA修正增益过大,或速度饱和处理不当 | 减小kAvoid,检查修正量是否超限 |
| 博弈状态频繁切换 | 迟滞窗口过短,或两个条件阈值太松 | 把退出延迟改成2秒以上,提高触发阈值 |
| 近距离碰撞没检测到 | 决策周期太大,detectDist过小 | 控制决策周期小于0.5秒,detectDist至少为safeRadius的2.5倍 |
| 覆盖率长时间卡住不上升 | 覆盖收益权重过低,无人机只想着避碰和节能 | 增大wCoverage,并检查coverageGap函数是否正确更新 |
排查时我建议按顺序来:先看安全距离相关参数,再看切换逻辑,最后调收益权重。因为很多诡异现象本质上都是碰撞检测参数错乱导致的连锁反应。有一次我调了半天博弈权重,最后发现问题出在detectDist设成了8米,等于safeRadius,无人机几乎等撞上才开始避让,再调权重也没用。
4.3 参数调优的个人实践心得
调参没有捷径,但有顺序。
第一步定安全半径。安全半径必须同时大于机体尺寸、定位误差和路径跟踪误差。如果这三项加起来是5米,安全半径最少设8米。我见过有人把safeRadius设成与机体半径相同,仿真动画里看起来没事,但实际传感器噪声一加就大量碰撞。
第二步调CVACA修正参数。保持detectDist与safeRadius的比例,先固定kAvoid=1,跑一次看有没有碰撞,然后逐步增加,直到出现抖动再回退一点。kAvoid我试过从1到5,1到2之间碰撞次数快速下降,超过3.5之后抖动明显,最后定在2.5附近。
第三步调博弈收益权重。我通常先把wCollision调到能让碰撞次数降到接近0,再看覆盖率。如果覆盖率涨不动,就把wCoverage调大。注意每次只调一个权重,否则找不到因果。一个可行起点是:wTarget=1.0、wCollision0=1.2、wEnergy=0.4、wCoverage=0.8。在密集场景里把wCollision0加到1.8,在稀疏场景里可以降到0.8。
另外,决策周期不是越小越好。决策周期越小,博弈求解次数越多,单步时间窗内位置变化越小,理论上更安全,但计算负载和通信开销都会上去。在我这个仿真里,决策周期0.3秒最稳,0.1秒时CPU占用已经到80%。如果你用的是真实机载处理器,还要考虑功耗和实时性,周期设在0.2秒到0.5秒之间比较合适。
最后提醒一点:调参一定要同时记录多个指标,不要只盯着动画。有一次我把碰撞调没了,结果覆盖率87%就卡住,因为无人机为了互相避让,全缩在区域边缘。要不是看着覆盖率曲线,很难发现这种隐性退化。我后来加了一条规则:在收益函数里加入“边缘惩罚”,当无人机离目标区域边界太近时,覆盖收益权重自动提高,这个问题才缓解。
5. 从仿真到实飞的一些个人体会
仿真跑通之后,我把这套逻辑往半物理方向迁移过,最大的感受是:策略层能Work,不代表真机也能Work。仿真里没有通信延迟,没有传感器噪声,也没有电机响应延迟。博弈自适应策略在仿真里每一步都能拿到所有邻居的精确位置,真实场景下单是数据频率不一致就能让收益函数失真。
所以如果你打算落地,我建议先做两件事:第一,在仿真里给位置观测加一个零均值高斯噪声,噪声方差大概在0.5米左右,看看碰撞次数会不会上涨;第二,在状态更新里加入一拍延迟,模拟通信周期和决策周期不同步的情况。这两件小事能在真正上飞机前暴露大部分问题。我自己在加了噪声和一拍延迟后,原本1次的平均碰撞次数涨到了5次左右,后来通过提高碰撞权重和增加CVACA修正增益才压下来。
另外,代码模块化很重要。我把博弈决策、CVACA修正、路径生成、结果记录都写成独立函数,后续换真实飞控时,只需要把期望速度输出接到底层控制器,不需要重写整个仿真。我个人现在做类似任务时,会额外把每一架无人机的收益函数分量都打日志,方便复盘。调参数其实是在调你对问题的理解,这句经验比任何公式都值钱。