☰
加窗FFT频谱泄漏:方窗与海明窗的幅值修正及代码实现
2026/10/20 14:30:50 网站建设 项目流程

简介:MATLAB加窗傅里叶变换演示程序,面向数字信号处理初学者及课程学习者,用于直观对比方窗(矩形窗)与海明窗对正弦信号频谱分析结果的影响。程序基于FFT实现,可调节窗长度以观察频率分辨率与频谱泄漏之间的权衡,适合课堂辅助教学或自学理解窗函数原理。资源为单个m脚本,压缩包仅759B,轻量易用,无需额外依赖,直接运行即可查看加窗前后频谱差异。已有456人学习下载,适合需要快速上手验证窗函数效果的MATLAB用户。通过运行该程序,可清晰看到方窗旁瓣较高、泄漏明显,而海明窗平滑过渡能有效压低旁瓣、提升频谱精度。代码结构简洁、便于阅读和二次修改,有助于掌握FFT操作、窗函数选择及频域分析的实际实现方法。

1. 加窗傅里叶变换不是玄学:方窗和海明窗的 FFT 结果为什么差这么多

一段干净的 47Hz 正弦波,采样率 1000Hz、采 200 点,直接拿 MATLAB 做 FFT,频率附近会蹦出一串小波纹,峰值幅值还对不上号;乘上海明窗再算,谱线干净了,主峰却明显变胖。问题出在频谱泄漏,加窗傅里叶变换的本质是在分辨率与幅值精度之间做取舍。

这篇文章用一份可直接复现的 MATLAB 演示程序,分别对方窗和海明窗加窗后的信号做傅里叶变换,把泄漏来源、谱特性差异、幅值修正一次讲清。

刚接触 FFT 的人能照着参数和代码搭出自己的试验,做过几年信号处理的熟手也能从选窗判断依据和踩坑记录里找到可复用的经验。

2. 频谱泄漏的本质:方窗自带的天生缺陷

2.1 截断就是加窗:DFT 背后那只看不见的手

很多人以为“加窗”是 FFT 之前手动做的一步操作,不加窗就是原汁原味。这个直觉是错的。DFT 只能处理有限长序列,我们采样 N 点,等价于把无限长的理想信号乘上一个长度为 N 的矩形函数,再送入 FFT。这个矩形函数在窗函数家族里就是方窗,也叫矩形窗或直角窗:窗内全 1,窗外全 0,截面像一道笔直的城墙。

时域相乘对应频域卷积。理想正弦波的频谱是位于 f0 处的一条谱线,数学上是一个冲激;它和方窗的频谱——一条以 0 为中心的 sinc 形状曲线——卷积之后,冲激被“涂抹”成方窗频谱的样子:一个主瓣加无穷多个按规律衰减的旁瓣。这就是方窗结果里那些波纹的来源,不是信号本身有这些频率成分。

所以“不加窗”这个说法严格意义上不存在。只要数据长度有限,就一定有一个隐形的窗在工作。默认意义上的“不加窗”就是乘方窗,它是最自然、最省事的截断,也是泄漏最典型的形态。想明白这一点再看泄漏,方向就对了:泄漏不是 47Hz 信号发散的杂波,而是方窗的频谱形状被卷积进了结果。

2.2 为什么 47Hz 会在频谱上抹开

这里要引入频率分辨率的概念。采样率 fs、点数 N 时,数据时长 T = N/fs,DFT 输出只在频率格点 k·fs/N(k = 0,1,…)上有值,相邻格点间距 Δf = fs/N = 1/T。在 fs=1000、N=200 的配置下,Δf=5Hz,格点是 0、5、10、……、45、50Hz。

如果信号频率恰好落在格点上,比如 45Hz 或 50Hz,能量就完整地落到唯一一条谱线上,幅值还原得很漂亮。可真实世界的频率几乎不可能精确对齐格点。47Hz 落在 45 和 50 之间,离最近的格点还差 2Hz,也就是 0.4 个格点间距。这时 DFT 没有“47Hz 那根谱线”可用,能量只能向 45Hz、50Hz 及更远的格点分摊,峰值处幅值掉到真实值的七成左右,剩余能量变成一串向两侧延伸的旁瓣波纹。这就是频谱泄漏。

泄漏的大小和偏离程度直接相关。偏离 δ 个格点时,主瓣方向谱线衰减大体遵循 |sin(πδ)/(πδ)| 的规律,δ=0.4 时大约只剩 0.76。换句话说,不是加窗出了问题,而是采样时长本身塞不下一个完整的周期循环。想把泄漏压到几乎不可见,最简单的办法是让数据长度覆盖整周期数,但实际工程中频率未知,这个条件很难预先满足,所以窗函数才被请出来救场。

2.3 海明窗为什么能压制泄漏:从时域形状看频域表现

海明窗的时域系数长这样:w(n)=0.54-0.46·cos(2πn/(N-1)),n=0,1,…,N-1。它和方窗最大的区别是:两端不是突然跳变到 0,而是平滑地落到 0,中间区域接近 1 但又不到 1。时域上的“软着陆”意味着信号在截断边界处不会产生剧烈的阶跃,泄漏到临近频率的能量也就小得多。

频域上,海明窗的第一旁瓣只有约 -43dB,而方窗是 -13dB,差了整整 30dB,相当于泄漏波纹被压到原来的约三十分之一。在演示代码的结果里,方窗谱线两侧能清楚看到一串锯齿状小峰,海明窗的结果几乎看不到这些杂波,只有干净的主峰。

代价同样明显:海明窗的主瓣宽度大约是方窗的两倍。方窗主瓣零点带宽约 2Δf,海明窗约 4Δf。主瓣变宽意味着两个频率接近的分量会被“糊”在一起,分辨能力下降。窗函数设计的基本矛盾就在这里摆开了:想让旁瓣低,就得接受主瓣宽;想保住分辨率,就得容忍泄漏。后面所有选窗决策,本质上都是在给这笔交易定一个对自己有利的成交价。

3. 演示实验设计:一份可直接复现的 MATLAB 程序

3.1 参数怎么定:采样率、点数、信号频率的选取逻辑

做演示程序最怕两件事:参数太理想,什么都看不出来;参数太复杂,读者不知道每行代码在干嘛。我的做法是取一个最能暴露问题的中间状态。下面是参数表:

参数数值选取逻辑
采样率 fs1000 Hz常见音频低端采样率,便于心算频段
采样点数 N200时长 0.2s,Δf=5Hz,格点好算
信号 1 频率 f147 Hz故意不在 5Hz 格点上,制造泄漏
信号 1 幅值 A11.0基准分量,用于检查幅值恢复
信号 2 频率 f2123 Hz第二个非整数格点频率,幅值弱一档
信号 2 幅值 A20.3用于观察弱信号在泄漏背景下的可辨识度

为什么选 47Hz 而不是 50Hz?50Hz 是格点,乘方窗后频谱也是一条干净的线,演示就失去了意义。47Hz 离最近的格点 0.4 格,泄漏明显但又不至于把主峰完全打散,正好用来对比海明窗的压制效果。123Hz 同理,它离格点 125Hz 只有 0.4 格,而且幅值只有 0.3,检验的是另一个能力:当强分量的旁瓣在附近作响时,弱信号还能不能被看见。

数据处理流程只有四步:构造时域信号,分别乘方窗和海明窗,做 FFT,幅值修正并画单边谱。每一步都保持最小工程量,方便你替换成自己的信号跑。

3.2 完整代码:方窗与海明窗的 FFT 对比

%% 加窗傅里叶变换演示:方窗 vs 海明窗 fs = 1000; % 采样率 Hz N = 200; % 采样点数,时长 0.2s,Delta_f = 5Hz t = (0:N-1) / fs; % 时间序列 % 两个正弦分量:47Hz/1.0 + 123Hz/0.3,都是非格点频率 f1 = 47; A1 = 1.0; f2 = 123; A2 = 0.3; x = A1 * sin(2*pi*f1*t) + A2 * sin(2*pi*f2*t); % 原始信号 % 加窗:方窗取全1,海明窗用 hamming 函数 wRect = rectwin(N); % 方窗(矩形窗) wHamm = hamming(N); % 海明窗 xRect = x(:) .* wRect; % 保证列向量逐点相乘 xHamm = x(:) .* wHamm; % FFT,点数取 N,不补零 XRect = fft(xRect, N); XHamm = fft(xHamm, N); % 频率轴取单边,含 0 和 fs/2 nHalf = N/2 + 1; faxis = (0:nHalf-1) * fs / N; % 幅值修正:2/N 是单边谱因子,mean(w) 是窗函数的相干增益 ARect = 2 * abs(XRect(1:nHalf)) / N / mean(wRect); AHamm = 2 * abs(XHamm(1:nHalf)) / N / mean(wHamm); % 直流与奈奎斯特频率在单边谱里本来就不该乘 2 ARect([1 end]) = ARect([1 end]) / 2; AHamm([1 end]) = AHamm([1 end]) / 2; % 画图:上图为方窗结果,下图为海明窗结果 figure('Color', 'w'); subplot(2,1,1); plot(faxis, ARect, 'LineWidth', 1.2); grid on; xlabel('频率 (Hz)'); ylabel('幅值'); title('方窗(矩形窗):峰值约 0.76,旁瓣波纹明显'); xlim([0 200]); subplot(2,1,2); plot(faxis, AHamm, 'LineWidth', 1.2); grid on; xlabel('频率 (Hz)'); ylabel('幅值'); title('海明窗:谱峰干净,主瓣略宽'); xlim([0 200]);

这段代码在常用版本的 MATLAB 上都能直接跑通,输出两张子图。上半部分是方窗的结果,你会看到 47Hz 附近一个约 0.7~0.8 的主峰,两侧每隔 5Hz 出现衰减的旁瓣波纹;123Hz 处的弱峰周围同样不干净。下半部分是海明窗的结果,主峰幅值修正后接近 1.0,123Hz 处接近 0.3,波纹基本消失,但主峰底部明显比上半部分宽。

需要注意 faxis 是按 N/2+1 个点构造的,对应 0~500Hz 的单边谱;绘图时我把横轴限制在 0~200Hz,让两个分量都看得清楚。如果你要测自己的数据,把第 5、6 行换掉就行,后面的流程不用动。

3.3 关键行拆解:幅值修正为什么必须除 mean(w)

很多人第一次跑完会问:为什么海明窗的幅值要“修正两次”?其实只修正了一次。2/N 是单边谱的因子:DFT 结果对称,取一半谱后乘 2 才能还原总能量。真正容易漏的是 mean(wRect) 和 mean(wHamm) 这两个商,专业叫相干增益。

窗函数不是全 1 时,乘窗会让信号整体变小。方窗平均值为 1,不加修正效果也正确;海明窗的平均值只有约 0.54,如果不除它,47Hz 的分量测出来会只有 0.5 左右,这就是幅值腰斩的来源。修正后:

% 验证海明窗的相干增益 w = hamming(N); fprintf('海明窗相干增益 cg = %.4f\n', mean(w)); % 输出约 0.5400,幅值修正倍率约为 1/0.54

这个 0.54 不是凑出来的数字,它来自海明窗系数公式的直流分量:0.54-0.46·cos(...) 展开后直流成分就是 0.54。除以它,本质是把加窗造成的能量损失按比例补回去。只做相对比较、不关心绝对幅值时,这步可以省;但工程上要读 dB 值、要测真实振动幅值,这步省了就是硬伤。

提一句容易被忽略的边界:单边谱里 0Hz 和 fs/2 处的幅值,不应该再乘 2,因为这两个点本来就只有一条谱线。代码里 ARect([1 end]) = ARect([1 end]) / 2 干的就是这件事,忘了它,直流和最高频率处会测出两倍的真实值。

提示:把修正公式记成 2*abs(fft(xw))/N/mean(w),无论换什么窗,公式都不用改,只要换窗口函数那一行。这是我用了很多年的习惯写法。

4. 运行结果对比:从谱图上能看到什么

4.1 方窗结果:幅值衰减与旁瓣波纹

方窗的谱图第一眼是“乱”。47Hz 分量不再是一根独立的谱线,而是一个主峰加上向左右两侧扩散的波纹,波纹的间距正好是 Δf=5Hz,衰减速度肉眼可见地按距离变缓。主峰高度大约只有 0.76,而不是真实的 1.0——能量被旁瓣偷走了。

更麻烦的是 123Hz 的弱分量。它自己的幅值只有 0.3,叠加在 47Hz 泄漏波纹的尾巴上,如果不仔细对照频率轴,很容易把它误认成旁瓣。反过来想,如果真实信号里本来没有 123Hz 这个成分,方窗结果里那些波纹也会被误判成“存在多个谐波”。这就是为什么用矩形窗做精细幅值分析容易翻车。

4.2 海明窗结果:干净的谱峰与变宽的主瓣

海明窗的谱图第一眼是“净”。47Hz 处的谱峰周围基本看不到旁瓣,修正后幅值回到 0.9x~1.0 的水平,123Hz 处也能清楚地认出 0.3 左右的独立峰。对弱信号检测来说,这种干净的底噪意味着动态范围大幅提高——你可以放心地说 123Hz 这个成分是真实存在的。

代价在形状上:47Hz 谱峰底部明显比方窗宽。方窗的主峰窄而带刺,海明窗的主峰像一个鼓包,左右延伸到相邻两三个格点附近。如果信号里有 46Hz 和 48Hz 两个真实分量,海明窗大概率会把它们融成一个峰,方窗则可能勉强分出两个突起。这是频率分辨率层面的损失。

4.3 直观对比表:主瓣宽度、旁瓣高度、幅度误差

对比项方窗(矩形窗)海明窗
时域形状全部为 1,硬截断两端平滑衰减到 0
主瓣零点带宽2Δf4Δf
第一旁瓣衰减约 -13 dB约 -43 dB
远端旁瓣衰减速率约 6 dB/oct约 6 dB/oct
相干增益 mean(w)1.0约 0.54
频率分辨能力强,可分辨约 Δf 间隔弱,约 2Δf~3Δf 才能分清
幅值测量精度差,泄漏会明显拉低峰值好,修正后接近真实值
典型用途初步探查、频率分离幅值测量、窄带分析

这张表最有用的信息是“远端旁瓣衰减速率”那一行:海明窗第一旁瓣很低,但远端衰减并不快,也是 6dB/oct。这解释了为什么信号里如果有一个很远但很强的干扰,海明窗的远旁瓣依然可能盖住目标。把近旁瓣压到 -43dB 得到的收益,在远端会被 6dB/oct 慢慢侵蚀掉。这个细节在实际频段分析里比“海明窗旁瓣低”这个说法更值钱。

4.4 判断依据:选窗本质是选分辨率还是幅值精度

综合上面的对比,选窗的决策逻辑可以收敛成一句话:先问自己是“要分得开”还是“量得准”。要分得开,用方窗;要量得准,用海明窗或汉宁窗一档。落到判断依据上,我会按三步走。

先确定目标分量之间的最小频率间隔。如果最小间隔接近甚至小于 2Δf,海明窗的主瓣会把相邻分量糊在一起,此时只能退回方窗,同时接受旁瓣泄漏,或者干脆加长采样时间把 Δf 做小——后者才是治本方案。如果最小间隔明显大于 3Δf,海明窗就能安全使用,主瓣不会互相重叠。

再看动态范围要求。需要检测的弱信号相对强信号要低 30dB 以上时,方窗的旁瓣会直接淹没弱信号,必须用海明窗这一级别的窗。若强弱差异在 15dB 以内,方窗的旁瓣虽然存在,但还不至于遮挡目标。

最后看应用口径。做故障诊断、振动幅值、声学测量这类看重绝对幅值的场景,默认海明窗起步;做频率计数、谐波结构初筛、瞬态信号观察这类看重“有几根谱线”的场景,方窗反而更合适。判断依据不是“哪个窗更好”,而是“我的输出到底要服务于下一环的什么判断”。

5. 加窗 FFT 常见问题排查:五个反复踩的坑

5.1 加海明窗后幅值直接腰斩

现象:同样的正弦波,乘方窗时 47Hz 幅值读数接近 1,乘海明窗后同一根谱线只剩下 0.5 左右。很多人第一反应是“海明窗把信号衰减了”,于是去翻窗函数定义,查来查去也没发现错误。

原因:根子在第 3 章讲的相干增益上。海明窗系数平均只有约 0.54,乘窗后信号被压缩了近一半,而幅值修正只做了 2/N,忘了除以 mean(w),读数自然腰斩。

解决:把幅值修正统一写成 2*abs(fft(xw))/N/mean(w),窗函数那一行随便换,公式不动。自检方法是用幅值为 1 的标准正弦波跑一遍,加窗读数应在 0.95~1.05 之间,低于这个区间就是修正因子漏了。

5.2 补零后以为分辨率提高,实际没变化

现象:N=200 的数据用 fft(x, 1024) 补零,谱线变密很多,看着像频率分辨率提高了,但两个相距 2Hz 的分量依然叠成一个包。

原因:补零只做插值,不产生新信息。频率分辨率由原始数据时长决定,Δf=5Hz 不会因为补零变成 0.98Hz。

解决:要拆开两个靠得很近的峰,只能加长采样时长。补零的正当用途是把峰值读得更准,比如配合插值精估频率,而不是用来提高分辨能力。我在早期做模拟信号分析时在这里栽过跟头,后来凡是涉及“频差很近”的题目,第一反应都是先看数据时长够不够。

5.3 海明窗与汉宁窗选错

现象:做某设备振动信号分析时,目标频率附近的旁瓣压得很干净,可频谱远端还有一串缓慢衰减的小鼓包,干扰了对干扰频段的判断,于是怀疑海明窗性能虚标。

原因:海明窗强在第一旁瓣只有约 -43dB,但远端衰减速率只有 6dB/oct;距目标很远的强分量,经过几十个格点后仍有相当残余。汉宁窗第一旁瓣高一些,约 -31.5dB,远端衰减却有 18dB/oct,远距离抑制更好。

解决:先估算干扰分量离目标有多少个格点,远距离干扰明显时换汉宁窗;拿不准就把两种窗都跑一遍,对比远端底噪,通常一眼就能定。这套判断我现在写进了自己的选窗备忘里。

5.4 直流和奈奎斯特频率的修正陷阱

现象:含直流偏置的振动信号做单边谱时,0Hz 和 fs/2 处读数比真实值大一倍,一开始还以为传感器标定系数错了。

原因:单边谱乘 2 的前提是正负频率各有一条谱线,直流只出现在 0Hz 一条线上,奈奎斯特频率也只有端点这一条,这两个点不该乘 2。

解决:取谱后单独处理首尾,或者像演示代码那样统一乘完再对首尾除 2。加上这行之后,直流偏置和最高频分量的读数才和真值对得上,后续标定才有意义。

5.5 主瓣变宽当成“加窗加坏了”

现象:换上海明窗后主峰明显变胖,有人以为是 hamming 函数用错,或者数据被某种平滑操作污染了,把原本好端端的结果当成 bug 排查。

原因:主瓣展宽是窗函数压制旁瓣的交换条件。海明窗主瓣零点带宽约 4Δf,是方窗的两倍,这不是错误,是设计使然。

解决:判断窗函数是否选得对,看旁瓣压没压住、幅值修没修回来,这两点达标就说明窗起效了。若主瓣宽度不能接受,说明场景需要方窗或更长的数据,别在一个“更高级的窗”里反复打转。

6. 收尾落地:什么场景用哪种窗,附一个可抄的固定流程

这篇 MATLAB 演示程序从头到尾要传递的判断就一条:方窗负责“看清有哪些频率”,海明窗负责“把幅值量准确”,二者配合才是完整打法。我自己在工程里固定用两步流程:先用方窗跑一遍原始数据,把频谱骨架和分量数量摸清楚,确定大致的频率位置;然后针对每个感兴趣的分量,单独乘海明窗再做 FFT,除以 mean(w) 恢复幅值,同时用补零把峰值频率读得更精确。这套组合既绕开了方窗旁瓣对幅值的污染,又避免了海明窗主瓣太宽导致看不清谱结构的问题。

顺带把最常用的修正套路固化下来,以后直接复制这一段:

xw = x(:) .* hamming(N); % 加海明窗 Xw = fft(xw, N); amp = 2 * abs(Xw(1:N/2+1)) / N / mean(hamming(N)); amp([1 end]) = amp([1 end]) / 2; % 直流与奈奎斯特不乘2

这段代码的核心价值在第三行:mean(hamming(N)) 让幅值修正永远跟当前窗严格匹配,我换汉宁窗、布莱克曼窗时只需要同步改第一个和第三个 hamming 为对应函数,后面不会出现“换了窗幅值就对不上”的尴尬。

回过头看,我在早期做某信号处理项目时吃过一次不小的亏:拿方窗谱的旁瓣波纹误判出一个根本不存在的“谐波分量”,白白排查了两天,最后用海明窗复核才确认是泄漏在捣乱。从那以后,我每次做频谱分析都强制走一遍“先方窗看结构、再海明窗测幅值”的流程,顺手把上面这段脚本沉淀成自己的常用工具。花五分钟把这份演示程序跑一遍,你在频谱上踩过的坑会少一大半。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询