简介:这份资源面向信号处理、故障诊断与振动监测方向的学习者和研究人员,提供一套基于MATLAB的完整联合去噪方案,用于从多通道含噪信号中恢复原始信号。其核心思路是先通过多变量变分模态分解(MVMD)将信号拆解为多个模态分量,再依据相关系数筛选有效模态,随后对保留模态施加小波阈值去噪,最后重构得到干净信号,并借助PSNR、SSIM、MAE、RMSE、相关系数、SNR等指标量化评估效果。压缩包共15个文件,约2.65MB,以11个xlsx数据表、3个m脚本和1份docx说明文档为主,数据表承载原始与归一化特征,脚本实现分解、频谱分析与主流程,文档则给出运行前的必要提示。目前已有113人学习。读者可据此掌握MVMD与小波阈值结合的完整流程,理解模态筛选与重构逻辑,并直接复用代码与数据完成自己的去噪实验与指标对比。
1. MVMD 加小波阈值去噪:为什么“先分解再阈值”比直接硬砍更靠谱
一段非平稳信号丢过来,直接上低通滤波,往往把有用成分一起抹掉;直接对小波系数硬阈值,噪声压下去了,可模态混叠又冒出来。MVMD(多元变分模态分解)加小波阈值这条路线,解决的正是这个两难:先用 MVMD 把多通道信号拆成一组窄带本征模态,再对每个模态单独做小波阈值去噪,最后重构。它适合手里有多通道采集数据、信噪比不高、又不想把特征频率误伤的从业者,比如振动监测、生理信号、结构健康监测这类场景。我第一次在模拟项目里把这条链路跑通时,最直观的感受是:噪声不是被“砍”掉的,而是被“分而治之”地剥离的。下面按“先立住原理、再动手复现、最后讲坑”的顺序,把 MATLAB 完整代码和数据组织方式讲清楚。
2. MVMD 与小波阈值去噪的底层逻辑和参数选型
2.1 MVMD 到底比单通道 VMD 多做了什么
VMD 处理的是单通道信号,把信号分解成 K 个模态,每个模态围绕一个中心频率。MVMD 把这件事扩展到多通道:它假设多个通道共享同一组中心频率,但每个通道在每个模态上有各自的幅度。换句话说,MVMD 找的是一组“共同频率骨架”,再让各通道去拟合这个骨架。这个假设在多通道采集里非常自然——同一个物理源在多个传感器上的主频是一致的,只是幅值和相位不同。
数学上,MVMD 的优化目标可以写成对每个模态、每个通道的解析信号做频率搬移后,让带宽之和最小,同时约束所有通道重构误差最小。求解用交替方向乘子法(ADMM),和 VMD 的迭代结构类似,只是变量从一维变成矩阵。实际写代码时,你不需要手推 ADMM,但必须理解三个参数:模态数 K、惩罚因子 alpha、以及收敛容差 tol。K 决定拆多细,alpha 决定每个模态的带宽,tol 决定迭代什么时候停。
常见做法是:K 从 3 到 8 试,alpha 取 2000 左右起步,tol 设 1e-7。如果 K 太小,两个物理模态会被塞进同一个分量;K 太大,一个模态会被拆成两个,出现“虚假分量”。判断依据不是看重构误差,而是看每个模态的频谱是否只有一个明显峰。我一般会先跑一遍 K=5,把各模态的频谱画出来,再决定加还是减。
2.2 小波阈值为什么放在 MVMD 之后而不是之前
小波阈值去噪的核心是:对信号做小波变换,噪声通常集中在高频细节系数里,且幅值小;有用信号的小波系数幅值大。于是设定一个阈值,把小于阈值的系数收缩或置零,再逆变换。问题在于,如果直接对原始多通道信号做小波阈值,噪声和有用信号在频带上重叠时,阈值会把有用成分一起削掉。
MVMD 先做了一步“频率分选”:每个模态的频带相对窄,噪声和有用成分在不同模态里的占比不同。对每个模态单独选阈值,就比全局一个阈值精细得多。比如低频模态里噪声本来就少,阈值可以设小;高频模态里噪声占主导,阈值可以设大。这就是“先分解再阈值”比“直接硬砍”靠谱的原因。
小波阈值本身有两个关键选择:小波基和阈值规则。小波基常用 db4、db6、sym8,分解层数一般取 3 到 5。阈值规则里,通用阈值(sqtwolog)偏保守,会多删系数;启发式阈值(heursure)和极小极大阈值(minimaxi)更温和。我一般用 sym8 加 heursure,分解 4 层,软阈值。软阈值比硬阈值平滑,但会引入一点幅值偏差;硬阈值保留幅值好,但容易在重构时产生振荡。如果后续要做幅值精确分析,硬阈值加小波基长度短一点更合适。
2.3 完整链路的参数表和选型理由
| 环节 | 参数 | 常用取值 | 影响 |
|---|---|---|---|
| MVMD | 模态数 K | 3~8 | 太小混叠,太大虚假分量 |
| MVMD | 惩罚因子 alpha | 2000~5000 | 越大带宽越窄,越小越容易混叠 |
| MVMD | 收敛容差 tol | 1e-7 | 太小迭代久,太大提前停 |
| 小波 | 小波基 | sym8 / db4 | 影响时频局部化能力 |
| 小波 | 分解层数 | 3~5 | 层数多低频分辨率高,但计算量大 |
| 小波 | 阈值规则 | heursure / sqtwolog | 保守或激进 |
| 小波 | 阈值方式 | 软阈值 | 平滑但幅值略偏 |
这张表不是让你照抄,而是让你知道每个参数动了之后,输出会往哪个方向变。调参的顺序建议是:先定 K 和 alpha,把 MVMD 的模态频谱看顺眼;再定小波基和层数,最后调阈值规则。
3. 在 MATLAB 里跑通 MVMD 加小波阈值的完整流程
3.1 数据组织与多通道信号构造
假设你手里有 3 通道采集数据,采样率 1000 Hz,时长 2 秒。为了可复现,这里用模拟信号构造:三个通道共享两个主频,再加一个通道特有的频率,最后叠高斯白噪声。实际项目里把data换成你的采集矩阵即可,行是时间点,列是通道。
% 构造多通道模拟信号:3 通道,1000 Hz,2 秒 fs = 1000; t = (0:1/fs:2-1/fs)'; N = length(t); % 共享频率成分 f1 = 50; % 通道共有的低频 f2 = 120; % 通道共有的中频 % 通道 3 特有频率 f3 = 200; % 各通道幅值不同,模拟传感器差异 ch1 = 1.0*sin(2*pi*f1*t) + 0.6*sin(2*pi*f2*t); ch2 = 0.8*sin(2*pi*f1*t + pi/6) + 0.9*sin(2*pi*f2*t + pi/4); ch3 = 0.7*sin(2*pi*f1*t + pi/3) + 0.5*sin(2*pi*f2*t) + 0.4*sin(2*pi*f3*t); % 叠高斯白噪声,信噪比约 5 dB rng(42); % 固定随机种子,保证可复现 noise_level = 0.3; ch1 = ch1 + noise_level*randn(N,1); ch2 = ch2 + noise_level*randn(N,1); ch3 = ch3 + noise_level*randn(N,1); data = [ch1, ch2, ch3]; % N x 3 矩阵这段代码的关键是data的维度:行是时间,列是通道。MVMD 的实现通常要求输入是N x C,如果你的数据是C x N,记得转置。rng(42)是为了让每次跑出来的噪声一样,方便对比去噪效果。实际采集数据不需要这一步。
3.2 MVMD 分解的核心代码与参数设置
下面是一个 MVMD 分解的函数调用框架。由于 MVMD 的完整 ADMM 实现较长,这里给出调用方式和参数设置,核心迭代逻辑封装在mvmd_decompose里。你可以在 MATLAB 文件交换区找到常见实现,或者按 ADMM 结构自己写。重点是参数怎么传、输出怎么用。
% MVMD 参数设置 K = 5; % 模态数 alpha = 2000; % 惩罚因子 tol = 1e-7; % 收敛容差 max_iter = 500; % 最大迭代次数 % 调用 MVMD 分解 % 输入 data: N x C,输出 u: N x C x K,omega: K x C [u, omega, iter_num] = mvmd_decompose(data, K, alpha, tol, max_iter); % 检查收敛 fprintf('MVMD 迭代次数: %d\n', iter_num); % 画出每个模态的频谱,确认没有混叠 for k = 1:K figure; for c = 1:size(data,2) [Pxx, f] = pwelch(u(:,c,k), [], [], [], fs); plot(f, 10*log10(Pxx)); hold on; end title(sprintf('模态 %d 各通道频谱', k)); xlabel('频率 (Hz)'); ylabel('功率 (dB)'); legend('通道1','通道2','通道3'); hold off; endu的维度是N x C x K,表示第 k 个模态在第 c 个通道上的时间序列。omega是每个模态在每个通道上的中心频率估计。迭代次数iter_num如果接近max_iter,说明没收敛,需要调大max_iter或放宽tol。画频谱这一步不能省,它是判断 K 是否合适的唯一直观依据。
3.3 对每个模态做小波阈值去噪
拿到u之后,对每个模态、每个通道单独做小波阈值。这里用 MATLAB 的wdenoise函数,它封装了小波分解、阈值和重构。如果你要自己控制阈值规则,可以用wavedec加wthresh手动实现。
% 小波阈值去噪参数 wname = 'sym8'; % 小波基 level = 4; % 分解层数 thr_method = 'Heursure'; % 阈值规则 sorh = 's'; % 软阈值 % 初始化去噪后的模态 u_den = zeros(size(u)); % 对每个模态、每个通道去噪 for k = 1:K for c = 1:size(data,2) u_den(:,c,k) = wdenoise(u(:,c,k), level, ... 'Wavelet', wname, ... 'DenoisingMethod', thr_method, ... 'ThresholdRule', sorh); end endwdenoise的输入是列向量,所以这里按通道循环。level取 4 是因为采样率 1000 Hz,4 层分解后最低频段大约覆盖到 31 Hz 左右,对 50 Hz 以上的成分足够。如果你的信号主频更低,层数要加。Heursure是启发式阈值,比sqtwolog温和,适合噪声水平未知的情况。
3.4 重构信号并对比去噪前后
去噪后的模态直接相加就是重构信号。因为 MVMD 的分解是完备的,所有模态之和等于原始信号,所以去噪后的重构就是sum(u_den, 3)。
% 重构去噪信号 data_den = sum(u_den, 3); % 计算信噪比改善 snr_before = 10*log10(sum(data.^2, 'all') / sum((data - data).^2, 'all')); % 注意:实际项目里需要干净信号才能算 SNR,这里用模拟信号已知干净成分 clean = [1.0*sin(2*pi*f1*t) + 0.6*sin(2*pi*f2*t), ... 0.8*sin(2*pi*f1*t + pi/6) + 0.9*sin(2*pi*f2*t + pi/4), ... 0.7*sin(2*pi*f1*t + pi/3) + 0.5*sin(2*pi*f2*t) + 0.4*sin(2*pi*f3*t)]; snr_before = 10*log10(sum(clean.^2, 'all') / sum((data - clean).^2, 'all')); snr_after = 10*log10(sum(clean.^2, 'all') / sum((data_den - clean).^2, 'all')); fprintf('去噪前 SNR: %.2f dB\n', snr_before); fprintf('去噪后 SNR: %.2f dB\n', snr_after); % 画一个通道的对比 figure; plot(t, data(:,1), 'Color', [0.7 0.7 0.7]); hold on; plot(t, data_den(:,1), 'b', 'LineWidth', 1.2); plot(t, clean(:,1), 'r--', 'LineWidth', 1.0); legend('含噪信号','去噪信号','干净信号'); xlabel('时间 (s)'); ylabel('幅值'); title('通道 1 去噪前后对比'); hold off;这段代码里,snr_before和snr_after的计算依赖干净信号clean,实际采集数据没有这个条件,可以用噪声估计或者只看波形平滑度。模拟信号的好处就是能定量验证。如果 SNR 改善不到 3 dB,说明参数有问题,优先检查 K 和阈值规则。
4. 避坑与排查:MVMD 加小波阈值最容易翻车的五个地方
4.1 模态数 K 选大了,重构信号反而变差
现象:K 从 5 加到 8,去噪后 SNR 不升反降,频谱上出现一堆窄带小峰。原因:MVMD 把噪声也拆成了独立模态,每个噪声模态被小波阈值处理后,残留的噪声结构被保留下来,重构时叠加回去。解决:不要盲目加 K。先用 K=3 到 5 跑一遍,看每个模态的频谱是否对应一个物理意义明确的频率。如果某个模态的频谱是平的或者多个小峰,说明它是噪声模态,应该减少 K 或者直接把这个模态置零。
4.2 alpha 取太小,模态之间频率重叠
现象:两个相邻模态的频谱在同一个频率上都有峰,重构信号里这个频率的幅值异常大。原因:alpha 控制带宽,太小则每个模态的带宽很宽,ADMM 无法把它们分开。解决:alpha 从 2000 起步,每次翻倍试。如果 alpha 加到 8000 还是重叠,说明 K 太小,两个物理模态被塞进一个了,先加 K 再加 alpha。
4.3 小波阈值规则选错,有用高频被削掉
现象:去噪后信号在 200 Hz 附近的幅值明显变小,而那个频率是有用的。原因:sqtwolog阈值偏高,把高频模态里幅值较小的有用系数也置零了。解决:换成heursure或minimaxi,或者对高频模态单独降低阈值。我一般会先对每个模态画出去噪前后的频谱,确认有用峰没被削。
4.4 分解层数超过信号长度允许的范围
现象:wdenoise报错或者输出全零。原因:小波分解层数太多,最粗尺度下的系数数量少于滤波器长度。解决:分解层数满足level <= floor(log2(N/(L-1))),其中 L 是小波滤波器长度。N=2000、sym8 的 L=16 时,level 最大约 6。取 4 是安全的。
4.5 多通道数据维度搞反,MVMD 输出错乱
现象:mvmd_decompose跑完,u的维度是C x N x K,后面按N x C x K索引,结果全错。原因:不同实现的输入约定不同,有的要求C x N。解决:在调用前打印size(data),确认是N x C。如果不是,用data = data';转置。这个坑我踩过不止一次,血泪经验就是:先看维度,再跑分解。
5. 进阶技巧:用包络谱和模态能量比验证去噪是否到位
跑通基本流程之后,怎么判断去噪是真的到位,而不是把有用成分也删了?我一般用两个指标:包络谱和模态能量比。
包络谱适合振动或调制信号。对去噪后的重构信号做希尔伯特变换取包络,再对包络做 FFT,看故障特征频率是否清晰。如果去噪前包络谱里噪声底很高,去噪后特征峰突出,说明阈值没伤到调制信息。
% 包络谱对比 env_before = abs(hilbert(data(:,1))); env_after = abs(hilbert(data_den(:,1))); [P_before, f_env] = pwelch(env_before - mean(env_before), [], [], [], fs); [P_after, ~] = pwelch(env_after - mean(env_after), [], [], [], fs); figure; plot(f_env, 10*log10(P_before), 'Color', [0.7 0.7 0.7]); hold on; plot(f_env, 10*log10(P_after), 'b', 'LineWidth', 1.2); legend('去噪前包络谱','去噪后包络谱'); xlabel('频率 (Hz)'); ylabel('功率 (dB)'); title('包络谱对比'); hold off;模态能量比是另一个角度:计算每个模态去噪前后的能量,如果某个模态能量下降超过 90%,说明这个模态基本被当成噪声删了。这时候要回头看这个模态的频谱,确认它是不是真的全是噪声。如果它包含有用频率,就要降低阈值或者换阈值规则。
% 模态能量比 energy_before = zeros(K,1); energy_after = zeros(K,1); for k = 1:K energy_before(k) = sum(u(:,:,k).^2, 'all'); energy_after(k) = sum(u_den(:,:,k).^2, 'all'); end ratio = energy_after ./ energy_before; disp('各模态能量保留比:'); disp(ratio);如果某个模态的保留比低于 0.1,而它的中心频率又落在你关心的频段里,那就是阈值设激进了。我一般会把保留比控制在 0.3 到 0.8 之间,低频模态保留比高一些,高频模态低一些。
最后一个习惯:每次调完参数,不要只看 SNR 数字,一定要把去噪前后的时域波形和频谱并排画出来。数字会骗人,波形不会。SNR 改善 5 dB 但波形上出现明显畸变的情况,我见过不止一次。希望帮到你。
本文还有配套的精品资源,点击获取