ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

UUV编队控制:Olfati-Saber框架下的flocking算法仿真解析

UUV编队控制:Olfati-Saber框架下的flocking算法仿真解析 简介面向UUV编队控制与多智能体协同的MATLAB仿真源码包围绕flocking算法提供完整实现可用于解决多UUV在动态环境中的编队保持、邻域交互与避碰控制问题适合控制理论与机器人方向的科研人员、工程师及高年级学生学习使用。压缩包共17个文件以15个.m脚本为核心涵盖sigma_norm、bump_func、action_func、phi_func等典型协议函数并附带1个.bmp示意图与1个txt说明文档整体仅15KB轻量易读便于修改参数或嵌入更大模型。通过运行该程序可学习多智能体系统建模、领导-跟随编队策略、邻域搜索与通信机制等关键知识点同时借助可视化输出直观观察编队运动轨迹评估一致性、稳定性等性能指标为后续算法改进与工程应用提供可靠模板。目前已有494人学习下载适合希望快速上手UUV编队仿真实践的开发者。1. 用 Olfati-Saber 框架理解 UUV 编队flocking 不是跟随航线而是规则驱动很多编队控制教程上来就把问题定义成「给定一条期望轨迹让 UUV 去跟踪」再用 PID 或滑模控制器把横向、纵向误差压到零。水下场景这么干很吃力DVL 和惯导估计出的位置本身有漂移水声通信带宽又窄让每个个体都拿到同一套全局轨迹并不现实。flocking 的思路正好反过来——不给定全局航迹只给三条局部规则分离、聚合、速度对齐。每个 UUV 根据感知半径内的邻居状态自己决定下一步怎么走。这个 MATLAB 仿真包里没有 Simulink 模型也没有六自由度水动力方程核心是十几个 .m 脚本把 Olfati-Saber 框架拆成 σ-范数、bump 函数、φ 函数和 action_function 四个层级。适合已经理解一致性基础、想从代码层面看清楚编队行为如何从局部交互涌现的人。2. σ-范数与邻域探测不可导距离的平滑处理2.1 为什么用 σ-范数而不是欧氏距离UUV 之间的交互强度完全取决于相对距离q_j - q_i。太近要排斥太远要吸引排斥和吸引的切换必须连续。但如果直接用欧氏距离||z||在z 0处梯度不存在控制器在这一点会发生跳变水下推进器和舵机响应慢跳变很容易让编队振荡甚至碰撞。σ-范数解决的就是这个问题定义如下function d sigma_norm(z, eps) % sigma_norm.m: 计算两个UUV相对位置的sigma范数 % z: 1x2 相对位置向量 [dx, dy] % eps: 平滑参数默认0.1 if nargin 2 eps 0.1; end d (sqrt(1 eps * sum(z.^2)) - 1) / eps; endsum(z.^2)是相对位置的平方和sqrt(1 eps * sum(z.^2))让整个表达式在零点附近保持平滑。当z 0时σ-范数的导数为 0不会出现梯度爆炸。eps控制这条曲线和欧氏距离接近的程度取值越小越接近原始距离但数值梯度越容易把相对位置的噪声放大。我一般取 0.1这个值既能保留可导性又不至于让远距离吸引衰减太快。包里的segma_1.m和sigma_norm_2.m可以理解为针对不同eps的实现变体做对比实验时直接替换调用即可主循环不用改。2.2 bump 函数把感知半径变成软边界每个 UUV 的声呐或水声通信范围有限超出感知半径的邻居不参与计算。但如果用if dist r这种硬阈值直接截断邻居数量的跳变会给控制量带来脉冲仿真曲线上会看到明显毛刺。bump_func.m的作用是把感知边界做平滑function b bump_func(z, r, R) % bump_func.m: 在(r,R)区间内从1平滑降到0 % z: 当前sigma范数 % r: 期望编队间距 % R: 最大感知半径 if nargin 3 r 5; R 1.2 * r; end val (z - r) / (R - r); if val 0 b 1; % 距离小于r完全作用 elseif val 1 b 0; % 超出感知范围完全不参与 else b 0.5 * (1 cos(pi * val)); % 余弦平滑过渡 end endval 0对应距离刚好等于r权重是 1val 1对应距离等于R权重降到 0。余弦曲线保证两端导数连续邻居刚刚进入感知范围的瞬间作用力是从零平滑长出来的不会猛地踢一脚。R取1.2 * r到1.5 * r之间比较常见。取太大感知范围内长期存在大量邻居邻接矩阵变稠密速度一致性被过度平均队形反而拉不开取太小感知范围里常常只剩一两个邻居聚合作用容易失效编队断链。这个参数直接影响后续邻接矩阵的稀疏程度值得多试几组。2.3 squad_norm 与邻接矩阵每次迭代都要重新算squad_norm.m对所有 UUV 两两计算 σ-范数得到一个N×N对称矩阵。得到这个矩阵后逐项套bump_func就得到带权重的邻接矩阵—每个 UUV 眼中的邻居分布。由于 UUV 一直在动邻居关系是时变的这个矩阵每个仿真步都要重建不能缓存。包里的check.m和queue.m是配合使用的辅助脚本check检查每个 UUV 的邻居数量低于阈值就记录一次孤立事件用来判断编队是否出现断链queue按初始位置给 UUV 编身份序号身份序号决定后面谁当 leader、谁当 follower。一个快速的验证办法是取 5 个 UUV初始位置随机手动跑一次squad_norm加bump_func看生成的邻接矩阵中非零元素是否都落在R以内。如果矩阵变得过密多半是R设得太大或者bump_func的R参数没有传入主循环导致默认值和预期不一致。这个排查思路在整个调试过程中会反复用到。3. φ 函数与稳态队形用 h 参数伸缩编队间距3.1 势场函数的分段设计flocking 框架里分离和聚合不是两个独立的控制器而是合在一个势场函数φ(z)里。Olfati-Saber 原始论文中的形式是φ(z) 0.5 * ((a b) * σ1(z c) (a - b))其中a、b分别控制排斥和吸引强度。工程实现里更常见的是按距离分段定义这个项目的phi_func.m如果直接打开看不到完整实现按下面的形式补全就能跑通function phi_val phi_func(z, h, eps) % phi_func.m: 计算势场函数值 % z: sigma范数对应的输入值 % h: 0到1之间的队形参数控制稳态间距 % eps: sigma范数平滑参数 if nargin 3 eps 0.1; end d sigma_norm(z, eps); r 5; R 1.2 * r; % 距离小于 h 时势场值为正产生排斥 if d h phi_val (d - h) / sqrt(1 eps * (d - h)^2); else % 在 h 和 R 之间形成吸引区域 phi_val 0.5 * ((d - h) / sqrt(1 eps * (d - h)^2) 1) * ... (1 - bump_func(d, h, R)); end end这个实现里d h时势场值为正对应排斥d超过h后势场值开始变负对应吸引bump_func的权重在接近R时把吸引作用平滑收掉。编队的稳态间距既不是r也不是R而是 φ 函数的零点附近。h可以理解为稳态距离的归一化参考点改变它就能改变整个队的松紧程度而不用动控制结构。3.2 h 对队形密度的影响参数h的取值范围是 0 到 1。它直接改变 φ 函数的零点h越大稳态距离越接近感知半径r编队摊开h越小UUV 向中心收拢编队变紧凑。实际调参时可以参考下面这组经验值h 取值队形状态稳态间距大致位置适用场景0.2紧凑防守队形接近 0.2r狭窄水道通过、隐蔽接近0.5标准作业队形约 0.5r常规巡逻、海底地形测绘0.9疏松展开队形接近 r大范围搜索、通信中继改队形不需要重新设计控制器只需要在运行中改h。我一般在仿真中段把h从 0.9 切到 0.5观察编队是否从展开状态平稳收缩。如果切换过程中出现 UUV 之间的大幅速度波动说明h变化过快控制器来不及响应。此时应给h加一个斜坡让它用 5 秒左右渐变到目标值而不是一步跳变。这个渐变操作在工程上比任何硬切换都稳。3.3 sigma_norm_gradient让势场力有方向φ 函数只给出势场的大小力的方向要靠梯度。sigma_norm_gradient.m计算的就是 σ-范数的梯度向量function g sigma_norm_gradient(z, eps) % sigma_norm_gradient.m: 计算sigma范数的梯度 % 输入相对位置向量输出同维度方向向量 if nargin 2 eps 0.1; end g z ./ sqrt(1 eps * sum(z.^2)); end这个梯度的模长不会超过 1方向始终指向邻居天然适合做单位方向向量。完整的势场力是三者相乘phi_func(d) * bump_func(d) * sigma_norm_gradient(z)φ 给力的大小bump 限制作用范围梯度给方向。调试中如果看到 UUV 乱飞先查这一行很多人会把phi_func的结果直接当控制量忘记乘梯度排斥力方向就全乱了。4. action_function 与三大行为合成速度一致性是怎么实现的4.1 三规则的控制力分解action_function.m是整套源码的核心聚合点。它把前面所有函数揉进一个控制律输入所有 UUV 的位置矩阵q、速度矩阵v、目标位置输出加速度指令。完整的 Olfati-Saber 控制律包含梯度项、速度一致项、导航项三项function u action_function(q, v, q_target, params) % action_function.m: 汇总梯度项、速度一致项、导航项 % q: Nx2 位置矩阵 % v: Nx2 速度矩阵 % q_target: Nx2 目标位置或虚拟leader位置 eps params.eps; r params.r; R params.R; h params.h; c1 params.c1; c2 params.c2; dt params.dt; N size(q, 1); u zeros(N, 2); for i 1:N grad_sum zeros(1, 2); vel_sum zeros(1, 2); for j 1:N if i j, continue; end z q(j, :) - q(i, :); d sigma_norm(z, eps); w bump_func(d, r, R); if w 0 % 梯度项势场的吸引与排斥 grad_sum grad_sum ... phi_func(d, h, eps) * w * sigma_norm_gradient(z, eps); % 速度一致项与邻居的速度差 vel_sum vel_sum w * (v(j, :) - v(i, :)); end end % 导航项把整个群拉向目标 nav c1 * (q_target(i, :) - q(i, :)) c2 * (0 - v(i, :)); u(i, :) grad_sum vel_sum nav; end end梯度项负责编队内部几何关系速度一致项让所有 UUV 的速度逐渐收敛到同一数值导航项给整个群一个共同运动趋势。三者缺一不可没有梯度项UUV 会撞在一起没有速度一致项编队前后拉锯队形看起来像在抖动没有导航项群体只会停留在初始位置附近漂移。c1和c2是导航项的位置增益和速度增益。c1太大UUV 会向目标点硬冲编队形状在途中被压缩c2太小群体会在目标点附近来回穿越。一组稳妥的起步参数是c1 0.5、c2 1.5再根据速度曲线微调。4.2 主循环和限幅别让控制量直接冲散仿真有了单步控制量主循环就是标准数值积分。这个包没有把主脚本放在文件列表里但根据现有函数结构最常见的组织方式是先建一个params结构体再按时间步迭代% run_flocking.m dt 0.02; T_final 100; N 6; q 20 * rand(N, 2); % 随机初始位置 v randn(N, 2) * 0.1; % 小初始速度 params struct(eps,0.1,r,5,R,6,h,0.5,... c1,0.5,c2,1.5,dt,dt); for t 0:dt:T_final u action_function(q, v, q_target, params); v v u * dt; v limit(v, 2.0); % 限制线速度 q q v * dt; gap get_gap(q, q_target); % 记录编队间隙 endlimit.m在这里负责速度限幅把每个 UUV 的速度向量长度限制在最大线速度内。UUV 推进器有物理上限仿真里可以完全不管但加上限幅能避免导航项瞬时增益过高导致速度爆炸。get_gap.m返回当前队形与期望队形之间的平均位置误差跑完仿真后画出gap曲线能直接看到收敛过程和波动幅度。c1、c2的调参规律可以归纳成一个小的经验表现象增益问题调整方向编队到目标点附近来回摆动c2偏小加大c2例如从 1.5 提到 2.5编队形状在移动中被压扁c1偏大减小c1例如从 0.5 降到 0.2群整体收敛太慢两个增益都偏小同时提高c1和c2步长取 0.1这种表比盲试参数有用因为问题现象和目标往往混在一起先固定一组再动另一组才能分清因果。4.3 theta_func角度门控的真实用途theta_func.m在文件列表里容易被当成冗余函数但它在 UUV 场景里很关键。它计算的是相对速度方向和位置连线方向之间的夹角用来判断一个邻居是从正面接近还是从侧面飘近。水下 UUV 转弯半径大如果排斥力对所有方向一视同仁正后方高速接近的 UUV 直到很近才会被感知等势场反应过来已经晚了。我习惯在action_function里给梯度项乘一个由theta_func生成的权重对后方接近的邻居加大排斥对前方同向的邻居保持正常响应。这个改动不会破坏理论上的收敛性但仿真轨迹会更符合真实平台的运动学约束。5. 邻接矩阵诊断与队形切换的实战技巧5.1 用 get_gap 曲线判断编队是否收敛跑完仿真第一件事不是看轨迹动画而是看gap时间序列。理想曲线的特征是初期快速下降然后小幅度振荡最后稳定在一个稳态值附近。如果曲线一直不下降先查c1是否太小导航项压不住群内交互如果曲线在下降过程中出现周期性尖峰多半是某个 UUV 的邻居数量突然变化说明R设置得太接近r邻接矩阵频繁通断。把R从1.2r提到1.4r尖峰通常会被明显平滑掉。5.2 队形切换动态修改 h 与领导关系queue.m和check.m配合能做更复杂的实验指定某个 UUV 为 leader其余为 follower。此时q_target不再是固定坐标而是 leader 实时位置加偏移。切换队形时可以这样写% 前50秒展开搜索队形 params.h 0.9; % 第50秒开始用5秒斜坡过渡到0.5 for t 50:0.1:55 params.h 0.9 - (t - 50) * (0.4 / 5); end % 第55秒之后 h 稳定在 0.5完整实现不复杂把 leader 的速度作为导航项的期望速度v_r把 leader 位置加固定偏移作为 follower 的目标位置。切换时只改h不改控制结构这比重新设计队形控制器可维护得多。队形旋转是另一个维度需要对q_target做旋转矩阵变换这个包里没直接提供但把变换结果赋值给q_target即可接入。5.3 三个常见数值问题的定位顺序第一出现NaN。最常见原因是bump_func里R r导致分母为 0其次是sigma_norm的eps传成 0。在两个函数的入口处各打印一次参数通常立刻能定位。第二速度持续发散。先降dt欧拉积分下dt超过 0.05 时反馈延迟会叠加在速度一致项上引发振荡降到 0.01 一般能解决。第三编队整体旋转。检查sigma_norm_gradient里是否把相对位置归一化成单位方向如果直接用 φ 函数值充当梯度向量的模旋转力矩会偏大。最后再说一句文件列表里的untitled.bmp它只是运行结果图或截屏参考不是必要输入别把它当成数据文件读进仿真。本文还有配套的精品资源点击获取
返回列表