简介:面向舰船噪声仿真与信号处理研究,这份MATLAB源码资源可用于模拟螺旋桨、机械振动及流体动力等典型舰船噪声,支持通过参数调整适配不同工况,适合船舶工程、水声工程及相关方向的初学者和工程师快速入门。压缩包内共1个.m文件,大小约692B,程序以MATLAB函数形式封装,虽为轻量级代码,但涵盖噪声源识别、物理模型构建、滤波与频谱分析、仿真结果可视化等核心环节,结构清晰,便于阅读、调试和二次开发。目前已有568人学习浏览,具有不错的参考价值。读者可获得可直接运行的.m源码及完整的舰船噪声建模思路,帮助理解傅里叶变换、短时傅里叶变换等信号处理方法在噪声分析中的实际应用;程序中预留了参数调整与优化空间,经过测试,可用于舰船设计、噪声控制以及反探测策略研究,为相关实验与工程实践提供数据支撑和灵感。
1. 舰船噪声仿真:ship noise 建模不是把白噪声调大声
做被动声呐算法和声呐仿真平台的人,大概率都遇到过同一个尴尬:实测数据不够。租船、布放水听器、跑不同航速和航道,成本高周期长,很难凑齐低航速、深潜、不同海况的组合。所以绝大多数算法训练集和系统级仿真平台里的舰船噪声,都是建模合成出来的。舰船噪声建模要解决的核心问题不是“像不像噪声”,而是要复现舰船信号在频域和包络域的三类可判别特征:机械线谱、空化连续谱、螺旋桨调制谱。网上流传的 jianchuanzaoshengxinhao.rar 这类舰船噪声仿真资料包,基本就是围绕这三类成分做合成,但固定脚本换不了航速、转速和船型参数。下面这套链路从谱型定义到时域合成再到验证,可以在本地跑通,参数全部可调。
2. 舰船噪声建模的三类频谱成分:线谱、连续谱与调制谱
2.1 机械线谱:轴频、叶频和它们的梳状结构
舰船机械噪声来自主机、辅机、减速齿轮、轴系轴承等旋转与往复部件。这些部件的运动是周期性的,辐射到水里的能量集中在基频和谐波上,表现为功率谱上一根根离散谱峰,也就是线谱。最常用的两个基准频率是轴频和叶频:轴频直接由转速决定,shaft_f = rpm / 60,120 r/min 的主机轴频就是 2 Hz;叶频再乘螺旋桨叶片数,blade_f = blades * shaft_f,五叶桨对应 10 Hz。线谱并不只出现在这两个频率上,齿轮啮合频率、辅机振动频率会在几百赫兹到几千赫兹产生各自的离散谱线,合成后在 LOFAR 图上看到的就是一组等间隔的“梳状”线族。四冲程柴油机发火频率约按N*C/120估算,二冲程按N*C/60,其中 N 是转速、C 是缸数。
实际测量里线谱不是理想无穷窄的。主机转速不稳定、轴系弹性扭转,会让中心频率出现 ±1% 量级的缓慢波动,谱线本身也有一定宽度。工程上习惯用 Q 值描述线谱宽度:Q 等于中心频率除以 3dB 带宽。低频轴频线谱 Q 值可到 100 以上,几百赫兹的机械线谱 Q 值多在 30~80 之间。仿真时如果全部用理想正弦叠加,得到的是无穷窄谱线,一加短时傅里叶窗就会被抹平,和实测对不上。更常见的做法还是用 Lorentz 线型,在频域精确控制带宽:
S(f) = A * (b/2)^2 / ((f - f0)^2 + (b/2)^2),其中b = f0 / Q。
注意线谱频率不是固定的。仿真里要保留“真实感”,可以给每根线谱的中心频率加一个低通滤波后的随机游走,让 LOFAR 图上的竖线带一点缓慢漂移,而不是完全笔直。
2.2 螺旋桨空化的连续谱:谱峰位置与高频滚降
螺旋桨在较高转速下叶梢附近压力降低,水汽化形成空泡,空泡破裂产生大量宽带脉冲,这就是空化噪声。它是舰船辐射噪声连续谱的主要来源,覆盖范围可以从几十赫兹一直延伸到几十千赫兹。空化噪声的典型谱型分三段:低频段谱级随频率上升,中频段到达峰值,高频段以接近f^-2的斜率滚降,折算下来大约是每倍频程下降 6 dB。峰值频率大多落在 100 Hz~1 kHz 之间,航速越快峰值越高、整体谱级越高。
深度影响的是空化起始条件。水深增加,静水压升高,螺旋桨要在更高转速下才开始空化,所以同一条船、同一个航速下,深潜时连续谱级通常低于浅航。工程上常用两个简化趋势:航速每倍增,宽带总声级上升约 6~9 dB;深度每增加一个量级,连续谱级下降若干 dB。整理成表格如下。
| 频谱成分 | 频域特征 | 关键参数 | 对应物理源 |
|---|---|---|---|
| 机械线谱 | 离散峰,Q 值 30~100+ | 轴频、叶频、谐波数 | 主机、辅机、齿轮、轴系 |
| 空化连续谱 | 带通形状,高频约 f⁻² 滚降 | 峰值频率、滚降斜率、谱级 | 螺旋桨空泡破裂 |
| 调制谱 | 包络谱上离散峰 | 叶频、谐波、调制深度 | 叶片周期切割空化区 |
空化连续谱与实际信号的差别主要在低频段。低速航行时,因船体结构辐射和推进器脉动,低频段往往叠加了 1/f 型能量,谱级并不是单调下降的。仿真时不必追求一个复杂半经验公式,把“上升—峰值—滚降”三段趋势表达清楚,比套一个无法调参的全频带公式更实用。
2.3 叶片周期调制:调制谱和 DEMON 谱的关系
空化噪声的幅度并不平稳。螺旋桨每转一圈,叶片依次扫过相同角度位置,空化强度和辐射条件周期性变化,宽带噪声的包络就被“捏”出周期起伏,这就是螺旋桨调制。包络基频是叶频,通常还带二三次谐波。这个调制在普通功率谱里看不出来,必须用幅度解调后再做谱分析,也就是 DEMON 谱分析。
这里必须分清两个容易混淆的概念:叶频处既可能有一条真正的线谱,也可能有宽带噪声包络的调制峰。前者在时域信号的直接 FFT 功率谱上可见;后者出现在包络的谱分析结果里,两者物理意义完全不同。仿真时线谱和连续谱要分开构造,调制只加在连续谱分量上,否则后续做目标识别特征提取时,会把线谱特征和调制特征搅在一起。
调制深度 m 也不是全频带一致的。空化充分的高频段调制深,低频段调制浅;同一根轴上,带通滤波取不同频带,DEMON 峰的高度会明显不同。这解释了为什么后面验证 DEMON 时要选带通频段,而不是直接分析全频带信号。
3. 用 Python 合成舰船噪声仿真信号:频域构造加 IFFT
3.1 直接构造 PSD 比滤波合成更可控
常见时域合成是白噪声过 FIR/IIR 滤波器成形,好处是实时性高,但舰船线谱带宽窄、Q 值高,滤波器阶数不够时线谱会被明显抹平,调阶数又会拖慢运算。离线仿真更常用的做法是频域法:把目标 PSD 按离散频率点算好,乘随机相位后做 IFFT 得到时域信号。频域法能精确控制每个频点功率,窄带线谱不会因滤波器瞬态失真;换一组随机种子就能得到同谱型的新样本,适合批量生成训练数据。
虽然频域法要一次性做整段傅里叶变换,但 60 秒 20 kHz 采样率的双精度数据不过百万级点数,内存完全不是问题。这个思路和很多舰船噪声仿真资料包里预生成信号的做法一致:先定谱型,再反变换出时域。
3.2 连续谱与线谱的频域构造代码
下面是一个完整可运行的最小实现。连续谱和线谱分别构造 PSD,分别做 IFFT,最后在时域对连续谱叠加调制,再和线谱相加。
import numpy as np def lorentz(f, f0, A, Q): # Lorentz 峰形:A 控制相对强度,Q 控制 3dB 带宽 b = f0 / Q return A * (b / 2) ** 2 / ((f - f0) ** 2 + (b / 2) ** 2) def cont_shape(f, f_low, f_peak, gamma): # 低频以 f^2 上升,越过 f_peak 后按 f^-gamma 滚降 y = (f / f_low) ** 2 / (1 + (f / f_low) ** 2) y *= 1.0 / (1 + (f / f_peak) ** gamma) return y def make_ship_noise(fs=20000, dur=60, rpm=120, blades=5, speed=12, depth=50, amp_cont=1.0, seed=42): rng = np.random.default_rng(seed) nfft = int(fs * dur) freqs = np.fft.rfftfreq(nfft, 1 / fs) # 空化连续谱,归一化后按航速和深度调谱级 psd_cont = cont_shape(freqs, f_low=30, f_peak=400, gamma=2.0) psd_cont /= np.max(psd_cont) psd_cont *= amp_cont psd_cont *= 10 ** (0.1 * ( 20 * np.log10(speed / 10) - 10 * np.log10(1 + depth / 20))) # 线谱:轴频、倍轴频、叶频、倍叶频,另加两条固定工频 shaft_f = rpm / 60.0 blade_f = shaft_f * blades lines = [ (shaft_f, 1.0, 80), (2 * shaft_f, 0.5, 60), (blade_f, 0.8, 50), (2 * blade_f, 0.35, 40), (50.0, 0.4, 100), (100.0, 0.25, 80), ] psd_lines = np.zeros_like(freqs) for f0, A, Q in lines: psd_lines += lorentz(freqs, f0, A, Q) # 线谱和连续谱分别加随机相位、分别 IFFT spec_lines = np.sqrt(psd_lines) * np.exp(1j * rng.uniform(0, 2*np.pi, len(freqs))) sig_lines = np.fft.irfft(spec_lines, n=nfft) spec_cont = np.sqrt(psd_cont) * np.exp(1j * rng.uniform(0, 2*np.pi, len(freqs))) sig_cont = np.fft.irfft(spec_cont, n=nfft) # 对连续谱叠加叶片调制,调制深度随航速增大 t = np.arange(nfft) / fs m = np.clip(0.2 + 0.03 * (speed - 8), 0.2, 0.7) env = 1 + m * np.sin(2 * np.pi * blade_f * t) sig_cont *= env sig = sig_lines + sig_cont sig = sig / (np.max(np.abs(sig)) + 1e-12) return sig, fs, psd_cont, psd_lines, freqs3.3 代码里每个参数对应什么
cont_shape函数用两个一阶环节组合出带通形状:(f/f_low)^2 / (1 + (f/f_low)^2)让谱级以每倍频程 12 dB 的斜率上升,到 f_low 附近转平;1 / (1 + (f/f_peak)^gamma)在超过 f_peak 后制造滚降,gamma 取 2 就是约 6 dB/oct。实际使用中可以先把f_low设在 20~50 Hz、f_peak设在 300~600 Hz,再根据目标船的实测曲线微调。
航速项的20 * np.log10(speed / 10)表示航速从 10 节升到 20 节时谱级增加约 6 dB,取的是工程经验下界;希望模拟高航速下空化加剧,可以把这个系数改成 25~30。深度项-10 * np.log10(1 + depth / 20)做的是线性水深近似,200 米比 20 米大约低 4.6 dB,和静压升高抑制空化的趋势一致。
线谱部分如果 Q 值低于 30,Lorentz 峰会展得很宽,容易盖住旁边的连续谱;Q 值高于 200 时,一旦nperseg对应的频率分辨率不够,峰又会被窗口抹平。推荐先按表格里的区间试,再对着 LOFAR 图调。调制深度m被限制在 0.2~0.7,8 节时约 0.2,20 节时约 0.56,模拟空化增强后包络起伏变大的趋势。需要更真实时可以加二次谐波:把 env 改成1 + m1*np.sin(2*np.pi*blade_f*t) + m2*np.sin(2*np.pi*2*blade_f*t + phase),m2 一般取 m1 的一半左右。
4. 舰船噪声仿真参数表与 LOFAR/DEMON 验证
4.1 六个最常动的参数与推荐取值
参数调优全部围绕这个表展开,表里给的是大多数中小型舰船的通用水准。
| 参数 | 推荐取值 | 对频谱和包络谱的影响 |
|---|---|---|
| 采样率 fs | 20~50 kHz | 决定最高可分析频率;DEMON 常用 100~3000 Hz 频带 |
| 时长 dur | 30~120 s | 频率分辨率等于 1/dur,太短低频线谱无法分离 |
| 转速 rpm | 90~200 | 直接决定轴频和叶频位置,影响整个梳状结构 |
| 叶片数 blades | 4~7 | 叶频 = rpm/60 × blades,决定调制基频 |
| 航速 speed | 6~20 kn | 主控连续谱级,约每倍增 6~9 dB,并增大调制深度 |
| 水深 depth | 10~200 m | 越深空化越弱,连续谱级越低,线谱相对越突出 |
4.2 用 Welch PSD 和 LOFAR 谱图验证线谱稳定性
生成信号后第一件事是看功率谱。用 Welch 平均做频谱估计,nperseg 取 4 秒以上,这样 0.25 Hz 的频率分辨率至少能看清 2 Hz 的轴频基波。
from scipy.signal import welch, spectrogram f, psd = welch(sig, fs, nperseg=fs*4, noverlap=fs*2) # 对数坐标看:连续谱包络 + 离散线谱 f_t, t_t, Sxx = spectrogram(sig, fs, nperseg=4096, noverlap=2048) # LOFAR 图横轴时间、纵轴频率、颜色为谱级LOFAR 图上应该看到两类结构:宽带连续谱作为背景,稳定的窄带线谱作为贯穿全图的细线。轴频和叶频是判断转速设得对不对的直接依据;如果 50 Hz 以下原本该有梳状结构却看不到,先查dur和nperseg是否够长,再查线谱 Q 值是否被设得过低。
4.3 用 DEMON 谱验证包络调制
普通功率谱无法确认调制是否生效,要用解调分析。常用做法是带通滤波、Hilbert 取包络、再去直流做 Welch 谱:
from scipy.signal import butter, filtfilt, hilbert b, a = butter(4, [100, 3000], btype='band', fs=fs) bp = filtfilt(b, a, sig) env = np.abs(hilbert(bp)) f_d, psd_d = welch(env - env.mean(), fs=fs, nperseg=fs*8) # 在 f_d 约等于轴频、叶频的位置找峰带通范围不能随意选。低频段调制浅,直接分析全带信号会把 DEMON 峰淹没;100~3000 Hz 是空化噪声占主导的频段,调制深度高,峰最明显。env - env.mean()是为了去掉包络里的直流分量,否则 0 Hz 附近一大团能量会压住低频峰。如果 DEMON 谱上只有轴频没有叶频,先查叶片数是否参与计算;若一个峰都没有,大概率调制 m 为 0 或调制被加到了线谱分量上。
4.4 三个典型问题与修正
- 线谱在 LOFAR 图上完全笔直。实测中主机转速有慢漂移,笔直线谱反而不真实。给每条线谱中心频率叠加一个低通随机游走,频偏控制在 ±1% 内,Q 值不变。
- 线谱找得到位置但幅度比连续谱低太多。Lorentz 的 A 参数和
psd_cont不在同一量纲体系,需要先对连续谱归一化,再用“线谱峰值高出连续谱 5~20 dB”这个经验值去对齐。不要直接沿用代码里的相对值。 - DEMON 谱分辨率不够。舰船轴频只有几赫兹,调制谱峰挨得很近。
nperseg=fs*8起步,不够就加时长和窗长,否则叶频和轴频峰重叠分不开。
5. 用 1/3 倍频程谱级和宽带总声级做仿真信号出厂校验
5.1 由 Welch PSD 计算 1/3 倍频程谱级
功率谱和 LOFAR 确认的是“细节像不像”,交付前还要确认“总体能量分布像不像”。声学工程里最常用的总体指标是 1/3 倍频程谱级,它把连续谱和线谱积分到固定频带里,便于和实测报告直接对比。实现如下:
def third_oct_levels(freqs, psd, ref=1.0): fcs = 1000 * (2 ** (np.arange(-16, 8) / 3.0)) levels = [] valid = [] for fc in fcs: f1 = fc * 2 ** (-1/6) f2 = fc * 2 ** (1/6) idx = (freqs >= f1) & (freqs < f2) if idx.sum() > 0: levels.append(10 * np.log10(np.trapz(psd[idx], freqs[idx]) / ref)) valid.append(fc) return np.array(valid), np.array(levels) f_c, lev = third_oct_levels(f, psd) # 画半对数曲线,横轴中心频率,纵轴谱级各频带内 PSF 已经由 Welch 得到,np.trapz按实际频率间隔做积分,结果就是该频带的声压级。对比实测曲线时重点看两点:峰值频带是否在同一 1/3 倍频程内;高频段滚降的斜率是否一致。若中频出现凹陷或高频段比实测陡太多,优先怀疑f_peak和gamma设置。
5.2 宽带总声级随航速斜率检查
把同一组参数在不同航速下各生成 30 秒以上信号,计算宽带总声级随航速的变化,这也是一个体面的自检:
def broadband_level(sig, fs): ff, pp = welch(sig, fs, nperseg=int(fs*2)) return 10 * np.log10(np.trapz(pp, ff)) for sp in [6, 10, 16, 20]: s, _, _, _, _ = make_ship_noise(speed=sp, depth=50) print(sp, broadband_level(s, fs))按经验,航速每倍增总声级应上升约 6~9 dB,如果拟合斜率低于 5 dB,说明连续谱的航速项20 * np.log10(speed / 10)权重不够;高于 10 dB 则要反过来降权重。深度不变时低频线谱带走的能量基本固定,斜率主要由空化连续谱贡献,这个指标也能顺带检查调制是否把宽带能量过度分散到包络频带上。数值曲线和实测值对不上时,优先微调f_peak和航速系数,不要先动线谱位置。
本文还有配套的精品资源,点击获取