水下机器人调试的时候,最折磨人的往往不是机械结构,而是你在岸上把参数调得漂漂亮亮,一下水就发现完全换了一套脾气。我之前在某高校实验室做无人潜航器的控制仿真时就有切身体会:最开始用PID做深度控制,实验室理想环境下表现还行,可一旦把非线性水动力项和外部流干扰加进模型,PID那组参数就明显压不住了。后来把控制器换成滑模控制(SMC),从Matlab和Simulink仿真一路做到半实物测试,一套控制结构管到底。今天这篇就把这套AUV滑模控制器的设计、建模和仿真实现思路完整拆一遍,给正在做水下机器人控制、想用Simulink快速验证SMC算法的读者一份能直接复现的参考。
这篇内容你能拿到的东西很明确:一个可以动手跑通的AUV垂直面深度控制仿真模型,一套从误差状态到滑模面再到控制律的完整推导流程,加上我当时在仿真里遇到的各类奇奇怪怪的问题和对应排查思路。不是纯理论罗列,是那种写完代码、跑完仿真、被波形坑过之后才总结出来的东西。
1. AUV建模的一笔账:要知道自己在控制一个什么样的系统
1.1 别急着上六自由度,按控制目标砍维度
水下机器人的完整运动模型是六自由度,三个平动加三个转动,光是整理惯性矩阵、科氏力矩阵、阻尼矩阵,就能写满好几页推导。如果一上来就把六自由度模型塞进Simulink,控制器还没写,光参数校对就能耗掉两三天。工程上更常见的做法是:按照控制任务把自由度砍掉,先在一个平面或一条轴向上把控制器逻辑验证清楚。
我做的是垂直面深度控制,所以状态只保留垂向位置z和垂向速度w,水平面的航向和位置默认由另一套控制器稳住。简化后的状态方程长这样:
- x1 = z,垂向位置;
- x2 = w,垂向速度;
- x1_dot = x2;
- m·x2_dot = τ + Δ - c1·x2 - c2·|x2|·x2。
其中τ是垂向推力,Δ是净浮力与重力之差,c1是线性阻尼系数,c2是平方阻尼系数。这个模型不算精密,但足够支撑控制器设计,也方便在Simulink里用积分器直接搭出来。
为什么我坚持保留|c2·x2|·x2那个平方阻尼项?因为水下机器人在速度稍微起来之后,水动力阻尼明显不再线性。实际测试里,正是这个非线性阻尼会让按线性模型设计的PID控制出现持续振荡。我第一次建模图省事,把阻尼项全部按线性近似,仿真出来的深度响应曲线和后面实机记录的数据对不上,后来把平方阻尼项加回去,曲线走势才基本吻合。
1.2 模型参数的不确定性才是SMC上场的原因
模型参数大体可以分三类:质量、排水体积、净浮力,这些通过称重和体积测量就能标定出来,精度很高;阻尼系数c1和c2,通常靠CFD估算加约束水槽试验获取,误差30%都很正常;剩下的就是海流、波浪这类时变外扰,根本没法精确建模。
参数有误差这件事本身,正是SMC能站住脚的根本原因。SMC的控制律里,模型参数只用于估计等效项,未建模动态和外扰只要其边界已知,切换项就能强行把它们抵消掉。也就是说,阻尼系数估算偏了,只要偏差在一定范围内,控制器照样能完成跟踪任务,只是代价是控制量抖振会略微变大。
在Simulink里做模型,我建议把已知参数统一放进一个结构体变量。比如param.m = 180、param.rho_g_V = 9800、param.c1 = 28、param.c2 = 40,后面加干扰往结构体里加字段就行。这样参数改动不会散落在各个模块里,调完一组参数也方便留档,换工况时直接加载不同的结构体即可。
提示:建模这一步花的时间,基本占整个项目的一半。模型和实体之间的偏差没搞清楚之前,控制器设计得再漂亮,放进仿真里也就是一场漂亮的空转。
2. 滑模控制器设计的推导过程:从误差状态到控制律
2.1 滑模面是什么,为什么要引入它
深度控制的本质是让z跟踪期望深度z_d。定义深度误差e = z_d - z,速度误差de = dz_d - w。如果直接对这些误差做比例微分控制,产出就是一个带前馈的PD控制器,抗模型失配的能力很有限。SMC换了一个思路:构造一个误差状态空间中的“滑模面”,让系统状态先被吸引到滑模面上,再沿着滑模面滑向平衡点。
最常用的一阶滑模面写出来就是:
s = de + λ·e
λ是一个正的设计常数,决定系统在滑模面上的收敛速度。这个式子值得多看两眼:当s恒等于0时,de = -λe,误差会以指数速度收敛到零。也就是说,把原来二阶误差系统的控制问题,转换成了如何让s快速归零并保持为0的问题。这里的s可以理解成一座桥,把高维误差动态降成一阶动态,之后所有控制精力都放在“把s推向零”这件事上。
2.2 等效控制加切换控制:一套控制律打天下的底气
要推导控制律,就对s求导。把前面AUV模型的动力学方程代入,并加入期望加速度项,可以整理出如下控制律:
τ = m·(d²z_d + λ·de + k·s + ε·tanh(s/φ)) + c1·w + c2·|w|·w - Δ
这个式子分两部分看。前一部分是等效控制,包含期望加速度前馈、滑模面误差修正和趋近项,作用是把系统状态稳定地引导到滑模面上;后一部分是对非线性动态的补偿,把阻尼力和浮力重力差直接抵消掉。切换项ε·tanh(s/φ)则专门用来对抗模型不确定性和外部扰动,其中ε要大于扰动上界,才能保证系统状态被“拽”到滑模面上。
这里有一个理论前提必须强调:切换项的幅值必须大于外部扰动和模型不确定性的上界之和,否则到达滑模面的条件不成立,系统会一直徘徊在滑模面附近无法收敛。但ε又不能取得太大,否则控制量抖振剧烈,执行器寿命和功耗都受影响。工程上常见的做法是在2到3倍扰动上界之间先保守取值,跑到仿真里观察响应再逐步往下压。
2.3 符号函数换成平滑近似:抖振问题在源头上的第一个解法
我第一次实现SMC时直接用符号函数sgn(s),仿真波形总算能跑通,但把控制输入τ的曲线拉出来一看,高频振荡非常夸张。这就是SMC出了名的抖振问题。抖振的根源在于符号函数在滑模面附近产生理想的高频切换,控制器输出在正值和负值之间反复跳跃。仿真里看波形只是不好看,放进实物里执行器根本扛不住,传动机构也会被迫高频磨损。
处理抖振最直接的修改,就是把sgn(s)换成饱和函数或者tanh函数。具体替换为:
ε·sgn(s) → ε·tanh(s/φ)
其中φ是边界层厚度。φ取很小的值时,tanh可以近似符号函数;φ取较大值时,切换项变得更平滑,抖振减弱但抗扰性也下降。我个人更偏好tanh,因为它的曲线连续可导,在边界层内没有转折点,对控制输出的平滑性更好。饱和函数sat(s/φ)在边界层边界处有个折角,虽然也能用,但生成的控制信号不如tanh自然。
以我前面那个简化AUV模型为例,初始参数大致可以取λ=0.5、k=5、ε=2.5、φ=0.5。需要注意这个组合只对那个模型有效。不同质量和水动力条件下的AUV,参数差异非常大。大型UUV和微型ROV之间,质量和阻尼系数甚至可以差出两三个数量级,照抄参数没有任何意义,理解每个参数的作用才是关键。
3. Simulink仿真架构搭建:从零到能跑通模型
3.1 顶层模块怎么切分
整个仿真模型在顶层可以分成四块:信号源、控制器、被控对象、数据记录。信号源负责生成期望深度z_d以及它的一阶、二阶导数;控制器根据状态误差算出控制量τ;被控对象就是AUV动力学模型,输入控制力和干扰,输出速度和位置;数据记录包括Scope示波器和To Workspace模块,把关键信号写进工作区供后续分析。
模块之间的信号线我一定建议命名,或者在连线上用Goto/Local标签方式管理。刚开始贪图省事全部用直连线,模块一多之后,根本分不清哪根线是期望速度、哪根线是反馈深度,排查问题的时候只能一根根往回捋,非常痛苦。命名信号线之后,双击连接线就能看到信号含义,排查效率能提高一半。
3.2 用MATLAB Function块实现SMC控制器
在Simulink里写SMC控制器,最直接的方式是放一个MATLAB Function块,把控制律完整地写成一个函数。下面是我当时用的核心代码,模型参数从函数输入端传入,便于统一整定:
function tau = smc_controller(z_d, dz_d, ddz_d, z, w, lambda, k, epsp, phi, m, c1, c2, delta) e = z_d - z; de = dz_d - w; s = de + lambda * e; sat_s = tanh(s / phi); f_estimate = c1*w + c2*abs(w)*w - delta; tau = m * (ddz_d + lambda*de + k*s + epsp*sat_s) + f_estimate; end这个函数基本就是2.2节控制律的代码化。特别提醒一下正负号问题:tau的符号取决于AUV模型的推力方向约定,我调试时因为正负号没对齐,出现过深度往反方向跑的情况。如果你复现时发现曲线方向不对,最先检查的就是模型里τ的正方向和状态方程里的符号约定是否一致。
还有一个实操细节:期望深度的一阶、二阶导数,不要用Simulink的Derivative模块对期望深度信号求导。Derivative模块本质上是数值微分,信号稍微有点噪声,导数曲线就会出现毛刺,进而污染控制量。更干净的做法是在信号源里直接解析构造导数,比如用Ramp和Step组合出梯形速度剖面,再解析求导得到期望加速度,根源上消掉数值噪声。
3.3 被控对象:两个积分器就能搭出AUV模型
被控对象建得越复杂,仿真越容易被数值问题干扰而掩盖控制算法的真实表现。我建议先用两个积分器串联搭简化模型,逻辑非常直观:
- 计算总加速度:acc = (τ - c1·w - c2·|w|·w + Δ + d) / m;
- 加速度经过第一个积分器得到速度w;
- 速度再经过第二个积分器得到位置z;
- 状态反馈回控制器,构成闭环。
注意干扰d一定要加在被控对象模型的加法器里,而不是加到控制器前馈路径上,这样控制器并不知道干扰的精确值,才能在仿真中真正检验SMC的抗扰能力。如果干扰提前告知了控制器,那仿真结果就只是自欺欺人。
搭建积分器模型的时候,最好把m、c1、c2、delta这些参数做成外部输入,而不是写死在Gain模块的数值里。这样后面参数整定直接改工作区变量或封装层参数就行,免去每次打开模型找模块改数的麻烦。
3.4 求解器配置:固定步长和变步长怎么选
Simulink求解器配置是很多人容易忽略的坑。连续模型默认用变步长ode45,对多数系统没问题,但SMC中含有快速切换机制,即使做了tanh平滑,切换动作也比较陡。我实际测试下来,变步长求解器会自动缩小步长来捕捉快速动态,仿真速度会明显下降,而且个别工况下还会出现数值刚性导致的震荡。更稳妥的方案是先用变步长ode45快速验证算法逻辑,等参数基本确定后切到固定步长求解器,步长取1e-3秒或更小,做最终的性能评估。
步长选择有个简单的权衡逻辑:步长太大,快速切换项可能被跨过,导致仿真结果失真;步长太小,仿真时间成倍拉长,调参效率低。我一般先用1e-3秒做日常调参,最后评估阶段再用1e-4秒跑一组最终数据归档。
3.5 示波器里该看什么信号
很多人做SMC仿真只盯深度跟踪曲线,看着曲线贴上了就觉得算法有效。实际上,真正判断SMC控制器工作状态的信号是滑模面s。建议同时观测这五路信号:
- z和z_d的跟踪曲线,判断最终控制精度;
- 速度w曲线,判断运动状态是否合理;
- 控制输入τ曲线,判断执行器负载是否可接受;
- 滑模面s曲线,这是最核心的指标;
- 切换项输出曲线,判断抖振程度是否可控。
判断逻辑非常直接:如果s曲线没有收敛到零附近,说明切换项强度不够,系统根本没进入滑模运动;如果s能在零附近,但围绕零的震荡幅度很大,说明边界层太窄或ε过大。先看清楚了s的形态再动参数,比盯着深度曲线瞎猜高效得多。
4. 仿真调试的真实过程:发散、震荡和参数整定思路
4.1 现象一:一运行就发散,控制量直接爆表
我完成第一版搭建后,最常遇到的情况就是仿真一启动,控制量瞬间冲到几百,深度曲线直接飞掉。这个问题的排查经验可以沉淀成一张表:
| 现象 | 可能原因 | 排查手段 | 解决办法 |
|---|---|---|---|
| 深度朝反方向跑 | 控制量符号约定和模型方向不一致 | 断开控制器,用固定正推力开环看z变化方向 | 检查τ的正方向和状态方程符号,统一约定 |
| 控制量瞬间爆表 | 滑模面初始值太大,切换项饱和 | 观察s曲线初值 | 在控制器输出端加饱和限幅,或设计状态约束的控制目标 |
| 深度发散但控制量正常 | 期望加速度前馈缺失或错误 | 检查dz_d和ddz_d是否由信号源正常提供 | 补上解析导数前馈 |
| 响应曲线振荡且发散 | 正反馈回路,反馈信号极性接反 | 检查反馈回路的加减法连接点 | 确认误差定义和反馈极性一致 |
排查顺序我总结成了三步:先看s曲线,再看控制量,最后看深度。很多发散问题只要看s就明白了,比如s在高频震荡且幅值不衰减,说明切换项不够或者趋近律参数不合适;如果s直接单调增大,说明符号方向搞反了,或者期望导数的符号有问题。先看s,能帮你在前两步定位到大部分问题。
4.2 现象二:能跟踪但抖振明显,执行器曲线上全是毛刺
在参数粗调合格后,最常见的问题就是抖振。切换项里的tanh虽然平滑了符号函数的突变,但边界层φ取得太小,控制量依然会有明显的高频分量。处理抖振我一般按照三个方向依次尝试,不必一上来就大改参数:
第一步,观察φ是否过小。把φ从0.1往0.5方向调,看τ曲线的毛刺是否明显减少。要注意此操作会让稳态误差略微增大,属于权衡。第二步,适当减小ε。如果当前ε已经超过实际干扰的2倍以上,就可以逐步下调。第三步,考虑切换项改为自适应边界层,让φ随|s|的大小动态变化:|s|大时φ大,平滑趋近;|s|小时φ小,保持精度。
还有一种更“曲线救国”的办法,是把趋近律换成指数趋近律的改进形式,比如加一个-η·s项让趋近速度随s幅值自动变化,本质上就是增加线性趋近项来减少切换频率。这个改法在仿真中效果明显,但设计工作量也会增加,适合对SMC有一定理解之后再尝试。
4.3 调参顺序和效果对比
SMC参数整定的顺序非常关键,我自己的经验是:先定λ,再定k,最后定ε和φ。λ决定滑模面的斜率和误差收敛速度,如果λ太大,期望加速度前馈会放大,执行器容易饱和;λ太小,系统响应慢。定住λ后再调k,k控制s趋近零的速度,相当于把系统“推向滑模面”的力度。ε和φ放在最后,是因为它们直接影响稳态性能和抖振幅值,依赖于前两个参数已经稳定。
下面是我在简化模型上做的一组典型调参对照:
| 参数调整 | 响应变化 | 注意点 |
|---|---|---|
| λ增大 | 收敛加快,深度响应变灵敏 | 过大会引发执行器饱和和超调 |
| k增大 | s更快归零,到达滑模面的时间缩短 | 过大会让控制量初期偏大 |
| ε增大 | 抗扰能力增强,稳态误差减小 | 抖振同步增强,要监控控制量 |
| φ增大 | 抖振减弱,控制量平滑 | 稳态精度下降,跟踪误差变大 |
可以把λ想成“误差收敛的阶梯”,k想成“把s拽回零的绞盘”,ε则是“对抗外力的保险丝”。调完一轮之后不要急着改下一组,先把波形存下来,最好是叠加对比图。只凭肉眼目测很难判断前面那组参数到底差在哪里,但把新旧曲线叠加之后,超调量、调节时间、稳态误差差多少一目了然。
5. 从理想仿真走向半实物验证:干扰、离散化和执行器约束
5.1 把干扰造得真实一些
基础仿真里的干扰通常就是一个阶跃或者正弦波。这个测试太温柔了,实际水下环境中的干扰是低频浪涌加高频噪声叠加出来的。我一直推荐给模型加组合干扰:
d = 500·sin(0.2·t) + 80·randn
第一项模拟慢变海流,第二项模拟波动与传感器噪声。组合干扰会让SMC的切换项始终处于动态对抗中,能够检验控制器在不同干扰频段下的表现。如果在这种干扰下深度跟踪误差还能保持在允许范围,再去面对真实水域时心里才有底。
另外还要考虑执行器饱和。AUV推进器或舵面能提供的力是有限的,控制律算出来的τ被限幅之后,实际上是一个非线性环节。把Saturation模块加在控制器输出和被控对象输入之间,再重新跑一遍,你会发现参数可能要整体回调,因为饱和限幅等效于减小了系统增益,原来的λ和k在饱和边缘工作时容易诱发极限环振荡。执行器变化率约束也可以通过Rate Limiter模块加进去,模拟电调响应速度,对防止实际作动器损坏很有帮助。
5.2 连续控制律的离散化处理
Simulink里默认的连续控制器,到了嵌入式环境必须离散化。最简单的做法是采用前向欧拉法,把每一拍的控制计算改成:
s_k = de_k + λ·e_k
τ_k = m·(ddz_d,k + λ·de_k + k·s_k + ε·tanh(s_k/φ)) + c1·w_k + c2·|w_k|·w_k - Δ
这里的采样周期T_s要和实际控制器的控制周期一致。T_s太大会导致离散化误差放大,切换项在滑模面附近可能来回穿越;T_s太小又给执行器带来不必要的计算负担。我一般先用T_s=0.02秒做离散化仿真,和连续系统对比结果,再逐步调整。离散化之后,之前连续仿真里看不到的离散抖振现象会显现出来,这是正常的。
这里有一个容易被忽略的坑:连续系统仿真中φ取0.5没问题,离散化后同一组参数在20毫秒采样周期下可能震荡明显。原因是离散化降低了系统对滑模面附近动态的感知精度,边界层厚度的有效作用变小了。遇到这种情况,适当放大φ,相当于用边界层把离散误差也包住,是最简单有效的对策。
5.3 仿真结论怎么约束实测预期
仿真验证只能证明算法原理和参数方向是对的,不能证明参数可以直接搬到实物。真实AUV的阻尼系数、海流强度、传感器延迟都和仿真模型有偏差,实测时几乎注定要重新调一遍参数。我能给的实操建议是:仿真中不要把参数整定到极限状态,留出20%到30%的裕量。比如最优ε在2.5附近,那实测就从3.0起步往回调;φ也稍取大一点,先保证执行器不抖振,再逐步收紧精度。这样能让从仿真到水下测试的跨度小很多。
在半实物验证环节,我的习惯是先用固定深度阶跃测试最简工况,确认系统方向正确之后,再切换到梯形深度剖面测试跟踪性能,最后才上正弦轨迹加干扰的复杂工况。分级测试的好处是,一旦出问题,能直接定位到是控制器逻辑、执行器响应还是干扰模型出了问题,而不是所有问题混在一起无从下手。
我个人在做这个项目时最大的体会是,SMC给AUV控制带来的不是某组神奇的参数,而是一种设计思路:先定义误差收敛方式,再用切换项兜住所有模型误差和外部扰动。这个思路在仿真环境中验证起来非常快,因为Simulink天然适合这种控制器与被控对象分离的架构。如果你准备在自己的AUV项目里尝试SMC,我的建议是从最简模型开始,先跑通一条深度控制链路,确认滑模面信号在示波器上乖乖归零之后,再一步步增加自由度、干扰和离散化复杂度。每一步只引入一个新变量,排查起来才不会手忙脚乱。