ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于MMG模型的船舶运动控制仿真:从模型到PID航向闭环

基于MMG模型的船舶运动控制仿真:从模型到PID航向闭环

简介:面向船舶运动控制学习与研究的一份MMG模型项目压缩包,内容围绕船舶多模型生成技术展开,可用于风浪、流速、舵角、载荷等不同工况下船舶六自由度运动的动态模拟与行为分析,帮助工程师与学者理解船舶运动规律并设计控制策略。包内共28个文件,包含Python源码、HTML/JS可视化页面、CSS样式与数据映射文件等,构成一套可运行的前后端交互模拟工具,便于修改参数并观察对应运动状态。已有901人学习下载。通过项目源代码与仿真脚本,读者可掌握MMG建模思路、参数配置方式与结果可视化流程;配套的静态资源与文档则辅助说明模型理论基础与使用步骤,适合作为船舶运动控制课程设计、自动驾驶评估或相关科研项目的参考。

1. 先搞懂这份 ship_mmg-master:船舶运动控制仿真在浏览器里怎么跑

船舶运动控制的入门难点不是 PID 公式,而是你手里没有一条能反复折腾的船。ship_mmg-master 这个工程就是为这个痛点准备的:一套基于 MMG 模型的船舶运动仿真资源,Python 写成、Flask 做界面,解压后在浏览器里动态展示不同工况和参数下的船舶运动状态。你在页面上改舵角、改螺旋桨转速、加环境扰动,船体轨迹和航向响应马上就能看到。

它适合两类人:正在学船舶运动控制的学生,想先直观感受"舵角给下去航向怎么响应";以及做操纵性预研的工程师,想快速验证控制算法的可行性,不想一上来就上 CFD 那套重工具。它定位是可交互的数值试验台,不是流场求解器——MMG 模型把船体、螺旋桨、舵的力分别建模再叠加,算的是三自由度操纵运动。对航向保持、循迹控制这些课题,这个精度足够看趋势。

下面从模型层开始拆,再把 Flask 界面跑起来,最后给一套闭环改造和交叉验证方法。

2. MMG 模型怎么落进代码:分离建模、三自由度方程与 kt_maneuver.py

2.1 为什么选 MMG 而不是整体模型

船舶运动数学模型大致分两派。一派是 Abkowitz 那类整体模型,把船体、螺旋桨、舵当作一个整体,用一个大型多项式去拟合全部水动力;另一派就是 MMG(Mathematical Model Group,由日本操纵性数学模型研讨组提出),把总水动力拆成船体力、螺旋桨力、舵力三块,分别建模再叠加。ship_mmg-master 用的是后者,这从项目里 kt_maneuver.py 这个模型文件的组织方式和"不同环境下叠加船舶运动状态"的描述都能对上。

我拆这个工程时最看重 MMG 的一点:每一块力的物理意义都能单独说清楚,方便改、方便查。想研究"螺旋桨转速对舵效的影响",你不需要动船体那一堆系数,只改推力减额因子和伴流分数;想换船型,把船体的线性水动力导数换掉就行。整体模型改一个参数往往牵一发动全身,对学习阶段来说太像黑匣子——出了问题你分不清是船体、螺旋桨还是舵在作怪。

kt_maneuver.py 承担的就是模型层职责。按这类小工程最常见的组织方式,它会把状态求导、三块力的计算、积分器收敛在一个文件里,app.py 那套 Flask 界面只负责周期性调用它拿结果,再推给前端。这种分层方式对初学者很友好:就算把前端全删了,拿着 kt_maneuver.py 也能单独跑仿真。

2.2 三自由度运动方程:先看骨架

忽略横摇、垂荡、纵摇——操纵性标准关心的主要是水平面运动——方程就是下面这个骨架。我按这份资源场景下最常见的实现方式重写了一遍等价形式:

# 状态向量: [u, v, r, x, y, psi] # u/v: 纵向/横向速度(m/s) r: 艏摇角速度(rad/s) # x/y: 位置(m) psi: 航向角(rad) def ship_derivatives(state, rudder_angle, prop_rps, p): u, v, r, x, y, psi = state m, mx, my = p['m'], p['mx'], p['my'] Izz, Jzz = p['Izz'], p['Jzz'] # 船体力: 线性项 + 非线性项分开写, 方便替换成自航模试验值 XH = p['Xuu'] * u * abs(u) + p['Xvr'] * v * r YH = p['Yv'] * v + p['Yr'] * r + p['Yvv'] * v * abs(v) NH = p['Nv'] * v + p['Nr'] * r + p['Nvvv'] * v**3 # 螺旋桨推力(只算纵向, 推力减额t修正) J = (1 - p['w']) * u / (prop_rps * p['Dp']) KT = p['kt0'] + p['kt1'] * J + p['kt2'] * J**2 XP = (1 - p['t']) * p['rho'] * prop_rps**2 * p['Dp']**4 * KT # 舵力单独算, 放下一节展开 XR, YR, NR = rudder_force(u, v, r, rudder_angle, p) # 联立求解加速度, 注意哥氏项符号 du = (XH + XP + XR + (m + my) * v * r) / (m + mx) dv = (YH + YR - (m + mx) * u * r) / (m + my) dr = (NH + NR) / (Izz + Jzz) return np.array([du, dv, dr, u*np.cos(psi) - v*np.sin(psi), u*np.sin(psi) + v*np.cos(psi), r])

先说明坐标系。x 轴指向船艏,y 轴指向右舷,这是船舶操纵学里最常用的船体坐标系;后面三行是把船体坐标的速度转换回地面坐标,供轨迹绘制使用。转换公式本身不难,但很多改代码的人会在 sin/cos 搭配上出错,画出来的轨迹整体旋转了 90°,一会儿避坑章还会再提。

我故意把 du、dv 里的 (m+my)vr 和 (m+mx)ur 显式写出来,因为这两个是坐标系旋转带来的哥氏项。刚从整体模型转过来的人最容易在这里丢符号——丢了的表现很典型:直航没问题,一转舵速度就疯涨,最后变成 nan。

螺旋桨推力里 J 是进速比,KT 是推力系数,用二次多项式拟合敞水特性曲线;推力减额 t 和伴流分数 w 是螺旋桨与船体干扰的宏观体现。舵力封装成了独立的 rudder_force,因为舵力公式涉及舵处有效来流的重构,单独拆开讲更清楚。

2.3 船体、螺旋桨、舵三块力怎么分工

船体力是"默认工况"的基底。直航时主要看纵向阻力 Xuu,u*|u| 这种写法保证阻力永远和速度方向相反;斜航时 Yv、Nv 起主要作用,分别决定横向力、艏摇力矩对横漂速度的敏感度;转艏时 Yr、Nr 是角速度导数。这个线性骨架加上少量非线性项(Yvv、Nvvv),覆盖常规操纵运动已经够用。真要预报横甩那种极限工况,才需要再上高阶项和横摇耦合,那不是这份资源的目标。

螺旋桨力这里只给了纵向推力,符合绝大多数 MMG 实现——螺旋桨对横向力和艏摇力矩的直接贡献很小,通常并进船体干扰项处理。工程上最常改的是 t 和 w:t 越大说明桨的推力被船体"吃掉"得越多;w 越大说明桨平面处的来流被船体减速得越狠。这两项直接决定螺旋桨转速给到多少才能顶到目标航速,换船型时必动。

舵力最值得单独写:

def rudder_force(u, v, r, delta, p): # 舵处有效来流, 考虑船体对舵的整流 uR = u * (1 - p['wR']) # 纵向有效来流 vR = v + p['lR'] * r # 横向速度 + 绕流修正 UR = np.sqrt(uR**2 + vR**2) alphaR = delta - np.arctan2(vR, uR) # 有效攻角 # 法向力按升力线斜率线性段近似 FN = 0.5 * p['rho'] * p['AR'] * UR**2 * p['fa'] * alphaR # 分配到x/y, 并向回转中心取矩 XR = -FN * np.sin(delta) YR = -FN * np.cos(delta) * (1 + p['aH']) NR = YR * p['xR'] - XR * p['yR'] + p['xH'] * (-FN * np.cos(delta) * p['aH']) return XR, YR, NR

我一般会把 wR、lR、aH、xH 这几个"舵-船体干扰系数"单独拎到参数区。它们不像船体导数那样有明确物理量纲,但恰恰最影响舵效。比如 aH 是操舵诱导横向力系数,小型船上常见 0.2~0.4,忽略它回转圈会明显偏大,航向保持控制表现为"舵给了但船不爱转"。fa 是舵升力系数斜率,按翼型可取 2~5 每弧度,取太大会让大舵角时控制饱和提前,反而降低实际舵效。

2.4 这份资源里最值得改的参数表

把下面这十几个参数理一遍,基本就掌握了整个模型的行为边界。以一般教学船的量级为例:

参数符号典型量级影响
船长L50~100 m无量纲化基准,别乱改
船体质量m按 L 近似估算惯性,决定响应快慢
附加质量mx, my0.05~0.2 倍 m纵向小、横向大
艏摇转动惯量Izz, Jzz约 m*L² 的 5%艏摇惯性
横向力导数Yv, Yr负值操纵响应敏感度
艏摇力矩导数Nv, Nr负值航向稳定性核心
推力减额t0.1~0.3桨推进效率
伴流分数w0.1~0.4桨平面来流速度
舵面积AR约 L² 的 1%~2%舵效直接相关
舵升力斜率fa2~5 /rad舵效线性段增益
操舵诱导横向力系数aH0.2~0.4舵效放大、回转半径

提示:模型做无量纲化时,船体力各项会除以 0.5·rho·L² 或 L³;换船型最容易出的问题就是"改了船长但没同步改无量纲系数对应的整体量纲",结果仿真数值看起来正常,量级却差出几个数量级,第四章还会细讲。

所以拆到这里你会看到,MMG 的价值在于力的来源可追溯。调试时你能指着某个系数说"问题就出在这",这是整体模型给不了的可解释性。

3. 把仿真跑起来:Flask 界面、参数映射与场景修改

3.1 环境准备与启动

先说运行条件。这类 Flask 小工程的依赖很轻,一般就是 flask 加 numpy,模型层的计算全是纯 Python 循环,不需要 GPU。启动命令是约定俗成的:

cd ship_mmg-master pip install flask numpy python app.py

然后浏览器打开 http://127.0.0.1:5000。app.py 里通常就两个路由:一个 GET 渲染主页面模板(templates 目录下的 html),一个接口返回当前仿真状态(JSON),前端用 setInterval 定时轮询刷新。这是小工程最稳的写法:不需要 WebSocket,模型计算和页面渲染完全解耦,改前端不会影响模型。

如果打开页面报模板找不到,先检查是不是在项目根目录下用python app.py启动的。Flask 默认把 templates 和 static 定位在启动目录,你在别的目录执行就不会。这个错我刚接触时犯过,纯属启动姿势问题。

3.2 界面参数与模型参数的映射

页面上那些滑块和输入框,背后对应的就是第二章里的模型变量。常见的映射关系长这样:

界面项模型变量作用位置单位陷阱
舵角deltarudder_force 输入界面给度,模型要弧度
螺旋桨转速n推力公式 prop_rpsrpm 要除以 60 转成 rps
初始航向psi0状态初值0° 朝北还是朝 x 轴,先看说明
初始速度u0状态初值常设服务航速
仿真时长/步长t_end, dt积分循环控制步长太大必发散
横流/风速Vc, Vw外扰力叠加相对风向还是绝对风向

单位是我每次拿到这类资源都会先确认的事。页面上舵角如果是 30,进模型之前一定要乘 pi/180;螺旋桨转速如果是 120 rpm,要除以 60。这类错误的表现特别典型:舵角给了 30° 但船没反应,或者转速给到 120 船速却离谱地高。别问我怎么知道的,新手期谁还没在这翻过车。

3.3 想换船型?改这几处就行

换船型是这份资源被问得最多的二次开发需求,步骤我按经验整理成五步:

先在参数区替换船长、船宽、吃水、方形系数这些主尺度;然后用经验公式或手头试验数据更新附加质量 mx、my 和转动惯量 Izz、Jzz;接着替换船体线性导数 Yv、Yr、Nv、Nr,这几项决定航向稳定性,如果有目标船的自航模试验数据,优先用试验值;再同步更新螺旋桨的 Dp、KT 曲线系数、t、w;最后跑一遍直航和 10°/10° 的 Z 形仿真,跟已知的操纵性指标对比,确认趋势对得上再进控制阶段。

这里九成的翻车都出在第一步和第三步之间:改了船长,但没有按比例更新舵面积和螺旋桨直径,造成舵效、推力与船体惯性不匹配。表现就是仿真要么回转圈大得离谱,要么直航时怎么也走不直。校验办法是先把开环响应跑出来,看稳态回转直径是否落在船长 3~5 倍的区间内,这个经验值可以帮你挡住大部分参数组合错误。

3.4 加风浪扰动:在力的叠加处动手

想看控制器在干扰下的表现,通常做法是在 ship_derivatives 的返回值里加一组外扰力。风简化为恒定的横向力和艏摇力矩,浪用一个慢变的正弦力矩模拟,够用且可控:

# 在 ship_derivatives 返回前叠加环境扰动 # 简化风: 横向力随风速平方变化, wind_dir 是来流方向 wind_Y = 0.5 * p['rho_air'] * p['Af'] * p['Cwy'] * Vw**2 * np.sin(wind_dir - psi) # 简化浪: 艏摇力矩用主导波频正弦近似 wave_N = p['wave_amp'] * np.sin(p['wave_omega'] * t - psi) dv += wind_Y / (m + my) dr += wave_N / (Izz + Jzz)

这种写法的好处是不动原有三块力,训练控制器时只动外扰项,方便做"有扰动/无扰动"的对照实验。注意风的来流方向要用相对风向而不是绝对风向——船在转向时 psi 在变,相对风向跟着变,很多人初改在这里把符号搞反,结果"顶风时船反而被吹向下游",一看就是风向没转对。

把上面几条串起来看,你不需要读懂全部代码,只要能定位"参数从界面到公式的路径"和"外扰加在哪一个力上",就具备了设计自己仿真实验的能力。接下来进入调试里更实际的部分——常见问题的排查顺序。

4. 避坑指南:仿真发散、响应迟钝与前端不刷新的五类问题

船舶运动仿真跑起来容易,跑"对"很难。下面五条都是我在类似项目上实际遇到、并且花过时间排查的问题,每一条按现象、原因、解决三个环节讲透。

4.1 跑着跑着速度发散成天文数字

现象:直航正常,一给稳定舵角,几十步之后 u 或 v 开始指数式增长,最后直接变成 nan。

原因:最常见是积分步长太大,显式欧拉法对这类刚性问题不稳定;其次是船体阻尼项的符号写反了——阻力项必须保证与速度方向相反,一旦符号错,速度越大推得越狠,必然发散。

解决:先检查 kt_maneuver.py 里的积分器步长。显式欧拉时 dt 取 0.05~0.1s 是安全的,改成 0.5s 大概率直接爆掉。再把 Xuu、Yv 这类阻尼项检查一遍,负号不能丢。如果想放开步长,把积分函数整体换成 RK4,dt 放到 0.2s 也稳,曲线还更光滑:

def rk4_step(f, state, delta, n, p, dt): k1 = f(state, delta, n, p) k2 = f(state + 0.5*dt*k1, delta, n, p) k3 = f(state + 0.5*dt*k2, delta, n, p) k4 = f(state + dt*k3, delta, n, p) return state + dt/6.0 * (k1 + 2*k2 + 2*k3 + k4)

换完之后,原本 0.5s 就炸的工况,0.2s 步长也能稳定跑完整个仿真时长。

4.2 给了大舵角,航向响应像在散步

现象:舵角给到 30°,船速掉得明显,但航向半天不转,回转半径比预期大好几倍。

原因:大概率是舵力被"无量纲化"缩水了。不少 MMG 实现把舵面积 AR 写成无量纲的(除以 L²),但舵力公式里的 rho 和 UR 都是国际单位,混用之后舵力小了三个数量级;另一种是 aH、wR 被设成 0,舵效被砍掉一大截。

解决:先做量级校验。随便取一组状态值,手算一次 FN = 0.5·rho·AR·UR²·fa·alphaR:以 30° 舵角、约 6~7 节船速、几平方米的舵面积为例,法向力应该在几十到上百千牛的量级。如果你算出来只有几牛,问题基本锁定在 AR 混用了无量纲值。把 AR 换成有量纲的平方米再跑,响应立刻正常。这种量级检查是排查模型类问题最快的手段。

4.3 界面有船形但轨迹不更新

现象:页面能渲染,船也画出来了,但位置和轨迹像被焊死一样,控制台还没报错。

原因:前端轮询的接口没被调用,或者后端仿真压根没在步进。Flask 开发服务器默认单线程,如果页面用同步请求、且模型循环在一个请求里全部跑完才返回,那前端拿到的永远是"最终态",看不到过程。

解决:常见做法是后端用独立线程跑仿真循环,最新状态写进全局字典,前端每 200~500ms 拉一次状态接口;或者让状态接口支持 step 参数,一次只前进 N 步,前端定时调用。我不推荐在浏览器里用 while 死循环等结果,会把开发服务器卡死。改完之后页面就能看到船在"动"了。

4.4 改了参数,重跑还是老结果

现象:把船长从 80 改成 120,重启服务再跑,页面输出完全没变化。

原因:一个坑是 Flask debug 模式下的代码缓存,另一个更隐蔽的是界面滑块有自己的默认值,页面初始化时把后端参数覆盖回去了。

解决:先在模型层把新参数打印出来,确认改的是这一份参数;再给接口加一个强制刷新参数的入口(比如 POST /reset 重新加载参数)。浏览器端做硬刷新清缓存。我自己有个习惯:每次改完参数,在日志里打一行[param] L=120.0 m=9.2e6,一眼就能确认改没改进去。看不到这行日志,就别开始调控制参数。

4.5 初始速度不为零,船自己漂起来了

现象:设了初始速度 u0=5.2 m/s,还没给舵角,船就开始横向漂移,航向也跟着偏。

原因:初始状态里 v 和 r 是 0,但 u 不为 0 时,哥氏项和船体非线性项并不自动平衡;更常见的是螺旋桨推力与船体阻力没配平,仿真会先花几十秒"自我适应"到一个平衡航速,这个过程看起来就是船在乱漂。

解决:先单独跑一条直航仿真,不给舵角,看稳态速度有没有回到你设定的服务航速。没回到就调整阻力系数 Xuu 或螺旋桨转速 n,直到直航速度收敛。这一步我叫它直航自平衡校验,是每次换船型之后必做的第一件事,永远先于任何控制实验。

最后说句实在话:船舶运动仿真里大多数"灵异现象",最后都能归到单位、符号、缓存三件事上。遇到问题别急着怀疑模型理论,先按这三类排查,往往十分钟内就有结果。

5. 从开环到闭环:给 MMG 仿真加一个 PID 航向保持控制器

5.1 先想清楚控制接口

开环仿真能看响应,但学船舶运动控制不能停在开环。改造原则很简单:不碰模型层,在仿真循环外面加控制器。每个周期拿到当前的 psi,控制器算出一舵令,替代你手动给的舵角喂给模型。

这么选的逻辑是:控制律、执行机构、测量噪声是三个独立的问题。先不引入噪声和舵机延迟,把纯反馈闭环调稳,再逐步叠加现实约束。如果你一上来就搞复杂的 LQR 加观测器,调不动的时候你根本分不清是模型问题还是控制问题。

5.2 PID 实现

一个够用的航向 PID 长这样:

class HeadingPID: def __init__(self, kp=1.5, ki=0.02, kd=0.8, max_rudder=35.0): self.kp, self.ki, self.kd = kp, ki, kd self.max_rudder = max_rudder # 度, 舵角限幅 self.integral = 0.0 def compute(self, psi, psi_ref, r, dt): # 误差归一化到[-pi, pi], 防止350度绕到10度算成340度 error = (psi_ref - psi + np.pi) % (2 * np.pi) - np.pi self.integral += error * dt self.integral = np.clip(self.integral, -10.0, 10.0) # 积分限幅 derivative = -r # 直接用艏摇角速度替代差分 rudder = self.kp * error + self.ki * self.integral + self.kd * derivative return float(np.clip(rudder, -self.max_rudder, self.max_rudder))

三个细节值得说透。第一,误差归一化那行是航向控制特有的:目标航向 350°、实际航向 10° 时,如果不归一化,误差会算成 340°,控制器会往错误方向打大舵;归一化后是 -20°,打左舵就能修正。第二,微分项用 -kd·r 而不是差分,因为 r 本来就是艏摇角速度测量值,等价于误差的微分还带符号,同时避开了数字差分的噪声放大,这是我在实船上换过的写法,比教科书形式稳得多。第三,积分限幅给到 ±10 就够,不给限幅,大风浪下舵角长时间饱和,退出饱和后超调极大。

5.3 三个要命的调参经验

第一条:先 P 后 PI。调参套路是 kd、ki 先归零,kp 从 0.5 开始往上加,看到航向能收敛;再单独加 kd 消振荡;最后加一点 ki 消稳态误差。三个参数同时调,调三天你都不知道是谁在起作用。这不是玄学,是控制回路调参的通用顺序。

第二条:静水直航不等于零舵角工作点。由于螺旋桨横向流和船体不对称,船在静水中保持直航通常需要一个小压舵角。你的控制器如果连这个工作点都稳不住,问题多半在积分项力度不够,或者误差归一化在作怪。

第三条:评价曲线别只看航向误差。把舵角输出和航向误差画在同一张图里,一眼就能看出控制器是不是在"疯狂抖舵"。抖舵的元凶通常是微分增益太大,或者轮询刷新率太低导致测量值呈阶梯状,微分项把阶梯噪声放大了。看到抖舵,先降 kd,再提高刷新率,别急着怀疑模型。

5.4 闭环数据记录

最后给一段闭环主循环的骨架,把控制量和状态一起落盘,后面画响应曲线、算 ITAE 指标、对比扰动实验都用它:

log = [] state = init_state(psi0=0.0, u0=5.2) psi_ref = np.radians(20.0) # 目标航向20度 controller = HeadingPID(kp=1.5, ki=0.02, kd=0.8) for step in range(int(t_end / dt)): psi = state[5] delta = controller.compute(psi, psi_ref, state[4], dt) state = rk4_step(ship_derivatives, state, delta, n_prop, params, dt) log.append([step * dt, state[3], state[4], state[5], delta])

跑完导出 CSV,横轴是时间,纵轴是航向、舵角、横坐标、纵坐标,一张图看全闭环行为。到这一步,界面里显示的是控制器在打舵,而不是你手动在打舵;所谓从开环到闭环,到这个循环才真正合上。

6. 交叉验证 MMG 仿真:用 Nomoto 一阶模型反推 K、T 参数

MMG 仿真参数对不对,不能靠"看着像"来确认,得有个能跟外部数据对上的客观指标。我用的办法是把 MMG 的阶跃响应降阶成 Nomoto 一阶模型,反推 K、T 两个参数,再跟公开的操纵性经验值比。

6.1 一个十分钟的数值实验

流程是这样的:在仿真里给一个 15° 阶跃舵角,分别左舵、右舵各跑一次,记录艏摇角速度 r(t)。稳态时 r 不再变化,Nomoto 方程 T·ṙ + r = K·δ 简化为 r_ss = K·δ,所以 K = r_ss / δ,注意 δ 要换算成弧度。再从响应曲线上找 r = 0.63·r_ss 对应的时刻,那就是一阶系统的时间常数 T。

然后对照这张参考表做判断:

检查项合格参考偏差时先查谁
左右 15° 回转 K 对称性两侧偏差 <5%螺旋桨横向力、初始压舵角
T 与船型关系T 约在 (1~3)·L/U 量级m、mx、Izz、Jzz
稳态回转直径约 3~5 倍船长AR、aH、fa 舵效参数
静水直航稳定性无舵角偏航 <1°/minYv、Nv 航向稳定导数

举个例子。某组参数跑出来稳态 r≈0.021 rad/s,δ=15°=0.262 rad,K≈0.080 /s;从响应曲线上读 T≈30s;船速 5.2 m/s、船长 75m,则 L/U≈14.4s,T 约为 2.1 倍,在合格区间内;稳态回转直径 U/r≈248m,约 3.3 倍船长,也符合货船量级。这组参数就可以认为是可信的,往上面叠控制器才有意义。

如果对不上,按表里最后一列查:K 偏小说明舵效弱,回舵面积和 aH;T 偏大说明船"迟钝",查惯性和附加质量;左右不对称则先查有没有给初始压舵角。

这套验证不只对 MMG 有效。Nomoto 一阶模型也可以反过来指导控制器设计——根据 K、T 估算闭环主导极点的位置,至少能判断出你的 PID 增益方向对不对。从那以后,我每次拿到任何船舶运动仿真资源,第一步永远是跑一条 15° 阶跃和一条 10°/10° Z 形,把 K、T 算出来跟已知船型对比。如果你手上正是这份 ship_mmg-master,解压后直接把这套流程当开机自检跑一遍,参数对得上再往上叠控制器。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进