简介:针对海洋学与信号处理研究中的二维海面仿真需求,这份MATLAB代码资源提供基于功率谱模型(如JONSWAP谱)与蒙特卡罗方法的海浪模拟实现,面向从事海洋遥感、雷达探测及相关环境模拟的研究人员和工程师。压缩包共两个文件,均为MATLAB脚本,整体仅2KB,小巧但核心功能完整。其中主脚本负责生成二维随机海面,支持自定义波长、频率、风速等关键参数,并直接输出海面高度图;另一脚本用于分析海浪的反射与折射效应,能够直观展示波浪遇障碍物或地形变化时的传播路径调整。通过这套代码,读者可直接在MATLAB中运行模拟,系统学习从参数设置、功率谱计算到随机相位合成海面的完整流程,同时理解反射效应对波浪传播的影响,为海洋动力学研究和预测模型开发提供实用基础。目前已有394人浏览学习,适合需要快速上手海面模拟的初学者或希望扩展仿真维度的进阶开发者。
1. 粗糙海面的蒙特卡罗合成:roughsea.zip 到底在算什么
把海面当作随机过程而不是规则的正弦波,是海洋遥感、SAR图像仿真与雷达回波模拟里最重要的一次思维切换。roughsea.zip 提供的两个 MATLAB 脚本正是围绕这一点展开:roughsea.m 输入风速、风向与模拟区域尺寸,输出符合统计特征的二维海面高度场;OceanRefalction.m 再叠加反射与折射效应,把结果向近岸场景推进。适合的人群很明确——需要批量生成海面样本做蒙特卡罗实验,又不想每次从谱公式开始推导的人。但这两个脚本也逼着使用者回答一个问题:你选的谱和方向扩展函数,与你的风速参数是否自洽?
2. JONSWAP 谱到二维方向谱:海浪能量如何被参数化
2.1 为什么用连续谱而不是有限正弦叠加
海面高度场在数学上可以展开成无穷多个平面波的叠加,但直接把几百个正弦波相加并不会得到可信的海面。原因是真实的海洋波分量相位几乎随机,固定相位叠加得到的只是特定时刻的“个例”,不具备统计代表性。信号处理里更稳的做法是把能量密度按频率或波数连续描述,把相位留给随机数生成器处理。这样每次运行脚本拿到的都是同一能量分布下的不同实现,批量做蒙特卡罗实验时,样本之间的差异才是真实海况差异的体现。
因此,模拟的第一步不是画波,而是先决定能量在频域里长什么样。这个决定权通常交给海浪谱模型,roughsea.m 里默认走的路线是 JONSWAP 谱。
2.2 JONSWAP 谱的公式逐项拆解
JONSWAP 谱来自北海联合海浪计划,适合受限风区、风浪未充分发展的场景,公式写作:
Sω(ω) = α·g²·ω⁻⁵·exp(-5/4·(ωm/ω)⁴)·γ^exp(-(ω-ωm)²/(2σ²ωm²))
其中 α 是 Phillips 常数,与风速 U10 和风区长度 fetch 的关系是经验式 α = 0.076·(U10²/(g·fetch))^0.22;ωm 是谱峰角频率,决定主波周期;γ 取 3.3 作为峰增强因子;σ 在 ω ≤ ωm 时取 0.07,否则取 0.09,用来控制谱峰两侧的宽度差异。如果把 γ 视为 1 并换用 PM 谱自带的 α 表达式,公式就退化成接近 Pierson-Moskowitz 谱的形式,适合开阔大洋的成熟涌浪。
选择原则通常是:受限风区、近岸风浪用 JONSWAP,边界条件信息明确的数值实验用 PM。实际测试时可以直接对上表格里的参数,验证脚本输出是否在合理范围:
| U10 (m/s) | fetch (km) | α 参考值 | 谱峰频率 fm (Hz) | 谱峰周期 Tp (s) |
|---|---|---|---|---|
| 5 | 10 | 约 0.012 | 约 0.447 | 约 2.2 |
| 10 | 20 | 约 0.014 | 约 0.281 | 约 3.6 |
| 15 | 50 | 约 0.014 | 约 0.181 | 约 5.5 |
这张表可以直接用来做粗校验。如果脚本输入 U10=10、fetch=20000,输出场的主波周期偏离 3.6 秒太多,优先检查 ωm 公式里 fetch 和 U10 的量纲。
2.3 方向扩展函数与二维谱的组装
一维频谱只描述能量随频率的分布,不携带方向信息。要生成二维海面,还必须把能量按波向展开,最常用的是 cos 的 2s 次方方向扩展函数:
D(θ) = C·cos²s((θ-θ0)/2)
其中 θ0 是主风向,常数 C 由 ∫D(θ)dθ=1 决定。s 越大,能量越集中在主风向附近;s 越小,波浪越呈现多向扩散。Mitsuyasu 的经验关系认为 s 随频率变化,低频段方向性强,高频段方向分布发散,但工程仿真里经常对全频段取常数,比如 s=8 到 20。
组装二维谱时有个容易出错的坐标变换:由角频率谱 Sω 转到波数谱 Sk 需要乘 |dω/dk| = g/(2ω),这是深水色散关系 ω=√(gk) 的导数;而在 (kx, ky) 平面用极坐标表示时,面积元 dkx·dky = k·dk·dθ,所以二维谱还要除以波数 k。两步缺失任何一步,海面总能量都会差一个量级。方向扩展函数归一化的常见实现是离散积分:
% 方向扩展函数 D(theta) 的向量化计算 theta = linspace(-pi, pi, 361); theta0 = 30 * pi/180; % 主风向,单位弧度 s = 8; % 方向集中度,粗略仿真取常数 D = cos((theta - theta0)/2).^(2*s); dtheta = 2*pi/360; % 角度步长 D = D / (sum(D) * dtheta); % 归一化,保证积分为 1代码的逻辑是先把 cos²s 方向权重铺到整个角度范围,再用矩形法做离散积分归一化。dtheta 是相邻角度采样点的间隔,sum(D) * dtheta 近似 ∫D(θ)dθ。如果这里不归一化,方向谱总能量会随 s 变化,导致同一风速在不同 s 下生成的海面有效波高不一致。
3. roughsea.m 的波数域实现:从功率谱到二维高度场
3.1 模拟场的基本流程与变量定义
roughsea.m 的套路是四步:定义网格与物理参数,计算二维方向谱,用蒙特卡罗法抽取随机相位,再用 ifft2 合成高度场。变量分两类:网格参数决定空间分辨率和区域尺寸,物理参数决定海浪的统计特征。常用的默认参数组合如下:
| 变量 | 含义 | 典型值 |
|---|---|---|
| M, N | 网格数量 | 512 × 512 |
| dx, dy | 空间采样间隔 (m) | 2.0 |
| U10 | 10m 高度风速 (m/s) | 10 |
| theta0 | 主风向 (rad) | 30° 换算弧度 |
| fetch | 风区长度 (m) | 20000 |
| gamma | 峰增强因子 | 3.3 |
空间采样间隔 dx=2m 时,Nyquist 波长为 4m,风浪谱的主要能量段可以被覆盖;模拟区域 1024m 大约容纳 50 个主波长,能明显减轻边界伪周期的影响。
3.2 roughsea.m 的核心实现代码
下面这段代码完整复现了从参数到海面高度场的过程,注释里标出了关键转换步骤:
% roughsea.m 的波数域合成主体 U10 = 10; fetch = 20000; theta0 = 30 * pi/180; M = 512; N = 512; dx = 2; dy = 2; Lx = M*dx; Ly = N*dy; g = 9.81; % 1) 构造波数网格,注意 fft 的频率坐标习惯 kx = 2*pi*(-M/2:M/2-1)/Lx; ky = 2*pi*(-N/2:N/2-1)/Ly; [KX, KY] = meshgrid(kx, ky); K = sqrt(KX.^2 + KY.^2); K(K == 0) = 1e-10; % 防止零波数处除零 % 2) 一维 JONSWAP 谱:先算角频率谱,再转到波数谱 omega = sqrt(g*K); % 深水色散关系 alpha = 0.076 * (U10^2/(g*fetch))^0.22; omega_m = 2*pi*3.5*g/U10 * (g*fetch/U10^2)^(-0.33); sigma = 0.07 + 0.02*(omega > omega_m); % 峰两侧不对称 Sw = alpha * g^2 ./ omega.^5 ... .* exp(-5/4*(omega_m./omega).^4) ... .* 3.3.^exp(-(omega-omega_m).^2./(2*sigma.^2*omega_m^2)); Sk = Sw .* (g./(2*omega)); % 频率谱转波数谱 % 3) 方向扩展函数,组装二维方向谱 theta = atan2(KY, KX); s = 8; Dtheta = cos((theta - theta0)/2).^(2*s); Dtheta = Dtheta / sum(Dtheta(:)); % 网格平均近似归一化 S2D = Sk ./ K .* Dtheta; % 极坐标面积元修正,除以波数 % 4) 蒙特卡罗随机相位,ifft2 恢复空间域 phi = 2*pi*rand(M, N); Amp = sqrt(S2D .* (2*pi/Lx) .* (2*pi/Ly)) .* exp(1i*phi); z = real(ifft2(Amp)) * M * N; % 乘回 M*N 抵消 ifft2 归一化 % 5) 显示 imagesc((0:M-1)*dx, (0:N-1)*dy, z); axis xy; colormap(jet); colorbar; xlabel('x (m)'); ylabel('y (m)'); title('二维海面高度 (m)');需要说明的参数和逻辑:第一步的波数网格必须用 fft 的坐标习惯,-M/2:M/2-1经过2*pi/Lx缩放后,零频在数组中心位置。第二步的Sk = Sw .* (g./(2*omega))来源于 dω/dk,这一步漏掉的话,高频段能量会被严重放大。第三步的S2D = Sk ./ K .* Dtheta是极坐标修正,Dtheta 按网格求和归一化属于近似做法,严格做法是按角度解析积分归一化,但对视觉效果影响很小。第四步的相位 phi 在 [0, 2π) 均匀随机取值,保证了海面高度场近似服从高斯分布。
3.3 网格分辨率与伪周期:两个最常见的坑
第一个坑是伪周期。当模拟区域只容纳几个主波长时,图像左右边界会明显看出重复,像瓷砖拼接。处理办法是把区域扩大一到两倍再裁剪局部,或者把网格数提高到 1024,让主波在区域内出现足够多的周期。第二个坑是栅格状纹理。如果零波数处处理不当,低频能量堆积在网格原点,会看到斜对角条纹。解决方法是在波数网格构建时就按fftshift的习惯排列,确保零频点位于数组中心。
提示:如果发现不同网格数下有效波高变化明显,优先检查方向谱组装时是否漏掉了
Sk ./ K这一步,这是谱域合成最常见的能量泄漏点。
4. OceanRefalction.m 的反射与折射叠加:边界条件如何进入海面
4.1 反射系数决定叠加比例
当海浪遇到海堤、船体或陡峭岸线时,入射波被边界反弹,与后续入射波叠加形成驻波成分。OceanRefalction.m 这类脚本处理的方式是把原始海面场按镜面原理翻转,再乘以反射系数 R 叠加回去。R 取决于边界对波能的吸收能力,不同岸线类型的参考范围如下:
| 边界类型 | 反射系数 R |
|---|---|
| 垂直光滑混凝土海堤 | 0.8 ~ 1.0 |
| 块石护岸 | 0.4 ~ 0.6 |
| 沙质缓坡海岸 | 0.1 ~ 0.3 |
反射系数不是随便拍的。垂直光滑墙反射强,驻波明显,波高可能增大到入射波的两倍;砂质缓坡消耗能量多,反射场对叠加结果影响小。做雷达回波仿真时,R 选错会导致近岸区域回波强度分布失真。
4.2 把入射场与反射场叠加起来的代码
叠加的常见实现是在空间域直接操作高度场矩阵:
% OceanRefalction.m 风格的入射加反射叠加 R = 0.6; % 反射系数,块石护岸参考值 z_inc = z; % 来自 roughsea.m 的入射场 % 假设海堤沿 y 轴,反射波在 x 方向反向 z_ref = R * fliplr(z_inc); % 镜面反射,等效于 kx 反号 % 只让近岸区域参与反射,用距离窗限制影响范围 dist = (0:N-1)*dy; % 到边界的距离,单位 m mask = exp(-dist/500); % 500m 衰减长度 z_total = z_inc + R * fliplr(z_inc .* mask);这里的关键点是fliplr做了空间域镜像,等效于波数 kx 变号,即入射波传播方向被翻转为反向出射波。mask 用指数衰减窗限制反射场的空间范围,模拟“只有靠近障碍物的区域才存在明显反射”的物理现象。衰减长度 500m 是经验值,结构物越高大、波越长,衰减长度应该增大。z_inc 与 z_ref 线性相加得到驻波场,驻波节线位置会出现在固定 x 坐标处,叠加后的结果可直接用surf(z_total)查看。
4.3 折射的简化近似与适用范围
浅水折射在物理上的表现是波向等深线法线方向偏转,波长变短,波高重新分布。二维谱域里可以做一个简化版本:把方向谱 S2D 按地形梯度旋转一个角度,再用插值重采样回原始网格。具体做法是构造旋转矩阵作用于波数坐标,griddata或interp2重新插值。此方法只适合地形变化平缓的近岸模拟,处理不了绕射、破碎波和非线性波波作用,所以它的适用范围主要限定在 SAR 图像仿真、雷达海洋杂波建模这类对精度要求不苛刻的场景。海洋工程结构荷载计算需要更严格的内波模式,这个脚本深度不够。
5. 动态海面序列与浪高标定:让模拟场经得起验证
5.1 用色散关系把静态场推成时间序列
静态海面只是一张空间随机图,真实海面每个波分量都按色散关系 ω=√(g|k|) 随时间演化。把生成的复振幅 Amp 乘上相位旋转因子,就能得到连续的海浪动画:
% 用色散关系做时间演化,生成动态海面序列 t = 0:0.5:30; % 时间序列,步长 0.5s for k = 1:numel(t) phase_rot = exp(1i * sqrt(g*K) * t(k)); zt = real(ifft2(Amp .* phase_rot)) * M * N; surf((0:M-1)*dx, (0:N-1)*dy, zt, 'EdgeColor', 'none'); view(2); caxis([-3 3]); drawnow; end时间步长取 0.5s 时,每帧之间主波移动约 1.8m,肉眼能看到连续传播效果。如果想要平滑动画,步长减到 0.25s 即可,代价是计算量翻倍,MATLAB 2026b 对这类循环中反复调用ifft2的多核优化明显,批量渲染时建议用parfor预生成所有帧再回放。
5.2 用谱矩验证有效波高
有效波高 Hs = 4√m0,其中 m0 是方向谱的零阶矩,即对二维谱做全波数积分:
% 从谱零阶矩反算有效波高 m0 = sum(S2D(:)) * (2*pi/Lx) * (2*pi/Ly); Hs = 4 * sqrt(m0); fprintf('有效波高 = %.2f m\n', Hs);按 U10=10、fetch=20000 的经验数据,Hs 应在 2m 量级。如果算出来偏差超过 20%,先检查Sk ./ K这一步是否丢失,再看方向谱归一化是否正确。固化了这套校验流程后,模拟出的海面场可以直接作为图像处理、目标检测算法的仿真输入。
5.3 固定种子做参数扫描
批量生成样本时,把随机种子设成可配置参数,然后对 U10 和 fetch 做扫描,用上面提到的 m0 校验公式画出 Hs-风速曲线。曲线和 JONSWAP 经验值对上之后,再投入生产级批量仿真。如果你习惯用 Codex 这类脚本化工具跑参数扫描,把种子暴露为参数会让问题定位快得多。最后提醒一句:固定随机种子、先让有效波高实测值与经验公式对齐,再谈纹理细节,这是海面模拟从“能画出图”到“能用于仿真”的分水岭。
本文还有配套的精品资源,点击获取