资讯详情

MATLAB中的FFT滤波:从频谱分析到频域滤波实战指南

发布时间:2026/9/16 23:27:02

500+
企业客户服务经验
120+
行业领域内容覆盖
3000+
原创页面设计沉淀
98%
客户满意度

MATLAB中的FFT滤波:从频谱分析到频域滤波实战指南

先说个实际场景。我以前做传感器数据采集的时候被50Hz工频干扰搞得焦头烂额时域波形上那个毛刺怎么滤都滤不干净FIR滤波器阶数加高了几十倍延迟大得像慢动作结果还不好。后来换了个思路先把信号做FFT看频谱然后再动手滤问题一下就清楚了。这就是FFT滤波的核心价值用频域的视野解决时域的问题。这篇东西我从原理到工程实现都会聊适合刚接触信号处理和MATLAB的初学者也适合那些已经在用MATLAB处理数据、但总觉得频谱分析这块差点火候的工程师。重点不是罗列函数而是把为什么要这样写和踩过的坑都讲明白。读完你至少能把FFT分析、频域滤波、信号还原这条链路完整跑通遇到常见的频谱泄漏、边界效应这类问题也知道怎么处理。1. 先搞明白FFT和滤波是怎么组合到一起的1.1 FFT滤波到底在干什么很多人一上来就写代码Y fft(x); Y(100:200) 0; y ifft(Y);三行搞定但中间发生了什么完全没概念。这样糊弄一次可以换个信号就翻车。FFT滤波的本质是用FFT这个工具把信号从时间域搬到频率域在频率域里对不同的频率成分做取舍然后再用逆变换IFFT把处理后的信号搬回时间域。打个比方你在菜市场买了一整块五花肉时域信号FFT就是把这块肉按肥肉、瘦肉、皮分开摆好频域分解滤波就是我要瘦的肥的扔了最后IFFT就是把剩下的瘦肉重新拼成一盘菜时域信号。这么说有点粗糙但对于理解核心思路足够了。关键是你要意识到滤波的本质不是把波形拉平而是把某几个频率的成分干掉或者保留。这个思路一旦建立你以后看任何滤波问题都会通透很多。1.2 为什么选FFT而不是直接上滤波器有人会问MATLAB里现成的滤波器设计函数一抓一大把fir1、butter、designfilt为什么还要绕这么大一圈用FFT我的答案很直接因为FFT让你先看见再动手而传统滤波器设计是盲选。做传统滤波器你得先假设信号和噪声的频率范围然后挑一个滤波器类型定阶数、定截止频率整个过程像在黑暗中摸索。而用FFT你直接把信号频谱画出来干扰在哪、有用信号在哪一目了然。拿前面的电磁干扰说频谱图上50Hz那根谱线高高耸立你一刀切下去干净利落。另外FFT滤波对非稳态特征的处理很灵活。比如你要保留的是信号里的某个瞬时冲击它在频域上铺得比较宽你可以在频域画一个任意形状的掩膜mask不像传统滤波器只能做低通、高通、带通这些规矩形状。这在图像处理领域叫频域掩膜在信号处理里一样好用。当然FFT滤波也不是万能的它适合离线批量处理实时性要求高的场景还是得用因果滤波器。这个后面我详细聊。2. 动手前的必修课频谱里的三个大坑2.1 采样率、频率分辨率和奈奎斯特频率做FFT滤波第一步不是写代码而是搞清楚你的信号是怎么被采样的。三个参数必须刻在脑子里采样率fs每秒钟采多少个点。采样率决定了你能看到的最高频率也就是奈奎斯特频率等于fs/2。采样点数N一共采了多少个点。FFT的分辨率就是fs/N意思是频谱上相邻两根谱线之间的频率间隔。频率分辨率df即fs/N。如果你采了1秒的数据分辨率就是1Hz采了0.1秒分辨率就是10Hz。这三个参数对滤波效果的影响极大。举个例子你有一个100.5Hz的信号采样率1000Hz采了1秒FFT分辨率是1Hz100.5Hz这个分量会落在100Hz和101Hz这两根谱线之间能量被摊开了这就是频谱泄漏。这时候你想精准滤掉它一刀切不干净想保留它又会发现幅度对不上。所以做FFT滤波之前先问自己一个问题我的数据长度够不够让目标频率和噪声频率在频谱上分开如果不够要么加长采集时间要么用后面要讲的窗函数。2.2 频谱泄漏和窗函数的选择频谱泄漏是FFT滤波最常遇到的坑。原因是FFT默认信号是周期的无限延拓但实际信号截断后首尾不连续这个不连续在频谱上会溅出很多假频率成分。解决办法就是加窗。窗函数的本质是让信号两端平滑衰减到0减少截断带来的突变。MATLAB里常用的有hann汉宁窗主瓣宽一点旁瓣衰减比较好通用性最强。hamming海明窗和汉宁很像旁瓣略低一点。blackman布莱克曼窗旁瓣衰减更大但主瓣更宽频率分辨率牺牲更多。rectwin矩形窗实际上就是不加窗主瓣最窄但旁瓣泄漏最严重。我的选择经验很简单如果你要滤除的信号和干扰频率离得近用矩形窗或汉宁窗保住频率分辨率如果干扰和有用信号离得远用布莱克曼窗把泄漏压到最低。滤波前加窗滤波后记得在IFFT之前或者之后做相应的处理否则幅度会偏。2.3 实信号的频谱对称性与单边谱MATLAB里fft算出来的结果是复数长度和输入一致。很多人第一次看到这个复数数组就懵了特别是后半段的值还挺大这是什么鬼要理解这个得知道一个基本性质对实信号做FFT频谱是共轭对称的。前N/21个点对应0到fs/2的正频率后N/2-1个点对应从-fs/2到0的负频率。也就是说后半段的信息和前半段是镜像的没有额外信息量。所以做频域滤波的时候一个常见的错误是只处理前一半频谱然后把后一半也置零结果IFFT回来信号变成了复数而且幅度只有原来的一半。正确做法是设计掩膜的时候要把正负频率都考虑进去。要么用fftshift把零频挪到中间对称地处理要么直接在原来的频谱上按照[0:fs/N:(fs/2-fs/N), -fs/2:fs/N:-fs/N]的频率轴来设计掩膜。我一般是先freq (0:N-1) * fs / N;生成频率轴做掩膜的时候判断物理频率是否落在目标区间这样正负频率自然就处理了不容易出错。3. MATLAB代码实战从信号生成到滤波还原3.1 构造一个带噪信号先看清楚频谱光说不练没用咱们直接上手。我先构造一个仿真信号一个50Hz的工频干扰叠加一个5Hz的有用信号再混入一点随机噪声。fs 1000; % 采样率 1000 Hz N 2000; % 采样点数 2 秒 t (0:N-1) / fs; % 时间轴 % 有用信号5Hz 正弦 x_signal 1.5 * sin(2 * pi * 5 * t); % 工频干扰50Hz 正弦幅度2 x_noise_50 2.0 * sin(2 * pi * 50 * t); % 随机噪声 x_rand 0.3 * randn(1, N); x x_signal x_noise_50 x_rand;先画时域波形你会看到信号被50Hz振荡骑在上面5Hz的趋势被完全淹没了。这就是很多人在时域里做滤波做得痛苦的原因有用信号和噪声的频率差很远时域波形上全是高频抖动你看着就头大。现在做FFT看频谱X fft(x); freq (0:N-1) * fs / N; % 画幅度谱取前一半因为后半是镜像 figure; plot(freq(1:N/2), abs(X(1:N/2))); xlabel(频率 (Hz)); ylabel(幅度); title(原始信号频谱);跑完你会看到在5Hz和50Hz处各有一根明显的谱线50Hz那根特别高其余地方是一些低矮的噪声底。这一步之所以重要是因为它直接告诉你滤波的目标是什么把50Hz那根线削掉保留5Hz那根。3.2 频域掩膜设计做一个窄带陷波接下来设计一个陷波器把50Hz附近的能量滤掉。这里有个关键点不能只把50Hz那一根谱线置零因为频谱泄漏可能让干扰能量扩散到49.5Hz和50.5Hz甚至更宽。所以陷波器要有一个宽度。% 设计频域掩膜将 49Hz~51Hz 的成分置零 mask ones(1, N); % 注意要同时处理正负频率这里用频率轴判断 for k 1:N if abs(freq(k) - 50) 1.5 % 49~51Hz 范围内的都置零 mask(k) 0; end end % 应用掩膜 X_filtered X .* mask;这段代码有几个问题需要注意。第一循环在MATLAB里效率低实际工程中建议用向量化写法比如mask(abs(freq-50)1.5) 0;。第二你可能会问为什么mask只置零了前一半的50Hz后一半呢因为频率轴freq是0~999Hz当k走到950Hz附近时abs(freq(k)-50)是900多不会被置零。但别忘了实信号频谱是共轭对称的50Hz的镜像在1000-50950Hz处。所以这个写法其实漏掉了负频率的镜像。这是新手最容易犯的错误。正确做法是要么先生成-fs/2到fs/2的频率轴用fftshift处理频谱再设计对称的掩膜要么直接用我推荐的逻辑掩膜只定义为物理频率是否落在陷波区间然后对整个频率轴做判断这样正负频率都会覆盖到。上面这个循环写法其实做到了这一点因为freq覆盖0~999Hz950Hz对应的物理频率是950Hz它不等于50Hz。哎这里确实漏了。我换个写法把负频率也纳入% 方法一先 fftshift把频率轴变成 -500~500 X_shifted fftshift(X); freq_shifted (-N/2:N/2-1) * fs / N; mask ones(size(X_shifted)); mask(abs(freq_shifted - 50) 1.5) 0; mask(abs(freq_shifted 50) 1.5) 0; X_filtered_shifted X_shifted .* mask; X_filtered ifftshift(X_filtered_shifted);这个方法更直白也不容易漏。把零频挪到数组中间正负频率对称分布然后一刀一刀切。3.3 IFFT还原与幅度校正检查滤波完成后用IFFT还原时域信号y real(ifft(X_filtered));这里用real()取实部是安全的因为理论上来讲只要我们对称地处理了频谱IFFT结果就是实数。如果处理不对称取real会丢掉一部分信息造成幅度偏差。所以前面频域操作一定要对称这比real本身重要得多。然后画图对比figure; subplot(3,1,1); plot(t, x); title(原始信号); subplot(3,1,2); plot(t, y); title(FFT滤波后); subplot(3,1,3); plot(t, x_signal); title(理想有用信号);跑完你会发现滤波后的线和理想有用信号基本重合只是噪声底还有一些。幅度上要验证一下滤波后的5Hz成分幅度是不是接近1.5因为前面信号是1.5*sin(5Hz)。如果幅度不对八成是频谱处理的时候把有用信号的幅度也削了或者前面加窗没做幅度恢复。还有一个很容易忽略的点频域滤波的本质是线性时不变系统的一个特例所以如果掩膜在边界处突变得很厉害比如从1直接跳到0时域上会产生振铃效应Gibbs现象。你会在滤波后的波形两端看到明显的高频抖动这就是掩膜不光滑导致的。解决办法是让掩膜的边缘有个过渡带比如用fdesign.arbmag设计任意幅度响应或者手动生成一个缓变的边缘。3.4 完整流程封装一个可复用的FFT滤波函数把上面这套逻辑封装成函数以后调用就方便了。我平时是这么写的function y fft_filter(x, fs, freq_range, mode) % FFT频域滤波 % x: 输入信号 % fs: 采样率 % freq_range: [f1, f2] 保留的频率范围Hz % mode: bandpass 或 bandstop N length(x); X fft(x); freq (0:N-1) * fs / N; mask ones(1, N); if strcmp(mode, bandpass) % 保留 f1~f2其余置零 mask(freq freq_range(1) | freq freq_range(2)) 0; elseif strcmp(mode, bandstop) % 滤除 f1~f2 mask(freq freq_range(1) freq freq_range(2)) 0; end X_filtered X .* mask; y real(ifft(X_filtered)); end这个函数虽然能用但有几个边界问题要自己注意第一正负频率对称性问题。上面这个写法只处理了0~fs/2的物理频率负频率没处理。如果freq_range在正频率范围内负频率镜像没被处理IFFT会产生误差。所以我实际用的时候会在频率轴构造时直接覆盖到-fs/2或者输入的时候就要求用户提供正负频率范围。第二掩膜突变造成的振铃。就是前面说的Gibbs现象所以实际工程我更喜欢用过渡带设计不搞这种硬切。下面我讲实战案例的时候会展示怎么处理。4. 实战案例把50Hz工频干扰从传感器信号里揪出来4.1 问题场景与频谱初诊说一个我以前实际碰到过的案例。现场采集一个振动传感器的信号采样率是10kHz采了10秒。信号有用成分集中在200Hz~500Hz但40Hz到70Hz附近有一大坨干扰明显是工频及其谐波。时域波形上干扰使得特征频率完全看不清。按老办法我先不加滤波直接对整段数据做FFT频谱图上看得很清楚50Hz处一根大谱线旁边还有100Hz、150Hz的谐波。为什么会有谐波因为工频干扰往往不是纯正弦而是带畸变波形畸变会产生整数倍的谐波分量。如果只滤掉50Hz100Hz那个尖峰还会残留所以要多做几个陷波。这就是先用FFT看清敌情的价值我知道敌人长什么样才知道刀该怎么下。4.2 设计带过渡带的陷波器避免振铃硬切的掩膜会造成振铃这在振动信号里是很致命的因为振铃的伪特征可能被误判为机械故障。所以我在实际工程里不会用mask(mask0)这种硬切而是用平滑的过渡带。实现思路很简单在掩膜边缘用cos窗生成一个斜坡而不是直接跳变。比如要滤除49Hz~51Hz掩膜值在49Hz~50Hz之间从1逐渐降到0在50Hz~51Hz之间从0逐渐升回1。这个可以用下面的方式生成% 定义一个带过渡带的陷波掩膜 function mask notch_mask(N, fs, fc, bw) % fc: 中心频率 % bw: 过渡带宽度的一半 freq (0:N-1) * fs / N; mask ones(1, N); f_low fc - bw; f_high fc bw; % 在 f_low~fc 之间线性下降fc~f_high 之间线性上升 idx_low find(freq f_low freq fc); idx_high find(freq fc freq f_high); if ~isempty(idx_low) mask(idx_low) 0.5 - 0.5 * cos(pi * (freq(idx_low) - f_low) / (fc - f_low)); end if ~isempty(idx_high) mask(idx_high) 0.5 - 0.5 * cos(pi * (fc - freq(idx_high)) / (f_high - fc) pi); end end这个掩膜本质上是一个余弦斜坡边缘不再是1到0的跳变而是平滑过渡。实际效果是频谱上的主瓣和旁瓣都会被压下去但同时不会产生剧烈的时域振铃。4.3 边界效应首尾不连续的麻烦还有一个容易忽视的问题FFT假设信号是周期延拓的。如果你滤波后的信号首尾不连续IFFT还原时会在首尾产生很大的振荡。这个在信号长度较短时尤其明显。我曾经采了一段1秒的信号FFT滤波后开头和结尾各出现了一个大的脉冲把前面的有用信号全盖住了。排查了半天最后发现是边界不连续导致的。解决的办法有几条滤波前对信号做数据延拓把首尾各延拓一段数据比如镜像延拓滤波后再裁掉。对信号做重叠保留法overlap-save或重叠相加法overlap-add把长信号分块处理避免边界效应累积。这两个方法在fftfilt函数里就有实现。如果信号比较长边界效应只影响开头和结尾一小段直接把这两段裁掉不纳入后续分析。我的习惯是先用简单的延拓解决因为大部分场景下边界效应只占信号总长度的不到1%裁掉就行。但如果信号总共就几百个点那必须用重叠法。5. 进阶对比FFT滤波和滑动窗口滤波到底怎么选5.1 滑动窗口滤波的适用场景随着嵌入式系统越来越普及滑动窗口滤波这个词的出现频率越来越高。它的思路是在时域上开一个窗口窗口内做均值、中值或加权运算然后窗口每次移动一个点得到新的滤波输出。滑动窗口滤波的优势是实时性每个点来了都能立刻算出滤波后的值不需要等一整段数据采完。所以单片机、FPGA这类资源受限的平台上滑动窗口滤波比如滑动均值、滑动中值、滑动FIR是主流。但它的代价也很明显窗口长度和滤波效果是矛盾的。窗口越长平滑效果越好但延迟越大对快速变化的信号响应越迟钝。而且滑动窗口滤波只能做低通性质的平滑你要做带通、带阻得设计很复杂的窗口系数。5.2 什么时候用FFT滤波什么时候用滑动窗口我的选择原则是这样的离线分析、频谱特征明显、需要精细的频率选择用FFT滤波。比如做振动诊断我要滤掉某个特征频率保留另一个特征频率FFT滤波最直观。实时控制、嵌入式平台、计算资源受限用滑动窗口滤波。比如ADC采样后做简单的均值平滑消除随机噪声。数据量大、需要流水线处理可以用滑动窗口FIR但也可以把FFT滤波做成重叠保留的流式处理两者性能和延迟可以做到接近。这里多说一句现在很多做预测性维护的同行喜欢把FFT和包络谱分析结合。包络谱的做法是先对信号做带通滤波比如用FFT滤波只保留高频故障特征频段然后做Hilbert变换求包络再对包络做FFT。这一步里FFT滤波的价值就非常大了你想保留哪个频段就保留哪个频段直接对频谱做掩膜比设计一个高阶带通滤波器省事得多。5.3 图像处理里的FFT滤波怎么理解热搜词里有个matlab图片处理我顺带提一嘴。图像本质上是二维信号FFT滤波在图像里的逻辑和一维信号一模一样把图像做二维FFT频谱中心是低频边缘是高频。低通滤波就是保留中心、削掉边缘高通滤波就是反过来带通就是保留一个环形区域。MATLAB里fft2、ifft2就是干这个的。我在做图像噪声消除的时候会在频域里把特定方向的条状噪声比如扫描条纹用掩膜干掉这在空间域时域里很难实现但在频域里就是画一条线的事情。这就是FFT滤波对比空间域滤波的杀手级场景空间域难以表达的形状频域里用一个掩膜就能做。6. 常见问题与排查技巧实录6.1 我的调试笔记FFT滤波翻车现场这里整理几个我实际踩过的坑每一个都卡过我好几个小时写出来大家少走弯路。问题1滤波后信号幅值整体减半现象IFFT还原后的信号波形形状正确但幅度只有原来的一半有时还带着相位翻转。原因负频率分量没处理好。实信号FFT结果正负频率是对称的如果你只把正频率的掩膜置零或保留负频率那边没做同样处理IFFT还原时实部就只剩一半能量。解决要么用fftshift把频率轴挪到-500~500Hz再处理要么把频率轴构造为(0:N-1)*fs/N之后再额外处理N/22到N的部分保持对称。问题2滤波后开头和结尾出现剧烈振荡现象波形两端有大幅度的振铃中间正常。原因边界不连续引起的Gibbs现象。FFT把信号当成周期延拓滤波后首尾不匹配IFFT就产生振铃。解决滤波前做数据延拓前延后延各几十个点滤波后裁掉或者用overlap-add/overlap-save方法分段滤波。问题3有用信号的幅度变了或者频率歪了现象5Hz的有用信号滤波后变成了5.2Hz或者幅度从1.5变成了0.8。原因频域掩膜把有用信号附近的能量也削掉了。比如陷波带宽设得太宽把5Hz附近的频谱也波及了。或者信号长度太短频率分辨率不够5Hz和50Hz的谱线相互泄漏。解决先看频谱确认有用信号和干扰之间的频率间隔再设置合理的带宽。加窗时选旁瓣衰减好的窗比如Blackman减少泄漏。问题4IFFT结果有虚部取real之后波形明显不对现象real(ifft(X_filtered))出来和预期完全不一样。原因频域操作破坏了共轭对称性。常见于只对正频率做了操作负频率没动或者手工修改了单个频点的复数幅值破坏了对称。解决检查所有频域操作是否对称。如果要修单个频点永远同时修改X(k)和X(N-k2)两个位置。以下是我整理的速查表现象可能原因排查步骤解决办法幅值减半负频率没处理检查频谱是否共轭对称fftshift对称处理首尾振铃边界不连续看滤波后两端波形数据延拓/overlap幅度偏差掩膜误伤对比滤波前后频谱图调带宽/过渡带虚部极大共轭对称破坏检查频域修改点成对修改频点50Hz滤不干净分辨率/泄漏看频谱图是否有残留加窗/加长数据全部变成NaN中间步骤除零检查是否有0长度的数组调试断点逐行看6.2 几个让调试效率翻倍的小习惯最后分享几个我在调试FFT滤波时养成的习惯虽小但很管用画图一定要叠加对比。滤波前和滤波后的频谱图用subplot放在一起别单独开窗口。你对比着看一眼就能发现滤波把不该滤的东西滤掉了。先仿真再实测。拿到真实信号之前先用已知频率的仿真信号把代码跑通。如果仿真都过不了别指望实测能用。我一般先用sin构造一个包含已知频率的信号跑通后换成真实数据这样能把算法问题和数据问题分开。用变量检查幅度恢复。滤波后我通常会找一个已知幅度的正弦成分对比滤波前后的幅度变化。如果这个都算不准后面定量分析直接白搭。不要在一个巨大的脚本里写死参数。滤波器参数、采样率、数据长度都抽出来作为变量调参方便也能避免改一步全盘崩。最后再分享一个小技巧做FFT滤波到现在我最大的体会是滤波不是目的看清信号才是。很多人急着把滤波器怼上去却忘了先做频谱分析。我建议每一次滤波之前都强制自己先画一张频谱图哪怕你心里已经知道干扰频率是多少也画一下。因为信号是会变的环境噪声也是会变的这张图能告诉你当下这一刻的实际情况。另外如果你的数据量特别大比如几十M个点一次fft没问题但IFFT之后首尾裁切很浪费。这时候我推荐用fftfilt这个MATLAB内置函数它自动做分块处理省去边界效应的麻烦就是少了一些灵活性。需要精细控制掩膜形状的场景再回来自写。手里有频谱图心里就不慌。这行做得越久越觉得FFT滤波的门槛不在数学而在你有没有养成先看谱再下刀的直觉。希望这篇东西能帮你把这个直觉建立起来。
热门专题

继续阅读更多专题内容

围绕企业服务、数字化转型与官网运营的常青话题,持续输出深度内容

企业官网建设指南 企业托管服务模式 财税政策与解读 企业数字化转型 官网SEO与获客 网站安全与运维
配套服务

读完这篇文章,了解更多服务

从整站搭建到SEO布局,17项核心服务助您打造高转化的企业官网

01

企业托管整站搭建

从信息架构到栏目预留,搭建可生长的企业站点骨架,每个页面独立原创设计。...

了解详情
02

规整可信网页设计

雪地靴温暖风原创设计,金属铜线条贯穿全页,拒绝通用模板与AI流水线。...

了解详情
03

企业服务SEO布局

关键词体系与语义化结构,从建站源头为搜索排名而生。...

了解详情
04

业务预约咨询表单

多场景表单与线索收集体系,把访问流量转化为可追踪的销售线索。...

了解详情
05

企业服务站点运维

安全巡检、数据备份与内容更新支持,全年守护网站稳定运行。...

了解详情
06

全终端商务适配

电脑、平板、手机一致呈现,移动端体验与转化同样出色。...

了解详情
需要专业建议?

让专业顾问为您解读行业趋势

关于企业官网建设、SEO获客与数字化转型的任何疑问,欢迎一对一咨询我们的专业顾问。