先讲一个背景。设备状态监测和工业信号处理里,信号降噪是最基础、也最容易翻车的一步。以前我拿到数据,第一反应就是低通滤波或者做一次 FFT 再甩掉高频;后来发现这套方法对付平稳信号还行,一遇到非平稳信号,要么削了真实突变,要么留了一堆毛刺。改用小波变换之后,我才真正理解了“分析尺度”这几个字的含金量。这篇文章从原理、参数到 MATLAB 仿真,完整记录一遍我进行信号降噪处理和方案选型的过程,希望能帮你少走几天弯路。
1. 为什么小波变换在降噪里比单纯滤波更靠谱
先说一个最容易让人困惑的问题:既然低通滤波和傅里叶变换都能去掉噪声,为什么还要用小波变换?
低通滤波器的思路很直接:噪声一般是高频成分,我把超过某个截止频率的分量全部压掉。这个方案对平稳信号确实有效,但有一个很致命的代价:有用信号里的突变、尖峰、阶跃,本质上也是高频成分。尤其是设备振动信号里的轴承故障冲击、心电信号里的 QRS 波群、语音信号里的辅音起始点,这些关键特征一旦被低通滤波器处理,就直接糊掉了。
傅里叶变换的问题类似。FFT 把整段信号分解成不同频率的正弦波叠加,你能知道信号里有哪些频率成分,却不知道这些频率成分具体出现在哪个时间段。一个频率为 100 Hz 的局部冲击,和一个持续整段时间的 100 Hz 正弦分量,在频谱上可能长得差不多,但你完全分不清它们的时间位置。对降噪来说,这意味着你无法区分“高频噪声”和“高频有用特征”。
小波变换的核心优势在于“时频局部化”。它用一族可以伸缩和平移的小波基函数去匹配信号的局部特征。低频部分用较长的时间窗,频率分辨率高;高频部分用较短的时间窗,时间分辨率高。这就相当于同时戴了两副眼镜,一副看全局趋势,一副看局部细节。
另一个实际层面的原因:小波变换是可逆的。也就是说,我先把信号分解到不同尺度上,把噪声所在的尺度系数做处理,再用逆变换重构信号,整个过程信息损失可控。相比之下,有些非线性滤波方法,比如中值滤波或者滑动平均,虽然也能去噪,但很难做到无损重构,而且滤波窗口一大了就容易产生相位偏移。
我个人的体感是:小波变换不是要替代所有传统方法,它适合的信号类型非常明确,就是“非平稳、含有瞬态突变、噪声与信号频带有重叠”的场景。如果你的信号本身就是平稳正弦波,噪声也是宽白噪声,那低通滤波表现可能也并不差。但工程现场采集到的信号很少有这么理想的时候。
2. 小波降噪的底层原理,搞懂这几个概念就不懵
很多教材一上来就堆一堆公式,其实容易把人劝退。我换个方式讲:把小波降噪想象成“用多把筛子筛东西”。大尺度筛子筛出信号的整体轮廓,小尺度筛子筛出信号的尖峰细节。噪声则混杂在各个尺度里,需要我们用一套规则把它们挑出去。
2.1 分解和重构到底在做什么
连续小波变换的定义是把信号和一个经过伸缩平移的小波函数做内积,得到一组小波系数。但实际工程中,我们几乎不用连续小波变换,因为计算量太大,而且信息冗余。真正落地的是离散小波变换,最常用的是 Mallat 算法。
Mallat 算法的思路也简单:信号先通过一个低通滤波器得到一个近似系数序列,同时通过一个高通滤波器得到一个细节系数序列。然后每次只对近似系数继续分解,一层一层往下走。这个过程可以理解为:
- 第一层分解后得到近似分量 A1 和细节分量 D1;
- 第二层分解时,对 A1 继续拆分,得到 A2 和 D2;
- 每多拆一层,频率范围就往低频方向挪一档。
降噪时,真正的噪声能量主要集中在细节分量里。近似分量往往保留了信号的主要能量和趋势。所以常见的做法是:只对细节系数做阈值处理,保留近似系数不处理,然后再通过逆小波变换重构信号。
我建议刚开始接触的同学不要死磕二维小波或者复杂的小波包,先从一维离散小波变换入手,把 C、L 这两个矩阵结构看明白,后面的路会顺很多。
2.2 阈值怎么选:硬阈值、软阈值与几种常用规则
阈值法的逻辑在小波降噪里是核心中的核心。信号经过小波分解后,真实信号对应的小波系数幅值通常比较大,而噪声对应的小波系数幅值比较小,而且分布相对均匀。所以我们设一个阈值,把小于阈值的系数置零,或者做一定压缩,就能把大部分噪声干掉。
阈值处理有两种最经典的策略:
- 硬阈值:绝对值大于阈值的系数保留原值,小于阈值的全部置零。优点是不压缩有效信号幅度,缺点是系数在阈值附近会发生跳变,重构出来的波形容易有抖动和振铃。
- 软阈值:绝对值大于阈值的系数向零方向收缩一个阈值量,小于阈值的置零。优点是连续性好,重构波形平滑,缺点是会把真实信号的幅度也压小一点,导致信号变“矮”。
实际使用中,软阈值用得更多,因为它稳定。但如果你特别在意波形的幅值,比如要分析冲击特征的能量大小,就要考虑半软阈值或者改进型阈值函数,后面我会专门讲。
阈值本身的取值有几种常用规则:
- 通用阈值,也叫 VisuShrink,公式是 lambda = sigma * sqrt(2*log(N)),sigma 是噪声标准差,N 是信号长度。这个阈值对高斯白噪声效果好,但信号较长时阈值容易偏大,导致过度平滑。
- rigrsure 规则,基于 Stein 无偏风险估计,阈值选得更“聪明”,适合信噪比不低、细节丰富的情况。
- heursure 是通用阈值和 rigrsure 的折中,信号信噪比低时倾向用通用阈值,信噪比高时倾向用 rigrsure。
- minimaxi 规则,阈值相对保守,能保留更多细节,但噪声滤除率也会低一些。
实际项目里我一般先跑通用阈值看整体效果,再用 rigrsure 微调,找出保留细节和滤除噪声的平衡点。
2.3 除了阈值,小波基和层数为什么重要
阈值只是其中一个环节,小波基函数和分解层数同样直接决定效果。
小波基函数的核心指标有正交性、紧支性、消失矩和对称性。
消失矩越高,小波能匹配信号的平滑部分就越强,对应的高频分量也就越敏感。但代价是支撑长度变长,计算量变大,边界失真也更明显。所以并不是消失矩越高越好。
分解层数的作用也很关键。层数太少,噪声没有被充分分离,去噪不彻底;层数太多,信号的有用低频成分也可能被当作噪声处理,重构后波形失真严重。这俩参数和阈值规则是相互耦合的,调参的时候不能单独看某一个。
3. 用 MATLAB 复现一次小波降噪仿真
前面把理论过了一遍,下面直接进仿真。我用的是带噪声的多频叠加信号,模拟工程中常见的混合信号情境。整套流程在 MATLAB R2020 之后都能直接跑。
3.1 构造带噪信号
为了能评价去噪效果,我们先用一个已知干净的原始信号,再叠加高斯白噪声。这样处理后可以计算信噪比和均方根误差。
clear; close all; clc; rng(42); fs = 1000; % 采样率 1000 Hz t = (0:fs-1)/fs; % 1 秒 % 原始有用信号:三个不同频率分量的叠加 x = 5*sin(2*pi*50*t) + 2*cos(2*pi*120*t) + 0.8*sin(2*pi*260*t); % 高斯白噪声,标准差 1.5 noise = 1.5*randn(size(t)); % 含噪信号 s = x + noise;这里把三个频率分量放在 50 Hz、120 Hz 和 260 Hz,模拟一种宽频带混合信号。噪声标准差设为 1.5,得到的输入信噪比大约是:
SNR_in = 10*log10(sum(x.^2)/sum(noise.^2));算下来大概在 8 dB 左右,是比较典型的低信噪比工程场景。
3.2 核心去噪代码
我推荐先自己写一遍分解、阈值处理、重构的完整流程,而不是直接无脑调 wdenoise。这样你对数据在每个环节的变化会有更直观的感觉。
level = 5; % 分解层数 wname = 'sym8'; % 小波基函数 % 小波分解 [C, L] = wavedec(s, level, wname); % 提取第一层细节系数,用于估计噪声标准差 D1 = detcoef(C, L, 1); sigma = median(abs(D1)) / 0.6745; % 通用阈值 lambda = sigma * sqrt(2*log(length(s))); % 保留低频近似系数,对其他系数做软阈值处理 start_idx = L(1) + 1; C_new = C; C_new(start_idx:end) = wthresh(C(start_idx:end), 's', lambda); % 重构 x_den = waverec(C_new, L, wname);这里有个细节要注意:sigma的估计使用了第一层细节系数。因为白噪声经过一次高通滤波后,能量最高、分布最接近高斯分布的往往就是第一层细节系数。用这个中位数绝对偏差公式估计噪声标准差,是 Donoho 和 Johnstone 的经典做法,比直接对原始数据求标准差要稳。
如果你觉得手动流程太啰嗦,也可以直接用 MATLAB 自带的封装函数:
x_den2 = wdenoise(s, level, 'Wavelet', wname, ... 'DenoisingMethod', 'SURE', 'ThresholdRule', 'Soft');不过我现在还是更习惯手动流程,因为可以随时修改阈值规则,也方便把中间系数导出来看效果。
3.3 结果评估指标
去噪效果好不好,不能只用眼睛看,要有量化指标。我最常用的三个指标是:
% 信噪比 SNR,单位 dB SNR = @(x, xd) 10*log10(sum(x.^2) / sum((x - xd).^2)); % 均方根误差 RMSE RMSE = @(x, xd) sqrt(mean((x - xd).^2)); % 相关系数 R = corrcoef(x, x_den); CC = R(1, 2);运行后你会得到三组数:
- 输入 SNR 大概在 8 dB 左右;
- 去噪后 SNR 一般能提升到 14 dB 到 18 dB 这个区间,具体取决于参数;
- RMSE 从 1.5 左右降到 0.4 到 0.7 之间;
- 相关系数能到 0.95 以上,说明重构波形和原始信号的相似度很高。
绘图部分我用两张图对比:
figure; subplot(3,1,1); plot(t, x); title('原始信号'); subplot(3,1,2); plot(t, s); title('含噪信号'); subplot(3,1,3); plot(t, x_den); title('小波去噪后信号');看图形时重点看两个位置:一是信号的拐点和峰值有没有被削平,二是去噪后的曲线是否还残留高频抖动。
4. 参数怎么调:小波基、层数、阈值规则的取舍
小白最容易卡在“参数调不对”这一步。同一份数据,换个小波基或者层数,结果可能完全不同。这一节我把调参思路梳理一遍,相当于给一个可复用的经验模板。
4.1 小波基选择与效果对比
先说结论:没有最好的小波基,只有最适合当前信号的小波基。我根据自己的经验整理了一张选型参考表:
| 小波基 | 特点 | 适用场景 |
|---|---|---|
| haar | 结构最简单,适合阶跃和突变 | 数字信号、分段常数信号 |
| db2/db4 | 正交、紧支性好,经典 | 生物电信号、振动信号 |
| sym4/sym8 | 近似对称,相位失真小 | 工程混合信号,通用性好 |
| coif4/coif5 | 消失矩较高,平滑性好 | 变化缓慢的连续信号 |
| bior3.5/bior4.4 | 双正交,对称性好 | 图像信号、需要保留相位 |
我自己的习惯是优先试 sym8。因为它在支撑长度和消失矩之间比较均衡,对称性也比 db 系列好,不容易造成明显的相位偏移。如果去噪后波形两端出现奇怪的抖动,再换成 coif4 或者 bior。
一个小技巧:如果你处理的信号本身就有比较明显的周期冲击成分,比如齿轮箱故障信号,可以优先考虑消失矩高一些的小波基,比如 sym10 或者 db8,这样能更准确地刻画冲击特征。
4.2 分解层数的影响
分解层数是另一个容易翻车的参数。理论上层数越多,频率划分越细,但实际工程里不是越大越好。
层数太少时,噪声和信号在最高频的细节分量里还没充分分开,阈值处理会把噪声留下。层数太多时,信号的一个重要低频成分也可能被分解到很深的细节层里,被阈值误杀。
我总结的经验是:采样率 1000 Hz 左右的数据,一般设 4 到 6 层;采样率 4000 Hz 以上时,可以适当加大到 7 到 8 层。但不要盲目超过 8 层,因为每次分解都会引入边界效应,层数越多,边界效应积累越严重。
如果实在拿不准,可以写个循环,分别用 level = 3 到 8 跑一遍去噪,对比 SNR 和 RMSE。我通常选 SNR 最高、RMSE 最小的那组,但还要扫一眼波形,确保没有明显失真。
4.3 改进阈值函数:半软阈值和自适应阈值
硬阈值和软阈值各有缺陷,工程实践里我更推荐半软阈值函数,也叫软硬折中阈值。它通过一个调节系数把硬阈值和软阈值结合起来,既能保持幅值,又能让曲线连续。
表达式如下,对每个小波系数 w:
- 当 |w| >= lambda 时,处理结果 = sign(w) * (|w| - alpha * lambda);
- 当 |w| < lambda 时,处理结果 = 0。
当 alpha 取 0 时等价于硬阈值,取 1 时等价于软阈值。实际中 alpha 一般取 0.5 到 0.8 之间,可以在幅度保持和平滑性之间取得平衡。
MATLAB 里没有现成的半软阈值函数,但我一般直接写:
function w_new = soft_hard_threshold(w, lambda, alpha) w_new = zeros(size(w)); idx = abs(w) >= lambda; w_new(idx) = sign(w(idx)) .* (abs(w(idx)) - alpha*lambda); end然后在主循环里替换wthresh:
C_new(start_idx:end) = soft_hard_threshold(C(start_idx:end), lambda, 0.6);用这种方式处理后,我发现对冲击型信号的幅度保持能力明显好于纯软阈值,同时波形比纯硬阈值更顺滑。
5. 帮踩过的坑:信号被削尖、振铃和过平滑
任何滤波方法都不是万能的。我在做小波降噪的过程中踩过不少坑,有些问题在教科书里很少专门提醒,但在实际仿真和项目里大概率会遇到。
5.1 波形失真与过平滑的常见原因
最典型的坑是“去噪后信号变圆了”。波形看起来特别光滑,但和原始信号一比,尖峰没了,幅值也跟着缩水。这种情况十有八九是分解层数太多,或者阈值选得太大。
比如我用 260 Hz 这个分量模拟的局部波形突变,如果分解层数设到 8 层,sym8 的支撑长度又长,阈值一大,重构回去基本就变成一个平滑正弦波了。真实的冲击故障特征被完全抹掉,这种去噪是失败的。
还有一种情况是阈值处理时把近似系数也一起压了。近似系数代表信号的主要低频能量,它不应该被阈值处理。如果你看到重构后的信号整体幅值明显下降,先检查一下代码里start_idx有没有写对,也就是有没有把低频近似系数误伤。
5.2 边界效应与振铃
信号的首尾一段区域,在做小波分解和重构时经常会出现明显的抖动或震荡,这就是边界效应。
原因是信号在边界处不满足周期延拓的假设,滤波器在边缘卷积时会“够不着”完整的数据,出现假成分。分解层数越多,边界效应影响的范围越大。
处理办法有几个:
- 使用
wavedec的时候指定边界延拓方式,比如周期延拓或者对称延拓; - 在信号处理之前做延拓,处理完再裁掉延拓段;
- 如果边界处本身没有重要特征,直接不评估边界区域。
我在实际项目里比较喜欢对称延拓,因为对振动信号来说,对称延拓比零延拓更符合物理规律。
5.3 典型异常排查速查表
我整理了一张问题排查表,基本都是我自己踩过或者帮同事调参时遇到过的:
| 现象 | 可能原因 | 排查和解决 |
|---|---|---|
| 去噪后波形过平滑 | 层数太多或阈值过大 | 减小层数,改用 rigrsure 规则 |
| 仍有明显毛刺 | 阈值太小或小波基不合适 | 加大阈值,换消失矩更高的小波基 |
| 两端出现震荡 | 边界延拓方式不对 | 改用对称延拓,或处理后再裁掉边界段 |
| 信号整体幅值下降 | 阈值函数压缩太狠 | 换成半软阈值或减小 alpha |
| 局部突变被抹平 | 小波基支撑长度太长 | 换短一些的 db4、sym4 |
| 重构波形相位偏移 | 小波基不完全对称 | 优先用 sym 或 bior 系列 |
这套速查表的价值在于,遇到问题时你有明确的排查方向,而不是无脑换参数。
6. 从一维到更多扩展:图像、小波包和场景建议
小波降噪不只能处理一维信号,扩展到二维就是图像去噪。原理本质上一样,区别在于二维小波分解会得到水平、垂直和对角三个方向的细节子带。工程上做工业图像或者医学影像预处理时,这个能力很实用。
MATLAB 里二维小波去噪可以直接用ddencmp配合wdencmp2,不过我更推荐先跑一遍二维小波分解,看看每个子带里的噪声分布,再决定阈值策略。
另外一个重要扩展方向是小波包。普通小波变换只对低频近似部分继续分解,高频部分就不再拆了。小波包变换会把高频部分也继续分解,对某些同时包含高频段噪声和低频段噪声的信号来说,小波包能更精细地区分它们。代价是计算量变大、参数更多,调参难度也更高。
如果信号的中高频段同时包含有用信息和噪声,而且它们的频带很近,我建议直接尝试小波包去噪,有时的效果会比小波变换好一个量级。
最后说几个我的个人使用建议:
第一,降噪前一定先看清楚信号里到底有什么。先画时域波形,再画频谱,搞清楚噪声是白噪声、脉冲噪声还是窄带干扰,不同噪声类型用的小波策略完全不一样。
第二,把所有参数都做对比实验时,不要只看 SNR 一个指标。SNR 提高不代表波形质量好,有时 SNR 很高,局部特征却被抹了。要结合 RMSE、相关系数和时域波形一起判断。
第三,小波降噪不是万能的。如果噪声频带和信号频带重叠得非常严重,小波变换也救不回来。这种时候可能需要考虑多通道采集、盲源分离或者深度学习方法。
我在实际项目里,用过小波变换处理机泵振动信号、用过小波包处理超声检测数据,也用过二维小波去噪处理工业相机的表面缺陷图像。这套方法论的通用性很强,但每个场景都需要重新调参,千万不要指望一个固定参数能打天下。先理解原理,再结合信号特征反复试验,才能把它的潜力真正发挥出来。