做欠驱动AUV水平轨迹跟踪控制这几个月,我把大部分时间花在了同一件事上:盯着Simulink里的误差曲线,思考为什么控制器改得越来越“花哨”,轨迹却总在某个时刻跳一下或者拖一条长尾巴。后来问题逐渐清晰——不是控制思想的问题,而是趋近阶段、积分项和扰动补偿之间的关系没有理顺。这篇博文记录我用全局积分滑模完成欠驱动AUV水平面轨迹跟踪控制的全过程,覆盖模型建立、误差坐标变换、控制器推导、Simulink仿真实现和参数整定经验。文中的思路和Matlab代码框架都可以直接搬走,适合正在做水下机器人运动控制的研究生、工程师,以及想快速跑通一个非线性控制仿真验证的同学。
1. 欠驱动AUV水平面跟踪难在哪,为什么滑模总能有一席之地
1.1 水平面模型天然“欠”了一个控制输入
先说清楚“欠驱动”这个三个字在实际仿真里意味着什么。一个水平面运动的AUV,如果不考虑垂荡、横摇、纵摇,通常只保留纵荡速度u、横荡速度v和艏摇角速度r三个自由度,对应的位置状态是北向位置x、东向位置y和艏向角ψ。但执行机构往往只有尾部螺旋桨产生的纵向推力τ_u,以及舵或艉推进器产生的偏航力矩τ_r。横荡方向没有任何直接推力。
这就像开一辆只有油门和方向盘的车,却没有侧向手刹。横荡速度v只能靠水动力、耦合效应和艏摇间接去影响,不能直接命令它等于某个值。这个限制带来一个很硬的问题:系统是三个自由度、两个控制输入,控制方向数量少于被控动态数量,非线性系统中的一大类成熟方法都不能直接套。
很多初学者刚接触这个话题时会问:既然少一个输入,那我把模型降成两个自由度不就行了?实际上不行,因为横荡速度v在转弯过程中会持续存在,而且与纵荡、艏摇之间存在强烈的科氏耦合项。忽略v,仿真结果会很漂亮,但拿到真实水池里基本没法用。所以研究欠驱动控制的第一课,就是老老实实把v当作一个可观测但不可直接控的状态,让它参与动力学,但不奢望给它分配一个控制力。
1.2 传统PID和反步法的无力感
我见过不少项目一开始都用PID。PID在天真的线性仿真里确实能跑,但一旦把轨迹设成大幅转向的圆形或者S形,问题立刻暴露:PID的抗扰动能力来自积分项,但欠驱动AUV的纵荡—横荡—艏摇耦合是非线性的,PID参数在某个航速下调节好,换个航速或换一组海况参数就失效。
反步法也是这个方向的高频选择。反步法的逻辑是把系统写成严格反馈形式,一层一层设计虚拟控制量。但欠驱动AUV的误差动力学中,横向误差要借助艏摇角才能间接控制,这导致虚拟艏摇角的设计比较绕,而且反步法需要对虚拟控制量反复求导,表达式复杂,在Simulink里实现时符号运算很痛苦,稍微改一下水动力模型就要重新推导半天。
滑模控制在这类问题上强在哪?它对“模型参数不准”和“外部扰动”天然不敏感。因为滑模控制的本质是设计一个滑模面,然后强行让系统状态沿着这个面滑行,一旦进入滑动模态,系统动态就由滑模面决定,而不是由原系统的精确参数决定。对欠驱动AUV这种模型不确定性大、外部水流扰动随机的对象,这个鲁棒性非常珍贵。
1.3 全局积分滑模要解决的三个问题
经典滑模控制也有几个众所周知的毛病,我这几个月最大的体会就是:论文里常写“设计滑模面、取趋近律、证明稳定”,但真正跑仿真时,问题全出在这些容易被一句话带过的地方。
第一个问题是趋近阶段。系统从初始状态到滑模面之间有一段趋近过程,这段时间系统还没进入滑动模态,对扰动和参数摄动的抵抗能力跟普通反馈控制差不多。初始误差越大,趋近阶段越长,鲁棒性就越差。
第二个问题是稳态误差。如果只使用常规滑模面,恒值扰动或模型偏差会导致稳态误差无法完全消除,尤其是横向位置误差e_y,因为横荡方向没有直接控制力,误差收敛完全依赖偏航运动和耦合,一点点残余误差都很明显。
第三个问题是抖振。理想滑模控制中的符号函数sgn(s)会让控制输入高频切换,实际中执行机构吃不消,仿真里也会出现严重的数值振荡。解决抖振需要用饱和函数或者双曲正切替代符号函数,但又会牺牲一定的鲁棒性。
全局积分滑模的思路,就是用一条带积分项的滑模面,加上一个衰减的辅助函数F(t),让初始时刻的滑模函数值恰好等于零。这样一来系统从一开始就落在滑模面上,没有趋近阶段;积分项把常值扰动引起的稳态误差压掉;趋近律中再用饱和函数取代符号函数,抖振被限制在可接受范围。接下来就按这个思路一步步展开。
2. 模型和误差方程:把轨迹跟踪问题翻译成数学语言
仿真要做得好,第一步不是写控制器,而是把模型从“论文符号”翻译成“Simulink里能积分的状态方程”。这一步如果偷懒,后面所有调节参数的经验都白搭。
2.1 坐标系约定与三自由度运动学/动力学
使用两个坐标系是水下机器人运动控制的标准做法:大地惯性坐标系用来描述位置和艏向,附体坐标系固连在AUV上,用来描述速度。水平面运动学方程写为:
[ \begin{aligned} \dot{x} &= u \cos \psi - v \sin \psi \ \dot{y} &= u \sin \psi + v \cos \psi \ \dot{\psi} &= r \end{aligned} ]
这里u是前向速度,v是横荡速度,r是艏摇角速度。动力学模型采用常规的三自由度简化形式:
[ \begin{aligned} m_{11}\dot{u} &= m_{22} v r - d_{11} u + \tau_u + \tau_{wu} \ m_{22}\dot{v} &= -m_{11} u r - d_{22} v + \tau_{wv} \ m_{33}\dot{r} &= (m_{11}-m_{22}) u v - d_{33} r + \tau_r + \tau_{wr} \end{aligned} ]
其中m11、m22、m33是包含附加质量的惯性系数,d11、d22、d33是一阶线性阻尼系数,τ_wu、τ_wv、τ_wr是外界水流等产生的干扰力和力矩。这个模型里已经包含了欠驱动的全部特征:第二行没有控制输入,横荡速度v只能被耦合项和阻尼项推动。
我仿真用的是一组接近某小型试验AUV的示例参数:m11=25 kg,m22=32 kg,m33=4.5 kg·m²,d11=6.5 N·s/m,d22=12 N·s/m,d33=8.5 N·m·s/rad。这里m22大于m11,是因为横荡方向附加质量通常大于纵荡方向。实际调试时不需要完全精确,关键是量级别差太远,否则参数整定经验就没有参考价值。
2.2 基于参考航向的误差坐标变换
轨迹跟踪的目标是让实际位置(x, y)跟踪参考轨迹(x_d(t), y_d(t))。直接拿(x-x_d, y-y_d)当误差也能设计控制器,但欠驱动系统下,横向误差的收敛主要靠艏摇来间接实现,所以更合理的做法是把误差投影到参考航向坐标系。
定义参考轨迹对应的速度大小U_d和航向角ψ_d。对于一条给定的参考轨迹,可以计算:
[ U_d = \sqrt{\dot{x}_d^2 + \dot{y}_d^2}, \quad \psi_d = \operatorname{atan2}(\dot{y}_d, \dot{x}_d) ]
然后把惯性系误差旋转到以ψ_d为基准的坐标系:
[ \begin{bmatrix} e_x \ e_y \end{bmatrix}
\begin{bmatrix} \cos\psi_d & \sin\psi_d \ -\sin\psi_d & \cos\psi_d \end{bmatrix} \begin{bmatrix} x - x_d \ y - y_d \end{bmatrix} ]
同时定义艏向误差ψ_e = ψ - ψ_d。对误差求导后得到一组很有用的误差动力学方程:
[ \begin{aligned} \dot{e}_x &= -U_d + u \cos\psi_e - v \sin\psi_e + r_d e_y \ \dot{e}_y &= u \sin\psi_e + v \cos\psi_e - r_d e_x \ \dot{\psi}_e &= r - r_d \end{aligned} ]
这组方程是我的整个控制器设计基础。e_x代表沿参考轨迹方向的前向位置偏差,e_y代表横向偏差,ψ_e负责把艏向行为关联进来。观察可以发现,e_y的导数里出现了u sinψ_e这一项,这意味着横向误差确实可以通过调整艏向角来间接收敛——这就是欠驱动AUV水平跟踪控制的核心机制。
参考轨迹取匀速圆周时,U_d和r_d都是常数,ψ_d随时间线性变化,计算非常方便。后面Simulink仿真里我用的就是这种轨迹,因为圆周运动既考核了稳态跟踪能力,又考核了持续转弯时横荡速度耦合的影响。
2.3 模型不确定性、外界扰动的仿真化处理
真实AUV受到的水流、缆绳扰动和未建模水动力一直存在。仿真如果不加扰动,控制器的鲁棒性就完全没有体现,后面换到实物时几乎必翻车。我加的是一个有界时变扰动:
[ \begin{aligned} \tau_{wu} &= 4 + 3\sin(0.2t) \ \tau_{wv} &= 2 + 1.5\sin(0.3t) \ \tau_{wr} &= 1.5 + \sin(0.4t) \end{aligned} ]
同时把模型参数m11、m22、d22等在前面的标称值基础上再人为偏移约10%,模拟参数辨识误差。这种常值偏置加周期性波动的扰动组合,最能检验积分滑模面是否真的能把稳态误差压下去。
这里要特别强调一个原则:仿真中扰动要和控制器设计时所用的扰动估计分开。控制器内部虽然会去估计并补偿扰动,但模型节点的输入必须使用真实扰动值,否则仿真就成了“自己拿答案照抄”,不具备参考价值。
3. 控制器设计:从全局滑模面到控制力/力矩表达式
控制器是整个项目的核心,也是计算量最集中的地方。我把设计过程拆成滑模面构造、控制律推导、工程可实现性三个步骤。
3.1 全局滑模面的构造思想
传统滑模面通常取误差及误差导数的线性组合,但这在这类速度级动力学模型中用起来不太顺手。更实用的做法是直接让速度误差参与滑模面,因为动力学方程里出现的是u和r的导数,控制量τ_u、τ_r可以很方便地解出来。
设计两个滑模面。第一个滑模面用于纵向推力通道,把前向速度误差u-U_d、沿轨迹方向误差e_x以及e_x的积分组合在一起:
[ s_1 = u - U_d + \alpha e_x + \beta_1 \int_0^t e_x d\tau - F_1(t) ]
第二个滑模面用于偏航力矩通道,把艏摇角速度误差、横向误差、艏向误差和横向误差积分组合在一起:
[ s_2 = r - r_d + k e_y + c \psi_e + \beta_2 \int_0^t e_y d\tau - F_2(t) ]
其中α、k、c、β1、β2都是待整定的正常数。F1(t)和F2(t)是全局滑模的关键辅助函数,取为:
[ F_1(t) = s_1(0) e^{-\lambda t}, \quad F_2(t) = s_2(0) e^{-\lambda t} ]
因为F1(0)=s1(0)、F2(0)=s2(0),代入滑模面表达式后立刻得到s1(0)=0、s2(0)=0。也就是说,在t=0时刻系统就已经处于滑动模态,不存在趋近阶段。随着时间推移,F(t)指数衰减到零,滑模面会变回普通的积分滑模面,而积分项把常值扰动引起的稳态误差逐渐吸收掉。
这个“全局”二字的实际价值,仿真里非常明显。普通滑模控制一开始需要把状态拉到滑模面,这个过程中误差收敛慢,而且受扰动影响大。换成全局滑模后,状态从第一步起就沿着滑模面运动,响应速度和扰动抑制能力都有提升。
3.2 控制律推导与趋近律选择
滑模控制的设计流程是:对滑模面求导,把动力学代入,然后指定趋近律来求解控制量。对s1求导:
[ \dot{s}_1 = \dot{u} - \dot{U}_d + \alpha \dot{e}_x + \beta_1 e_x - \dot{F}_1 ]
把动力学中的u一阶导代入,再令\dot{s}_1等于指定的趋近律表达式,就能解出τ_u。我用的趋近律是指数趋近律加饱和函数的变体:
[ \dot{s}_1 = -k_1 s_1 - \varepsilon_1 \operatorname{sat}\left(\frac{s_1}{\phi_1}\right) ]
其中sat是边界层宽度为φ的饱和函数,k1和ε1是正增益。写成这个形式有两个原因:第一,-k1s1项保证远离滑模面时快速回归;第二,饱和函数代替符号函数,在滑模面附近输出连续变化的控制量,避免抖振。最终解的纵向控制律为:
[ \begin{aligned} \tau_u =&, m_{11} \left[ \dot{U}d - \alpha \dot{e}x - \beta_1 e_x + \dot{F}1 - k_1 s_1 - \varepsilon_1 \operatorname{sat}\left(\frac{s_1}{\phi_1}\right) \right] \ &- m{22} v r + d{11} u - \hat{\tau}{wu} \end{aligned} ]
\hat{\tau}_{wu}是控制器对纵向水流干扰的估计值。偏航力矩通道的处理完全对称。对s2求导,代入偏航动力学,指定趋近律\dot{s}_2 = -k_2 s_2 - \varepsilon_2 \operatorname{sat}(s_2/\phi_2),得到:
[ \begin{aligned} \tau_r =&, m_{33} \left[ \dot{r}d - k \dot{e}y - c \dot{\psi}e - \beta_2 e_y + \dot{F}2 - k_2 s_2 - \varepsilon_2 \operatorname{sat}\left(\frac{s_2}{\phi_2}\right) \right] \ &- (m{11}-m{22}) u v + d{33} r - \hat{\tau}{wr} \end{aligned} ]
稳定性分析是常规操作。取Lyapunov候选函数V = 0.5s1² + 0.5s2²,代入趋近律后得到\dot{V} ≤ -k1s1² - k2s2² - ε1s1sat(s1/φ1) - ε2s2sat(s2/φ2)。饱和函数在边界层内部时,s·sat(s/φ)近似等于s²/φ,依然非负,所以\dot{V}整体不大于零,系统在边界层内最终一致有界。由于全局滑模保证s(0)=0,系数误差和扰动引起的偏差会被快速拉回滑模面。
3.3 为什么说这个控制律“能落地”
我自己评估控制器设计时有一条标准:实现时不能出现任何“在Simulink里根本搭不出来”的项。
这套控制律的所有组成部分都是可以实时计算的。位置和速度信号来自姿态传感器和DVL,e_x、e_y、ψ_e可以由参考轨迹和当前位姿算出,滑模面里的积分项用连续积分器累加,F(t)和它的导数都是简单指数函数,饱和函数在Simulink里就是现成模块。不需要对系统模型做精确求逆,也不涉及符号计算,离散化之后每个采样周期就是一组代数运算和一次状态更新。
唯一需要处理的是扰动估计\hat{\tau}{wu}和\hat{\tau}{wr}。仿真中为了验证方法本身,可以暂时假设干扰经低通滤波后已知;更接近实际的做法是用一阶扩张状态观测器估计。我在控制器模块里预留了扰动观测器接口,核心更新公式是:
[ \dot{\hat{\tau}}{wu} = L\left( m{11}\dot{u} - m_{22}vr + d_{11}u - \tau_u - \hat{\tau}_{wu} \right) ]
L是观测器增益,取10左右。这个观测器不需要额外传感器,只需纵向速度u的导数信息,而仿真里速度状态本身就在模型里,实现零额外成本。
4. Simulink搭建全流程:模型、控制器、参考轨迹三块分开做
很多仿真跑不起来,不是因为算法不对,而是Simulink框图结构不合理,到处是代数环和数据线交错。我的经验是分成四个模块,每个模块只做一件事,调试时逐块检查。
4.1 总体框架与数据流
整个仿真分为四个模块:参考轨迹与误差计算模块、控制器模块、AUV动力学模型模块、扰动与参数摄动模块。数据流是一条环路:参考轨迹模块计算出x_d、y_d、ψ_d、U_d、r_d,结合AUV输出的实际位姿算出e_x、e_y、ψ_e;控制器模块根据误差和速度状态计算出τ_u、τ_r;AUV动力学模型积分得到x、y、ψ、u、v、r;然后回到误差计算模块,形成闭环。
这种模块划分的意义在于,把非线性的AUV模型和控制器分开。你可以随意更换控制器对比算法,模型模块完全不用动;或者反过来,保持控制器不变,只改模型的阻尼参数,观察鲁棒性。实际调试时,如果某条曲线异常,先判断是模型层还是控制层的责任,效率高得多。
搭建时务必认真处理一个细节:滑模面里的积分项不要直接在控制器MATLAB Function里用memory方式累积,而应该用Simulink的Integrator模块,把积分器的输出作为控制器输入。否则在变步长求解器下,离散积分步长不稳定,曲线会出现莫名其妙的锯齿,而且抗积分饱和逻辑也不好加。
4.2 AUV动力学模型实现
AUV动力学模型我用Level-2 M-file S-Function实现。这种方式比MATLAB Function模块更贴近嵌入式实现,而且方便改动力学参数。核心代码框架如下:
function auv_model_sfun(block) setup(block); end function setup(block) block.NumInputPorts = 2; % tau_u, tau_r block.NumOutputPorts = 1; % [x, y, psi, u, v, r] block.InputPort(1).Dimensions = 1; block.InputPort(2).Dimensions = 1; block.OutputPort(1).Dimensions = 6; block.SampleTime = [0 0]; % continuous block.SetPreCompOutPortParam(false, true); block.RegBlockMethod('Outputs', @Output); block.RegBlockMethod('Derivatives', @Derivatives); endDerivatives回调函数里是模型的核心状态方程:
function Derivatives(block) x = block.ContStates.Data; % [x, y, psi, u, v, r] tau_u = block.InputPort(1).Data; tau_r = block.InputPort(2).Data; % 待辨识参数 m11 = 25; m22 = 32; m33 = 4.5; d11 = 6.5; d22 = 12; d33 = 8.5; % 外部扰动 tau_wu = 4 + 3*sin(0.2*block.CurrentTime); tau_wv = 2 + 1.5*sin(0.3*block.CurrentTime); tau_wr = 1.5 + 1*sin(0.4*block.CurrentTime); % 运动学 xdot = x(4)*cos(x(3)) - x(5)*sin(x(3)); ydot = x(4)*sin(x(3)) + x(5)*cos(x(3)); psidot = x(6); % 动力学 udot = (m22*x(5)*x(6) - d11*x(4) + tau_u + tau_wu)/m11; vdot = (-m11*x(4)*x(6) - d22*x(5) + tau_wv)/m22; rdot = ((m11-m22)*x(4)*x(5) - d33*x(6) + tau_r + tau_wr)/m33; block.Derivatives.Data = [xdot; ydot; psidot; udot; vdot; rdot]; endS-Function在Simulink中的配置比较固定:新建Model block,选择S-Function名称为auv_model_sfun,输入为两个端口,输出一个端口,维度6。连续状态个数在setup里通过block.NumContStates = 6指定。求解器选择ode45,最大步长建议设0.01s,否则非线性模型变步长积分容易出高频振荡。
4.3 参考轨迹与误差计算模块
参考轨迹我采用匀速圆周,因为圆周轨迹的U_d、r_d都是常数,能清楚看出控制器在稳态转弯时的表现。参考轨迹为:
function [ex, ey, psie, Ud, rd] = ref_errors(x, y, psi, t, R, omega) % 参考轨迹位置与速度 x_d = R*sin(omega*t); y_d = R*(1-cos(omega*t)); dx_d = R*omega*cos(omega*t); dy_d = R*omega*sin(omega*t); % 参考速度与航向 Ud = sqrt(dx_d^2 + dy_d^2); psi_d = atan2(dy_d, dx_d); rd = omega; % 旋转误差 dx = x - x_d; dy = y - y_d; ex = cos(psi_d)*dx + sin(psi_d)*dy; ey = -sin(psi_d)*dx + cos(psi_d)*dy; psie = psi - psi_d; % 注意把psie调整到[-pi, pi]区间 psie = atan2(sin(psie), cos(psie)); end角度误差wrap处理的细节经常被忽略。如果你不把ψ_e限制到[-π, π],滑模面中的cψ_e项会在角度跨过±π时产生一个巨大的突跳,控制器会以为艏向误差突然变成了6弧度,给出一大串控制输出,轨迹直接飞掉。这个问题我至少遇到三次,每次都以为是控制器参数问题,最后发现就是角度取模没有写。
4.4 控制器模块与关键参数设置
控制器模块我放在MATLAB Function中,输入为e_x、e_y、ψ_e、u、v、r、U_d、r_d、积分器状态z1、z2和辅助函数初值,输出为τ_u和τ_r。控制器的核心计算逻辑如下:
function [tau_u, tau_r] = smc_controller(ex, ey, psie, u, v, r, Ud, rd, z1, z2, F1, F2, dF1, dF2, t) % 控制器参数 alpha = 0.4; beta1 = 0.05; k = 0.3; c = 0.8; beta2 = 0.10; k1 = 1.0; eps1 = 0.2; phi1 = 0.02; k2 = 1.5; eps2 = 0.5; phi2 = 0.02; lambda = 1.0; % F衰减速率 m11 = 25; m22 = 32; m33 = 4.5; d11 = 6.5; d22 = 12; d33 = 8.5; % 误差微分项(用运动学方程代入) r_d_dot = 0; % 圆周轨迹常数 ex_dot = -Ud + u*cos(psie) - v*sin(psie) + rd*ey; ey_dot = u*sin(psie) + v*cos(psie) - rd*ex; psie_dot = r - rd; % 滑模面 s1 = u - Ud + alpha*ex + beta1*z1 - F1; s2 = r - rd + k*ey + c*psie + beta2*z2 - F2; % 饱和函数 sat1 = min(max(s1/phi1, -1), 1); sat2 = min(max(s2/phi2, -1), 1); % 控制律 tau_u = m11*( -alpha*ex_dot - beta1*ex + dF1 - k1*s1 - eps1*sat1 ) ... - m22*v*r + d11*u; tau_r = m33*( -k*ey_dot - c*psie_dot - beta2*ey + dF2 - k2*s2 - eps2*sat2 ) ... - (m11-m22)*u*v + d33*r; % 限幅 tau_u = max(tau_u, -20); tau_u = min(tau_u, 60); tau_r = max(tau_r, -15); tau_r = min(tau_r, 15); end注意我在这里没有显式加扰动估计补偿项,而是靠积分项把扰动吸收。如果扰动很大,积分项会变得很大,所以后面必须加抗积分饱和逻辑。F1和F2由外部积分器的初始条件生成,它们的时间导数dF1 = -λF1、dF2 = -λF2,直接在控制器内计算即可。
限幅非常重要。真实AUV的推进器推力和舵机力矩都有饱和,Simulink里不设限制,控制器输出可能达到几千牛,积分器状态也会被推得极大,导致一松开限幅系统立刻震荡。限幅设置的另一个作用,是迫使你认真对待“执行机构能提供的能量是有上限的”这件事。
5. 仿真验证与参数调试:从发散到稳定的过程
参数调试是这个项目里最消耗时间也最涨经验的部分。直接给一组“能用”的参数没有意义,必须讲清楚调试顺序和现象判读方法。
5.1 初始条件与参数表
仿真初始状态设为:x=2 m、y=2 m、ψ=0 rad、u=0 m/s、v=0 m/s、r=0 rad/s。参考轨迹取半径R=10 m、角速度ω=0.05 rad/s的匀速圆周,对应U_d=0.5 m/s。表1汇总了控制器参数。
表1 控制器参数表
| 参数 | 含义 | 数值 |
|---|---|---|
| α | s1中位置误差权重 | 0.4 |
| β1 | s1积分项增益 | 0.05 |
| k | s2中横向误差权重 | 0.3 |
| c | s2中艏向误差权重 | 0.8 |
| β2 | s2积分项增益 | 0.10 |
| k1, k2 | 指数趋近律增益 | 1.0, 1.5 |
| ε1, ε2 | 饱和项增益 | 0.2, 0.5 |
| φ1, φ2 | 边界层宽度 | 0.02 |
| λ | F(t)衰减速率 | 1.0 |
调试顺序建议从k1、k2开始。这两个参数决定状态靠近滑模面的速度,取值太小会收敛慢,取值太大会在边界层附近振荡。先把k1、k2调稳定,再加位置误差权重α、k、c,最后加积分增益β1、β2和全局项衰减λ。积分增益上来太快,系统会像“弹簧末端挂了重物”一样低频来回晃。
5.2 典型结果曲线怎么读
仿真时长设置为200秒,采用ode45变步长求解,最大步长0.01秒。正常情况下,位置跟踪效果会呈现三个阶段的特征:
第一个阶段是0-20秒,误差从初始的2米左右快速收敛。因为全局滑模面初始值等于零,系统直接从滑动模态开始,所以这一阶段误差下降比较干脆,没有普通滑模那种先花几秒“找滑模面”的过程。
第二个阶段是20-100秒,误差进入稳态,横向误差e_y通常在0.1米量级,艏向误差在0.02弧度量级。由于圆周轨迹持续转弯,横荡速度v周期性变化,误差曲线上能看到明显的同频波动,但这属于欠驱动系统的正常现象,不影响轨迹贴合。
第三个阶段是100-200秒,如果存在固定偏置扰动,积分项会逐渐把偏置吸收,横向误差进一步压小。仿真曲线里e_y会呈现一种“缓慢爬升然后回落”的形态,这是积分项在“咬住”常值扰动,不用慌张。
控制输入曲线同样值得观察。τ_u应当始终为正,因为前向速度需要维持;τ_r则周期性变化,方向与圆周转向一致。边界层宽度φ取0.02时,τ_r曲线上会有细微的高频扰动,这是滑模控制在边界层内的正常切换活动,但幅度不应超过5%的满量程。如果切换幅度明显较大,说明φ太小或者ε太大,需要调大φ或者减小ε。
5.3 全局积分滑模和传统滑模的对照实验
为了验证全局项和积分项的实际贡献,我做了一组对照实验:同一模型、同一扰动、同一参考轨迹,只是控制器分别使用普通滑模面(不带积分项和F(t))和全局积分滑模面。表2记录了两个控制器在仿真中的典型差异。
表2 普通滑模与全局积分滑模对照
| 对比项 | 普通滑模 | 全局积分滑模 |
|---|---|---|
| 初始阶段误差收敛时间 | 约8秒 | 约3秒 |
| 稳态横向误差最大值 | 0.32 m | 0.11 m |
| 稳态纵向误差最大值 | 0.21 m | 0.06 m |
| 控制输入抖振幅度(τ_r) | 明显,接近限幅 | 轻微,约5%限幅 |
| 对参数摄动+10%的适应 | 误差增大明显 | 误差基本不变 |
普通滑模的曲线在启动阶段总有一段“犹豫”,因为系统要先从初始状态走到滑模面。全局积分滑模没有这个过程,一开始就进入滑动模态,所以初始收敛更快。稳态差距主要来自积分项:普通滑模面对常值形式的扰动分量,横向误差会残留一个固定的偏移量,积分项则能把这个偏移零。
这个对照实验强烈建议你自己做一遍。它花费的时间不多,但能够彻底回答“全局积分滑模到底比传统滑模强在哪”这个问题——不是时间复杂度上的强,而是鲁棒性和稳态精度的强。
6. 工程化落地中的几个真问题
仿真跑得再好,也不代表能直接装到AUV上下水。以下几类问题是我在从“仿真收敛”走向“可以拿去水池试验”的过程中反复踩过的。
6.1 抖振与执行机构寿命
滑模控制的抖振,在仿真里只是在曲线形状上有点丑,但在实物上意味着舵机高频来回摆动、推进器推力频繁反转。我很长一段时间把φ设成0.005追求所谓的“滑模精度”,结果仿真曲线确实漂亮,但控制器输出的高频分量很重。后来把φ调到0.02-0.05,控制输入平滑了很多,稳态误差只增加了2-3厘米。这个权衡完全值得。
工程上还有一个技巧:饱和函数边界层宽度不要固定,可以在误差较大时用小φ保证快速收敛,误差小到一定阈值后再切换到大φ抑制抖振。这样能兼顾动态过程的强鲁棒性和稳态过程的执行机构友好性。
6.2 积分饱和与初始时刻跳变
积分项是双刃剑。当控制器输出已经顶到限幅值时,积分器还在累积误差,导致“松开限幅后系统过冲”。处理办法是限幅判断后停止积分器更新,或者把积分器的状态一直被限制在某个范围内。
另一个问题就是全局滑模面的初始值。仿真里F1(0)=s1(0)、F2(0)=s2(0)很好算,但如果中途重置了Simulink积分器,或者实际系统运行到一半重新上电,全局项初值就不对了,系统会重新出现趋近阶段。实际工程中我倾向于在软件启动时花一个采样周期计算当前s值,并把F初值实时设为当前s值,而不是直接从零开始。这样即使中途切换控制器参数,也能保持全局滑动特性。
仿真中实现F初值的方法,是在求解器设置里把两个积分器z1、z2的初值设为:
[ z_1(0) = \frac{s_1^{*}-(u(0)-U_d(0)+\alpha e_x(0))}{\beta_1}, \quad z_2(0) = \frac{s_2^{*}-(r(0)-r_d(0)+k e_y(0)+c\psi_e(0))}{\beta_2} ]
其中s1*和s2*是你想设定的初始滑模面值,通常为零。这样即使积分器不是从零开始,滑模面在初始时刻也为零。
6.3 从仿真到水下实物的差距
仿真和实物之间最大的差距并不是动力学模型不准——现代辨识方法可以把模型辨识到相当高的精度——而是传感器噪声和执行机构延迟。仿真里滑模控制器能直接读取无噪声的速度和位置,但实物的DVL、光纤惯导和深度计都有噪声,艏向角在低速时有明显漂移,这些噪声会直接进入e_y和ψ_e,再被滑模面的微分项放大。
我的经验是,在设计阶段就给仿真加入适当的测量噪声和零阶保持器延迟,例如位置噪声均值0.05m、艏向噪声0.01rad,再观察控制器是否仍能保持稳定。如果在这种情况下积分项增益β2仍然偏大,就说明参数设计有问题,应该马上减小,而不是继续加什么“鲁棒项”去补救。
另外,执行机构的速率限制是实物上很隐蔽的破坏者。Simulink里限幅模块只限制了幅度,却没有限制速率变化。真实舵机每秒只能转多少度,推进器推力变化速率也有限制。如果控制器要求τ_r在0.2秒内从0变到满值,实物根本做不到。建议在仿真输出端串一个一阶惯性环节来模拟执行机构动态,时间常数取0.1-0.3秒,再看控制性能是否还能接受。
最后说一点个人体会。这套控制器真正值钱的地方,不是最后那条贴合的轨迹曲线,而是滑模面中三个“看不见”的设计选择:积分项消除稳态误差、全局函数消除趋近阶段、饱和边界层调和抖振与精度。做这类控制仿真,最容易陷入“参数调一调就能收敛”的假象,真正有价值的调试,是把每个参数变化对误差曲线形态的影响原因想清楚,然后带着这套理解去水池里解决那些仿真中根本看不见的问题。希望这篇记录能给正在折腾欠驱动AUV控制仿真的朋友省下几个晚上的时间。