简介:一份面向船舶工程与极地航行研究人员的数值模拟方法资源,聚焦破碎冰区船舶机动性能分析,将非光滑离散元法(NDEM)与三自由度MMG模型结合,解决低—中冰浓度下船舶操纵运动仿真难题。资源提供完整Python代码实现与逐段解释,覆盖冰场随机生成、船冰作用力计算、MMG运动方程求解、结果可视化全流程,并系统讨论冰浓度、冰尺寸、冰厚度、船速和舵角对转向灵活性的影响,推导冰力矩与冰阻力临界关系。压缩包仅含1个docx文档,大小54KB,以论文复现笔记形式组织,理论推导与可运行代码一一对应,便于读者边调试边理解模型原理。目前已有64人学习下载,适合具备一定编程和船舶工程基础的研发人员作为冰区操纵性仿真参考。
1. 把 NDEM 接进 3-DOF MMG 模型到底要解决什么
冰区船舶耐冰性仿真里最尴尬的事,莫过于 MMG 操纵性模型算船又快又稳,但面对碎冰和冰脊,经验公式根本估不出冰载荷;非光滑离散元(NDEM)能还原冰与船体碰撞碎断的细观过程,可把船当刚体边界时,又没人告诉它船正在往哪走。把 NDEM 和 3-DOF MMG 模型耦合,正是近年冰区操纵类论文里最常见的数值模拟方法:NDEM 算冰载荷,MMG 算船对载荷的运动响应,每个时间步交换一次力和状态。下文从两条线推进——MMG 方程离散、NDEM 接触求解——然后给出双向耦合的最小 Python 代码和参数表,覆盖论文复现时最容易出错的三个位置。
2. 3-DOF MMG 模型:运动方程离散与可直接跑的 Python 代码
2.1 坐标系与三自由度操纵方程
MMG(Maneuvering Modeling Group)是上世纪七八十年代提出的分离式操纵模型,核心思想是把船体水动力拆成船体、螺旋桨、舵三部分贡献,分别用约束模试验或 CFD 拟合多项式,再线性叠加。3-DOF 意味着忽略垂荡、横摇和纵摇,只保留纵荡(surge)、横荡(sway)和艏摇(yaw)。对冰区直航、避让和中等幅度操纵,这三个自由度已经够用,而且和 NDEM 交换载荷时物理量最干净:一个力矢量加一个力矩。
仿真里固定两套坐标系。大地坐标系记录船位和艏向,随船坐标系以重心 G 为原点、x 轴向船首,所有水动力都在随船系里表达。带附加质量的 3-DOF 运动方程写成:
(m + mx)·du/dt − (m + my)·v·r = XH + XP + XR + XI
(m + my)·dv/dt + (m + mx)·u·r = YH + YR + YI
(Izz + Jzz)·dr/dt = NH + NR + NI
等号右边前三个下标分别是 hull、propeller、rudder,最后一个下标 I 是冰载荷,也就是第四章耦合的入口。左边 −(m+my)vr 和 +(m+mx)ur 是随船系旋转产生的交叉项。做论文复现时,交叉项符号写反是第一个高频错误,典型表现是:直航时横荡速度 v 缓缓漂起来,而物理上对称直航的 v 应当恒为零。
2.2 船体、螺旋桨与舵的分离建模
船体水动力取低速多项式形式:
XH = X_uu·u|u| + X_vv·v² + X_rr·r² + X_vr·v·r
YH = Y_v·v + Y_r·r + Y_vv·v|v| + Y_rr·r|r|
NH = N_v·v + N_r·r + N_vv·v|v| + N_rr·r|r|
注意 Y 和 N 里一般不放大写 V·R 交叉项,因为常规斜航约束模试验测不到,强行加反而破坏同一组导数之间的拟合关系。螺旋桨推力按 XP = (1−tP)·ρ·n²·D_P⁴·KT(J) 计算,tP 是推力减额分数,KT 用进速比 J 的多项式逼近;舵力按平板翼公式 YR = −0.5·ρ·AR·CY·uR²·δ。复现时公式以原论文为准,但整组力的符号必须与随船坐标系自洽,否则第四章加的冰载荷方向会整体反转。
2.2.1 直接可运行的 MMG 步进类
import numpy as np class MMG3DOF: """3-DOF MMG 操纵模型,状态 [u, v, r, x, y, psi]""" def __init__(self, p): self.m = p['mass'] # 船体质量 kg self.mx = p['add_mx'] # 纵荡附加质量 kg self.my = p['add_my'] # 横荡附加质量 kg self.Iz = p['Izz'] # 艏摇惯矩 kg*m^2 self.Jz = p['add_jzz'] # 艏摇附加惯矩 kg*m^2 self.rho = p['rho'] # 水密度 kg/m^3 self.Dp = p['prop_diam'] # 螺旋桨直径 m self.tP = p['tP'] # 推力减额分数 self.c = p['hydro'] # 水动力导数 dict def hull_forces(self, u, v, r): c = self.c XH = c['X_uu']*u*abs(u) + c['X_vv']*v*v + c['X_rr']*r*r YH = c['Y_v']*v + c['Y_r']*r + c['Y_vv']*v*abs(v) + c['Y_rr']*r*abs(r) NH = c['N_v']*v + c['N_r']*r + c['N_vv']*v*abs(v) + c['N_rr']*r*abs(r) return XH, YH, NH def step(self, st, nps, delta, dt, F_ice=(0.0, 0.0, 0.0)): u, v, r, x, y, psi = st u = max(u, 1e-6) # 避免零速除零 XH, YH, NH = self.hull_forces(u, v, r) # 螺旋桨推力,KT 假设随进速比线性下降 KT = self.c['KT0'] - self.c['KT1'] * (u / (self.Dp * nps)) XP = (1.0 - self.tP) * self.rho * nps**2 * self.Dp**4 * KT # 舵力,uR 近似取 u uR = u YR = -0.5 * self.rho * self.c['AR'] * uR**2 * self.c['CY_d'] * delta NR = YR * self.c['xR'] XR = -0.5 * self.rho * self.c['AR'] * uR**2 * self.c['CX_d'] * delta**2 # 冰载荷直接加在右侧,随船系分量 Fx = XH + XP + XR + F_ice[0] Fy = YH + YR + F_ice[1] Mz = NH + NR + F_ice[2] # 显式欧拉,小步长下对操纵问题足够稳定 du = (Fx + (self.m + self.my) * v * r) / (self.m + self.mx) dv = (Fy - (self.m + self.mx) * u * r) / (self.m + self.my) dr = Mz / (self.Iz + self.Jz) u += du * dt; v += dv * dt; r += dr * dt psi += r * dt x += (u * np.cos(psi) - v * np.sin(psi)) * dt y += (u * np.sin(psi) + v * np.cos(psi)) * dt return np.array([u, v, r, x, y, psi])这份示例代码按最小可运行原则组织:F_ice 元组就是第四章耦合预留的接口,顺序是随船系下的 FX、FY、NZ。显式欧拉在 dt 不超过 0.1s 时够用;如果换成 RK4,注意把交叉项和冰载荷放在同一时刻求值,否则会出现分裂误差,表现为运动轨迹低频振荡。
2.3 一组用于自检的水动力导数量级
复现论文时最怕拿到一组不知道量级对不对的导数。下面给出一组无因次导数的典型量级,用来做程序自检,不是用来替代原论文参数。量纲恢复公式为:X = X′·0.5ρLdU²,Y = Y′·0.5ρLdU²,N = N′·0.5ρL²dU²。
| 无因次导数 | 典型量级 | 物理含义 |
|---|---|---|
| X_uu′ | -1.0e-3 ~ -2.0e-3 | 直航阻力二次项 |
| Y_v′ | -0.9e-2 ~ -2.0e-2 | 横荡力对 v 的导数 |
| Y_r′ | 0.5e-2 ~ 1.0e-2 | 横荡力对 r 的导数 |
| N_v′ | -1.0e-2 ~ -3.0e-2 | 艏摇力矩对 v 的导数 |
| N_r′ | -0.5e-2 ~ -2.0e-2 | 艏摇阻尼 |
如果从原论文抄来的导数量纲恢复后落在表外一个数量级以上,先回去检查无因次基准用的是船长还是水线长,这是二次复现最常见的参数错误来源。
3. NDEM 非光滑离散元:接触求解为何不需要弹簧刚度
3.1 与软球离散元的本质差别
常规软球 DEM 需要给每个接触做法向弹簧和阻尼器,刚度选大了时间步必须压到微秒级,选小了冰排会被压穿。NDEM 属于非光滑接触动力学(NSCD),由 Moreau 与 Jean 在 1980-1990 年代建立,接触条件直接在冲量层面写成互补形式,关键点是不需要任何接触刚度,时间步可以放到毫秒级。对冰区模拟这是决定性的:冰排尺度动辄几十米,粒子数百上千个,软球 DEM 的时间步根本跑不完一个操纵回合。
NDEM 里颗粒可以是圆盘、多面体或黏接块体。模拟碎冰航道的常见做法是:二维圆盘表示碎冰块,船体用一组线段边界表示;要模拟平整冰时,把冰粒子用黏接键连成冰场,键的强度决定弯曲和压溃行为。
3.2 Signorini 互补条件与库仑摩擦圆锥
每个接触要解三个未知量:法向冲量 Pn、切向冲量 Pt、接触点相对速度。约束有两个。第一是法向不可贯入且不可拉张,写成 Signorini 条件:间隙 g ≥ 0,Pn ≥ 0,g·Pn = 0。第二是库仑摩擦:|Pt| ≤ μ·Pn,且当 |Pt| < μ·Pn 时切向相对速度为零。
这套条件里没有任何刚度参数,接触力的上限由摩擦锥限定,具体数值由动量方程反解。这正是“非光滑”的含义:接触过程在时间尺度上被视为瞬间完成,速度允许跳跃,位移保持连续。
3.3 单步求解的投影扫描迭代
常见做法是 Moreau-Jean 时间步进,每个时间步内对全部接触约束做多次扫描迭代。算法骨架分四步:根据当前速度预测每个接触点的法向与切向相对速度;逐接触求解局部冲量,把法向冲量投影到非负半轴、切向冲量投影到摩擦锥;用冲量更新两侧颗粒速度;重复扫描直到残差收敛。投影操作是关键——它把互补条件和摩擦锥直接变成代码里的两行限幅。
3.4 NDEM 单步的示例代码
import numpy as np MU = 0.3 # 冰-船摩擦系数 N_SWEEP = 20 # 每时间步扫描次数 def step_ndem(parts, contacts, dt): """速度级 NSCD 投影迭代,只处理平动,转动同理""" for _ in range(N_SWEEP): for c in contacts: i, j = c.i, c.j n, t = c.n, c.t # 法向指向 i,切向与之正交 vn = np.dot(parts[i].v, n) - np.dot(parts[j].v, n) vt = np.dot(parts[i].v, t) - np.dot(parts[j].v, t) M_inv = parts[i].m_inv + parts[j].m_inv # 法向:Signorini 投影到非负半轴 Pn = -vn / M_inv if Pn < 0.0: Pn = 0.0 # 切向:投影到库仑摩擦锥 Pt = -vt / M_inv Pt_max = MU * Pn Pt = max(-Pt_max, min(Pt_max, Pt)) # 用冲量更新速度 dv = (n * Pn + t * Pt) * M_inv # 此处除以总逆质量后再乘各自逆质量 parts[i].v += n * Pn * parts[i].m_inv + t * Pt * parts[i].m_inv parts[j].v -= n * Pn * parts[j].m_inv + t * Pt * parts[j].m_inv for p in parts: p.pos += p.v * dt return parts代码里 Pn 的硬性非负和 Pt 的硬性限幅,就是“非光滑”的全部实现,没有任何刚度参数可调。严格 NSCD 还会在法向冲量中加入恢复系数项并把间隙量纳入预测,这里写的是最常见的速度级投影骨架。注意 dv 那行只作注释示意,实际更新用后面两行,分别按各自逆质量分配冲量。
3.5 NDEM 参数表
| 参数 | 符号 | 碎冰模拟常用范围 |
|---|---|---|
| 摩擦系数 | μ | 0.1 ~ 0.5 |
| 法向恢复系数 | e | 0 ~ 0.3 |
| 冰密度 | ρ_ice | 880 ~ 920 kg/m³ |
| NDEM 时间步 | dt_ndem | 1e-4 ~ 5e-3 s |
| 扫描次数 | N_sweep | 10 ~ 30 |
摩擦系数和冰密度直接影响载荷幅值,论文复现对不齐试验数据时,先检查的就是这两个量而不是步长。扫描次数 N_sweep 决定迭代收敛程度,小于 5 时接触力会出现明显的步间抖动。
4. NDEM-MMG 双向耦合:时间步配比与冰载荷传递代码
4.1 为什么用显式弱耦合
MMG 不关心冰载荷从哪来,NDEM 也不懂操纵。把两个模型接起来,只需在每个宏观时间步交换两类数据:NDEM 把冰对船体边界的接触力合成为力与力矩,传给 MMG 当作外力;MMG 把船体位置、速度和艏向传回 NDEM,更新船边界。这个结构叫显式弱耦合,两个模块各自独立调试,论文复现里绝大多数实现都走这条路线,可靠且容易定位发散来源。
4.2 时间步长配比与子循环
两个模型的自然时间步差一到两个数量级:MMG 用 0.02~0.1s,NDEM 用 1e-4~5e-3s。最稳妥的做法是子循环:每个 MMG 步内,NDEM 固定小步长跑 N 步,船体边界位置按 MMG 起步与落步状态线性插值;整个子循环内的冰载荷求平均后再传给 MMG。子循环比“两边统一用大步长”稳得多,也比每步实时同步更容易排查发散,代价只是解耦了一点接触时序,对宏观操纵量影响可忽略。
4.3 耦合主循环与坐标变换代码
def world_to_body(F_xy, psi): """大地系合力转随船系:绕 z 轴旋转 -psi""" c, s = np.cos(psi), np.sin(psi) return np.array([c * F_xy[0] + s * F_xy[1], -s * F_xy[0] + c * F_xy[1], F_xy[2]]) def interpolate(st_prev, st_curr, frac): """位置与艏向线性插值,供子循环更新船边界""" x = st_prev[3] + (st_curr[3] - st_prev[3]) * frac y = st_prev[4] + (st_curr[4] - st_prev[4]) * frac psi = st_prev[5] + (st_curr[5] - st_prev[5]) * frac return x, y, psi def run_coupled(mmg, ndem, segments, dt_m, dt_n, T_total, nps, delta): st = np.array([U0, 0.0, 0.0, 0.0, 0.0, 0.0]) n_sub = max(1, int(round(dt_m / dt_n))) t = 0.0 while t < T_total: st_prev = st.copy() F_world = np.zeros(3) for s in range(n_sub): frac = (s + 1) / n_sub bx, by, bpsi = interpolate(st_prev, st, frac) # 船体边界是 NDEM 里的移动刚体线段组 ndem.update_boundary(segments, bx, by, bpsi) ndem.step(dt_n) F_world += ndem.boundary_force_torque() F_world /= n_sub # 子循环内取平均,抑制锯齿 F_body = world_to_body(F_world, st[5]) st = mmg.step(st, nps, delta, dt_m, F_body) t += dt_m # 此处落盘轨迹、冰载荷序列供后处理 return st主循环顺序是先子循环收冰载荷,再转随船系,再走 MMG。F_world 是大地系下 NDEM 对船体边界所有接触力的合力与合力矩;world_to_body 让它进入第二章运动方程的右侧,符号由旋转矩阵保证一致性。若发现冰载荷符号整体反转,检查这里而不是检查 NDEM 接触。
4.4 耦合参数建议
| 参数 | 建议值 | 说明 |
|---|---|---|
| dt_m | 0.02 ~ 0.1 s | MMG 宏观步长 |
| dt_n | 1e-4 ~ 5e-3 s | NDEM 子步长 |
| n_sub | 10 ~ 100 | 取 dt_m / dt_n 四舍五入 |
| 接触检测 | 每 NDEM 步一次 | 冰排相对船体运动快时不可省略 |
| 载荷平均 | 子循环内算术平均 | 消除单步接触脉冲带来的高频振荡 |
子循环内载荷取平均是稳定性的关键。如果实测加速度序列出现相邻步正负交替,说明单步冰载荷没有被平均掉,需要在 collect 阶段再加一个滑动平均窗口,窗口宽度取 3~5 个 NDEM 步。
5. NDEM-MMG 数值模拟的复现核对:三个算例与三个必调参数
5.1 三个可复现的核对算例
算例一是无冰直航。令 F_ice 恒为零,船应稳定在设计航速 U0,横荡速度 v 与艏摇角速度 r 保持零。若 v 或 r 漂移,先查第二章交叉项符号和第三章导数量纲,这两个位置吃掉了复现者一半的调试时间。
算例二是回转圈试验。稳定打舵 δ = ±35°,稳定后的回转直径应在 2~4 倍船长之间。直径偏大说明 N_r 或 Y_v 的量级不对;直径随时间持续收窄,多半是 dt_m 太大引入了数值阻尼,把 dt_m 缩到 0.02s 再观察。
算例三是单冰碰撞动量守恒。让一条冰排朝静止船边界撞去,统计碰撞前后船加冰系统总动量变化,应等于边界外力冲量,误差控制在 1% 以内。这个算例同时检验 NDEM 接触迭代收敛性和耦合里力传递的方向符号,比直接看冰载荷曲线更早暴露问题。
5.2 最值得先调的三个参数
NDEM 摩擦系数 μ 对载荷幅值最敏感,0.1 与 0.5 之间的峰值能差出一倍,曲线对不上时先扫 μ 而不是加密网格。时间步配比 n_sub 决定接触序列的采样密度,n_sub 偏小会出现锯齿状冰载荷,偏大则浪费算力;用算例三做收敛性检查,逐步放大 n_sub,直到动量误差不再明显下降。第三个是法向投影的间隙容差,NSCD 理论假设刚性接触,实现里要给一个很小的间隙容许值判断是否成对接触,容差设太大会让冰排“悬浮”在船边界附近载荷偏小,设太小则颗粒在边界上来回震颤。
提示:复现这类论文不需要深度学习框架,核心工作量就在三处——MMG 方程离散、NDEM 接触迭代、两个模型的时间步接口。先把无冰算例跑稳再接冰,是排查效率最高的调参顺序。
最后留一个对称性检查技巧:用 180° 对称的初始条件各跑一次——艏向从 0° 和 180° 起算、舵角取相反数,两条轨迹必须关于船中平面镜像对称。这条检查能一次性暴露坐标系旋转方向、力传递正负号和多处交叉项符号错误,是接任何耦合代码时最先该做的冒烟测试。
本文还有配套的精品资源,点击获取