首页 / 资讯中心 / 文章详情

船舶航向非线性自适应控制:MATLAB回步法设计与仿真

船舶航向非线性自适应控制:MATLAB回步法设计与仿真 ★ FEATURED ARTICLE
做船舶仿真控制这几年我感触最深的一件事是航向控制从来不是给个PID就完事的问题。大型商船转艏是一个大惯性、慢时变、强非线性的过程——同一艘船满载和压载状态下旋回性能完全不同航速从经济航速提到满载航速模型参数能变出一倍量级的差距。所以当看到基于MATLAB李亚普诺夫非线性的船舶航向回步自适应控制器设计这类项目时我觉得这是个非常典型的非线性自适应控制仿真案例值得拆开来讲清楚背后的设计逻辑和实现细节。这个项目解决的核心问题是在船舶模型参数未知、存在非线性阻尼的情况下设计一个回步Backstepping也叫反步/反推自适应控制器使船舶航向精确跟踪期望艏向并通过李亚普诺夫方法从理论上保证系统稳定。源码是基于MATLAB m脚本实现的包含被控对象模型、参考航向生成、控制器、自适应律和绘图主体代码我下面会完整列出来你完全可以照着在自己电脑上复跑一遍。适合看这篇文章的人有三类一是船舶控制方向的本科生、研究生做课程设计或毕业论文二是对非线性控制感兴趣想找一个比倒立摆、机械臂更工程化的仿真案例三是已经搭过Simulink模型但想搞清楚自适应律推导过程的朋友。我会把从模型到控制器推导、再到MATLAB代码实现和调参踩坑的完整链路都写出来。1. 船舶航向模型非线性与不确定性从哪来1.1 一阶Nomoto模型与Norrbin非线性修正先统一变量符号后面所有推导都基于这套定义ψ船舶航向角单位rad下文常以deg表示r ψ̇转艏角速度yaw rateδ舵角单位rad物理舵机一般限幅±35degK旋回性指数单位s⁻¹表示单位舵角产生的稳态转艏角速度T船舶追随性时间常数单位s表示艏向对舵响应的快慢一阶Nomoto模型是最经典的船舶航向响应模型写成状态空间形式就是ψ̇ rṙ -(1/T)r (K/T)δ这里T和K是常参数。T越大船舶惯性越大、响应越迟钝K越大同样舵角下转艏能力越强。典型量级是多少我整理了一个参考表仅供仿真初值选取不同船型差异很大船舶类型K (s⁻¹)T (s)特点小型快艇/交通艇0.5~1.02~10机动性好响应快中型货船/集装箱船0.2~0.410~30常规商船典型范围大型液货船VLCC0.1~0.240~100惯性大操纵笨重但常参数Nomoto模型只在小舵角、中等转艏速度下成立。当船舶大角度旋回、转艏角速度明显增大时水动力阻尼会呈现非线性增长。工程上常见做法是加上Norrbin非线性项写成Tψ̈ ψ̇ αψ̇³ Kδ对应状态方程就是ṙ -(1/T)r - (α/T)r³ (K/T)δ我把系数重新记为ṙ -a·r - b·r³ c·δ其中 a 1/Tb α/Tc K/T。这个三次项非常关键r小的时候它没什么影响但r一旦大起来这一项会提供很强的额外阻尼。做仿真时如果忽略它你会发现控制器在高海况、大转角场景下给出的舵令明显偏乐观实际船舶根本转不了那么快。1.2 参数不确定性自适应控制器为什么必要如果一艘船的K、T、α都是准确已知的常量那用精确反馈线性化加上回步控制器就够了根本不需要自适应。但工程现实恰恰相反K和T会随航速、装载状态、吃水、水深、船体污底甚至海域情况变化。拿航速来说经验规律是K大致随航速增大而增大T则大致随航速增大而减小。也就是说同一艘船在12节和20节航行时闭环特性差异很大。拿装载来说满载时排水量大、惯性大T明显上升。所以固定增益控制器在某一工况下调好了换一个工况就可能出现过冲、振荡或者响应太慢的问题。这就是自适应控制器存在的理由控制器本身在运行中在线估计模型参数并基于估计值实时调整舵令。本文设计的回步自适应控制器就是在被控对象的a、b、c三个系数都未知的前提下同时完成参数估计和航向跟踪。外部风浪流干扰本文先不纳入被控对象等把控制器逻辑跑通后作为扩展项这是做仿真的合理顺序。2. 李亚普诺夫稳定性与回步设计如何嵌在一起2.1 回步法的核心思想逐层设防从外向内推回步法适合一类严格反馈系统也就是状态变量一层套一层、控制输入只出现在最后一层的结构。船舶航向模型恰好就是这样第一层ψ̇ r也就是说航向角变化率由转艏角速度决定第二层ṙ -a·r - b·r³ c·δ转艏角速度变化率由舵角决定设计思路可以用一句话概括先假设我能直接控制r那要让ψ跟上期望航向r应该等于多少然后再反推要让r达到这个目标值δ应该等于多少。第一步定义一个航向误差e1 ψ - ψ_d其中ψ_d是期望航向。如果有一个理想的转艏角速度指令α1并且r能瞬间等于α1那么误差方程可以写成e1̇ r - ψ̇_d α1 - ψ̇_d为了让e1指数收敛取虚拟控制律α1 ψ̇_d - k1·e1这样e1̇ -k1·e1只要k10航向误差就按指数速度衰减。但问题在于r并不能瞬间等于α1所以定义第二层误差e2 r - α1 r - ψ̇_d k1·e1接下来的任务就变成设计真实舵角δ让e2也收敛到0。这就是回步法回一步的整个逻辑——从输出端误差开始向内退一层每退一步就引入一个误差变量和一个待设计的虚拟控制项。2.2 李亚普诺夫函数一个能量账本李亚普诺夫第二方法的核心是找一个正定函数V让V沿系统轨线的导数始终非正从而证明系统不会越跑越远。对回步控制器来说最自然的候选函数就是把两层误差平方加起来V1 1/2·e1² 1/2·e2²对V1求导代入前面推导的误差动态控制律设计的目标是让所有交叉项都消掉只剩下负的阻尼项。推导到最后可以得到控制律δ [ -e1 ψ̈_d - k1(r - ψ̇_d) - k2·e2 â·r b̂·r³ ] / ĉ这里â、b̂、ĉ是对a、b、c的估计值。代入V1̇后理想情况下会得到V1̇ -k1·e1² - k2·e2²这就是能量账本在起作用只要k1、k2为正这个函数只能下降不能上升。但问题是如果â、b̂、ĉ估计错了控制律里就带着偏差上面这个等式不成立账本上会多出未知的交叉项。所以要把参数估计误差也记进账本扩展李亚普诺夫函数V V1 1/(2γ_a)·(â-a)² 1/(2γ_b)·(b̂-b)² 1/(2γ_c)·(ĉ-c)²把参数估计误差的平方放进V相当于记账时把控制器自己的学习误差也记进去了。这么一来自适应律就不是拍脑袋定的而是为了保证V̇≤0而必须满足的条件。推导得到的自适应律为â̇ -γ_a·e2·rb̂̇ -γ_b·e2·r³ĉ̇ -γ_c·e2·δ代入之后估计误差与真实误差的交叉项恰好全部抵消最终仍有V̇ -k1·e1² - k2·e2² ≤ 02.3 稳定性的够用与参数估计的有限由Barbalat引理可以推出跟踪误差渐近收敛到0也就是说航向跟踪效果是有理论保证的。但这里有个很多新手容易误解的点V̇非正只能说明参数估计误差有界不能说明参数估计误差一定收敛到0。要让参数估计真正收敛到真值需要满足持续激励条件PE也就是期望航向信号要足够丰富——不能老是朝着一个方向转一次就完事而要来回机动让被估计参数在系统响应里的作用被充分激发。做仿真项目时你会遇到一种典型情况只做一次20°转向航向跟踪曲线完美但â、b̂、ĉ的曲线还在慢慢飘根本没到真值。这不是控制器坏了而是激励不够。这也是为什么我在后面的仿真参考信号里会建议设计成多段机动单纯一个阶跃转向对展示跟踪效果够用但对展示参数辨识效果远远不够。3. MATLAB仿真实现模型、参考信号与闭环控制器3.1 仿真架构为什么用m脚本而非Simulink这一期源码我用的是纯m脚本方案三个文件各司其职ship_model.m被控对象模型只描述船舶本身的动力学ref_heading.m参考航向生成函数输出期望艏向及其一阶、二阶导数main_backstepping.m主脚本定义参数、闭环控制函数、自适应律调用ode45积分并绘图用m脚本而不是Simulink主要原因是自适应律的推导过程和参数拆解在脚本里看得更清楚改一个γ值重新跑一遍也方便批量扫参。Simulink的优势在于信号流可视化但调试参数估计这种带代数环和嵌套状态的逻辑反而麻烦。如果你是想快速复现并理解原理我强烈建议先用m脚本跑通再决定要不要转Simulink。3.2 被控对象模型代码ship_model.m很简单传入状态、舵角和模型参数返回状态导数function xdot ship_model(t, x, delta, p) % x [psi; r] psi x(1); r x(2); psi_dot r; r_dot -p.a * r - p.b * r^3 p.c * delta; xdot [psi_dot; r_dot]; end实际仿真中这个函数会被闭环函数调用而不是独立运行因为控制器需要根据状态实时计算舵角。3.3 参考航向信号设计参考航向我不用阶跃原因很简单阶跃信号在起始瞬间导数无穷大回步控制器里含ψ̇_d和ψ̈_d阶跃会给控制器输出一个尖峰舵角瞬间顶到限幅既不真实也不利于观察自适应效果。更贴近实际驾驶台操作的做法是用一段平滑过渡我采用三次平滑多项式在50s到150s之间从0°转到20°起点和终点的速度、加速度都为零function [psid, d1, d2] ref_heading(t) % 输出期望航向psid、一阶导d1、二阶导d2 if t 50 psid 0; d1 0; d2 0; elseif t 150 tau (t - 50) / 100; psid_final deg2rad(20); psid psid_final * (3*tau^2 - 2*tau^3); d1 psid_final * (6*tau - 6*tau^2) / 100; d2 psid_final * (6 - 12*tau) / 10000; else psid deg2rad(20); d1 0; d2 0; end end如果你想检验持续激励条件下的参数收敛可以把这段函数改成一个来回机动的形式比如50s转20°、200s转回0°、350s再转到-15°。参考信号导数要保证连续这是回步控制器对信号的基本要求。3.4 主脚本与闭环控制主脚本把被控对象、控制器、自适应律全部集成到一个闭路函数里状态向量扩展为[psi; r; a_hat; b_hat; c_hat]后三个就是在线估计的参数。这样ode45可以直接推进整个状态向量clear; clc; % 船舶模型参数典型中型货船量级 p.K 0.2; p.T 30; p.n1 1.0; p.n2 0.3; p.a p.n1 / p.T; p.b p.n2 / p.T; p.c p.K / p.T; % 控制器增益 p.k1 0.2; p.k2 0.5; % 自适应增益 p.ga 0.3; p.gb 0.03; p.gc 0.1; % 参数投影下限防止c_hat过小导致控制器奇异 p.c_min 0.1 * p.c; % 初始状态艏向0转速0估计初值给0.05/0/c真值的1.1倍 x0 [0; 0; 0.05; 0; 1.1 * p.c]; % 闭路仿真 [t, X] ode45((t, x) closed_loop(t, x, p), [0 600], x0); % 绘图 psi_ref arrayfun((tt) ref_heading(tt), t, UniformOutput, false); psi_ref cell2mat(psi_ref) * 180 / pi; figure(Color, w); subplot(3, 1, 1); plot(t, rad2deg(X(:, 1)), b-, t, psi_ref, r--, LineWidth, 1.2); legend(实际航向, 期望航向); ylabel(航向 (deg)); grid on; subplot(3, 1, 2); delta_log arrayfun((i) compute_delta(t(i), X(i, :), p), 1:length(t)); % 说明compute_delta是下面closed_loop里控制律的副本这里直接再算一遍用于绘图 plot(t, rad2deg(delta_log)); ylabel(舵角 (deg)); grid on; subplot(3, 1, 3); plot(t, X(:, 3), LineWidth, 1.2); hold on; plot(t, X(:, 4), LineWidth, 1.2); plot(t, X(:, 5), LineWidth, 1.2); legend(a\_hat, b\_hat, c\_hat); xlabel(时间 (s)); ylabel(参数估计); grid on; function xdot closed_loop(t, x, p) psi x(1); r x(2); a_hat x(3); b_hat x(4); c_hat x(5); % 参考信号及导数 [psid, d1, d2] ref_heading(t); % 回步误差 z1 psi - psid; z2 r - d1 p.k1 * z1; % 控制器c_hat加下限保护防止分母趋近于零导致仿真发散 c_hat_use max(c_hat, p.c_min); delta (-z1 d2 - p.k1 * (r - d1) - p.k2 * z2 a_hat*r b_hat*r^3) / c_hat_use; % 舵角限幅 35deg delta max(min(delta, deg2rad(35)), deg2rad(-35)); % 被控对象真模型 r_dot -p.a * r - p.b * r^3 p.c * delta; % 自适应律 a_hat_dot -p.ga * z2 * r; b_hat_dot -p.gb * z2 * r^3; c_hat_dot -p.gc * z2 * delta; xdot [r; r_dot; a_hat_dot; b_hat_dot; c_hat_dot]; end注意一个细节我在主脚本里用了一个compute_delta函数来重复计算舵角曲线实际调试时更简单的做法是在集群仿真中直接把delta也输出成状态或者保存成全局变量。这里为了保持状态向量干净就用了一个较轻量的重复计算。ode45的积分器在遇到舵角限幅这种非光滑环节时可能放慢步长但一般不会出问题。跑通这个代码之后你会看到三条典型的曲线航向跟踪曲线几乎贴着期望航向走舵角曲线在过渡阶段有一个明显的先正后负或单向偏转参数估计曲线则在初始段快速调整后趋于平缓。4. 仿真结果的分析方法与调参经验4.1 三条曲线怎么看仿真跑完第一件事不是关心曲线好不好看而是先确认控制律的形状是否符合物理直觉。航向曲线上你应该看到从50s开始船艏跟随期望平滑转动实际航向与期望航向之间的最大误差集中在转入初期和转出末期的几十秒内误差一般能控制在0.2deg以内。如果k1、k2选得特别小误差会拉大选得特别大则会出现来回修正的锯齿状航向。舵角曲线是最能暴露问题的。转向初期由于参考信号加速度为零初始舵令很小真正的舵角峰值会出现在过渡段的中部偏前对应期望加速度最大的位置。如果看到舵角在±35deg限幅边界长时间贴边说明参考转向规划得太急或者增益太大这时要回到参数和信号规划上找原因而不是继续加自适应增益。参数估计曲线要分情况看。一次20°转向后â往往会在初始阶段明显调整b̂和ĉ变化可能不大。这是正常的因为单次转向对持续激励的贡献有限。如果想看到参数估计趋近真值的变化过程把参考航向改成多段往返机动让a、b、c各自在响应中的贡献都被激活。4.2 k1、k2、γ三个增益怎么配合回步控制器里增益其实分两类一类是控制器的反馈阻尼k1、k2决定跟踪速度和稳定裕度另一类是自适应律增益γ决定参数估计调整速度。它们的作用方向和副作用我整理成了一个表增益作用调大的效果调小的效果建议起始范围k1第一层航向误差阻尼航向修正快但舵角需求增大响应迟钝误差大0.1~0.3k2第二层转艏角速度误差阻尼抑制超调、收敛快但高增益易抖振和舵角饱和出现明显振荡0.3~0.8γ_aa的估计速度估计收敛快但可能引起参数振荡收敛慢参数跟踪滞后0.05~0.5γ_bb的估计速度b对应的是r³项r大时很敏感太大会爆非线性补偿不足0.005~0.05γ_cc的估计速度过大会使c_hat抖动甚至符号翻转舵令振荡收敛慢0.01~0.1我给出一套比较稳妥的调参顺序按这个来基本不会把自己绕晕先关掉自适应让â、b̂、ĉ直接等于真值只调k1、k2直到航向跟踪和舵角曲线都满足要求。只对a做自适应γ_a从小到大慢慢加观察â曲线和舵角曲线是否出现抖动。依次开放b、c的自适应每次只动一个确认没引入新的振荡再继续。最后把所有γ放一起整体微调。这套方法的核心原则是一次只动一个旋钮。回步控制器本身增益不算多但三个自适应通道一起开时问题出现后很难一眼定位是哪个通道引起的。4.3 工程约束对控制性能的影响舵角限幅是最先要考虑的物理约束。理论上回步控制器设计的李亚普诺夫证明是在无饱和条件下成立的一旦限幅生效严格来说全局稳定性证明就不成立了。但在工程实践中只要饱和不是长时间持续系统仍然能正常工作。真正要警惕的是饱和期间误差积累饱和结束后控制器给出一个方向相反的补偿猛舵造成明显的航向摆动。缓解办法是降低k2或者把参考转向时间拉长。舵机速率限制也是一个实际约束。真实舵机转舵速率通常不会超过每秒几度仿真里如果不做速率限制控制器给出的高频舵令变化会被理想化。做固定步长离散仿真时可以在舵令后加一个速率裁剪模块但在ode45连续仿真里正确做法是把舵角也扩展成一个带一阶惯性环节的状态这会增加状态维度但更真实。这个扩展适合作为你跑通基础版之后的进阶任务。测量噪声的影响更隐蔽。回步控制器用到r ψ̇如果仿真里没有直接测量r而是对罗经的ψ做差分噪声会被放大很多。这也是为什么纯理论控制器转工程应用时经常要加滤波器。加了低通滤波之后控制器的相位滞后会增大你又得回头把k2减小一些。这就是工程上典型的拆东墙补西墙没有免费的午餐。5. 源码调试中的几个大坑排查链路与对策5.1 一跑就NaN先查分母和限幅我最初跑这版代码时第一轮就翻车了。把c_hat初值设成稍微偏离真值的值仿真到某个时刻直接NaN。排查链是这样的先看最可能的元凶——控制器里有除法分母是ĉ而ĉ是通过自适应律变化的它没有任何下界保护。如果ĉ在小范围内振荡时穿越零点控制律瞬间变成无穷大然后r被推到极大值r³再进一步爆炸整个系统就崩了。定位到这一点后解决思路不是去掉c的自适应而是加一个投影下限。最简单的方式就是代码里那个max(c_hat, p.c_min)让控制器始终使用一个正的有界值。如果c_hat在仿真后期真的要穿越零点那说明γ_c给得太大了应该减小γ_c而不是继续硬扛。实际项目里更稳妥的工程做法是**c这个参数不参与自适应直接用标称值放在分母里只对a和b做自适应。**原因是c的物理含义是舵效增益它的不确定范围通常没有a、b那么夸张而且它出现在控制器输出通道上估计错误的影响会被控制律的乘法直接放大。想先快速跑通、验证逻辑就固定c想做完整论文级分析再加带回下限保护的c自适应。5.2 初始段舵令异常大参考信号与增益的交互另一个常见现象是明明模型参数都正确一仿真初始几秒舵角就打到限幅。这时候先别急着怪控制器检查一下参考信号是否连续。如果直接用阶跃航向指令ψ̇_d和ψ̈_d在跳变时刻是无穷大控制器为了追踪这个不现实的信号输出自然爆表。换成光滑参考后我会同时把k1、k2降低到0.1~0.2量级再看舵角曲线。如果你用的是阶跃但加了低通滤波也要小心。滤波器虽然让信号平滑了但滤波器的初始条件如果不匹配会在前几秒产生一个虚假的过渡过程。我踩过的一个具体坑是二阶滤波器初始状态给零但期望航向不为零结果滤波器输出的ψ̇_d初始值不是零控制器认为航向正在快速变化给出一个很大的反向舵令。这类问题通过绘制参考信号的一阶、二阶导数曲线就能快速发现。5.3 自适应律符号搞反的排查方法自适应律和参数估计误差的符号关系非常容易搞反。公式里是â̇ -γ_a·e2·r但不同教材定义参数误差时可能用θ̃ θ - θ̂而不是θ̂ - θ导致符号正好相反。如果你发现仿真里参数估计曲线朝着远离真值的方向跑或者航向误差明明大但参数根本不更新大概率是符号问题。我的排查方法是加一个已知真值对比实验把被控对象参数设为已知控制器里â、b̂、ĉ初始值故意设成偏离真值的值然后关闭自适应看跟踪误差是否按预期变化。接着打开自适应如果参数估计曲线没有朝着真值方向移动就把对应通道的γ取负号重试。这种方法虽然土但在多通道自适应系统里比死盯数学推导要快得多。5.4 ode45与固定步长的选择源码里用的是ode45因为它对自适应这类刚性问题相对友好。但你如果计划把这个控制器移植到实时仿真平台或者嵌入式代码里最终还是要换固定步长积分。我做对比测试时发现固定步长欧拉积分在dt 0.1s时某些参数组合下会出现数值振荡尤其是r³项在r接近限幅值时会被放大得厉害把步长缩小到0.01s问题消失。如果你用固定步长自写积分器建议先用RK4而不是欧拉且在参数估计通道上注意步长不能太大。顺便提醒一句用ode45时如果输出结果中出现奇怪的锯齿先检查是不是odeset的容差太小或者舵角限幅导致的非光滑点。限幅点会让积分器局部加密步长这是正常的不用惊慌。最后分享一个个人习惯我每次跑完这类非线性控制器仿真都会顺手做一个参数扫描把k1、k2、γ_a、γ_b、γ_c各取三档跑一批组合记录每个组合下的最大航向误差、最大舵角、稳态误差三个指标。这样选出来的参数组合在写报告或论文时可以直接说经过对比试验选取比拍脑袋给一组增益有说服力得多。船舶航向控制是个经典问题但每代人的验证工具都在变MATLAB m脚本依然是快速验证非线性控制算法最顺手的媒介。源码你可以直接跑难的是愿意花一个下午去动那些增益、观察曲线反应——这一步没人能替你省。
阅读完成 · 觉得有帮助?
咨询建站