搞旋转机械故障诊断的同行,对“信号越测越糊”这件事应该都不陌生。加速度传感器贴在轴承座上,实测信号里真正的冲击成分早就被轴承座、壳体、传感器安装方式层层滤波,再叠上齿轮啮合、转子不平衡这些强干扰,包络谱里往往只剩一团糊状底噪。解卷积周期估计这一支方法,就是为了把“被路径抹平”的冲击序列重新“捋”出来,其中最小熵反卷积(MED)和最大相关峰度反卷积(MCKD)又是用得最多的两种。这篇文章就把这两类盲反卷积方法的原理、MATLAB源码、参数调试经验和常见坑一次讲透,适合正在做滚动轴承、齿轮箱故障特征提取,或者被包络谱搞到头秃的同学参考。
我最早接触MED的时候,心里其实有个疑问:信号都已经被传递路径污染成这样了,光靠一个滤波器怎么可能恢复得出来?后来把Wiggins那套思路和McDonald的MCKD推导都过了一遍,才明白这类方法本质上是“利用冲击信号的统计特征反推滤波器系数”。这篇文章不打算铺开讲太多数学,重点放在“为什么这么做、代码怎么写、参数怎么调、效果怎么判断”。
1. 为什么实测信号里的冲击特征会被“淹没”——从盲反卷积的出发点讲起
1.1 实测信号路径上的层层失真
先建立一个直观的画面。滚动轴承内圈出现点蚀以后,滚珠每经过一次缺陷位置就会产生一个冲击,理论上这个冲击是极窄的脉冲序列。但传感器采集到的信号,是脉冲经过轴承内外圈、轴承座、端盖、传感器磁座、压电晶体等多个环节之后的结果。每个环节都相当于一个低通滤波器或谐振器,冲击信号的高频成分被大幅衰减,原本尖锐的脉冲被展宽成衰减振荡,幅度也小了很多。
传递路径还会引入另一个问题:相位失真。不同频率成分经过路径后的延迟不一样,导致波形进一步畸变。如果再叠加上来自齿轮啮合、不对中、松动等与旋转同步的强周期成分,以及环境噪声,那么实测信号的时域波形看起来就是一团乱麻。这个时候直接在时域上看有没有周期性冲击,基本靠猜。
频域呢?用FFT看频谱,也只能看到几个转频谐波和结构共振峰,冲击对应的故障特征频率往往淹没在噪声底里。包络谱虽然能解调出低频调制成分,但效果严重依赖带通滤波器的中心频率和带宽选择,选不好就什么都看不到。
1.2 传统包络谱分析为什么经常失灵
包络谱分析的基本流程是:带通滤波 → Hilbert变换求包络 → 对包络做FFT。这套流程的标准做法是先通过谱峭度或快速峭度图找共振频带,再在这个频带内做解调。谱峭度图本身没什么问题,但在信噪比低、干扰成分多的场合,峭度图选出来的频带经常是干扰最严重的频带,而不是故障冲击最集中的频带。
还有一个更隐蔽的问题:传递路径对冲击的响应是卷积关系,即传感器信号是真实冲击序列与路径脉冲响应的卷积。包络谱本质上是在频域做乘法然后在特定频带解调,它无法从根本上“反演”卷积过程。说得再直白一点,包络谱只是在做“观测信号的解调”,而盲反卷积在做“恢复源信号本身”。这两件事的目标是有本质区别的。
1.3 反卷积思路的直观理解
反卷积的想法其实很简单:既然观测信号x[n]等于源信号s[n]和路径响应h[n]的卷积,而系统是不可知的,那么我们就设计一个逆滤波器g[n],让y[n] = x[n] * g[n]能够尽可能恢复s[n]的统计特性。
关键在于“统计特性”怎么定义。Wiggins提出MED时,用的统计特性是峭度。熵小意味着信号“更锐利”,所以最小熵反卷积可以理解为:让恢复信号的峭度最大化,即寻找信号中稀疏的、大幅值的冲击成分。MCKD沿用了这个思路,但把目标函数换成了“相关峭度”,它显式地考虑了冲击的周期间隔T,更适合提取周期性冲击。
用一句话总结:MED是在“盲”的状态下把任何稀疏冲击都放大,MCKD则在你告诉它“周期大概是T”的前提下,专门把间隔为T的冲击放大。两种方法都需要源码实现,下面结合MATLAB代码逐一拆解。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MED最小熵反卷积:把“峭度最大”当作目标函数
2.1 MED的数学目标与迭代逻辑
MED的目标函数是最大化恢复信号y的峭度,即:
O(g) = (Σ y[n]^4) / (Σ y[n]^2)^2
其中y[n] = Σ x[n - k] * g[k],k = 1,...,L,L是逆滤波器长度。
目标函数对滤波器系数g求偏导并令其等于零,整理后可以得到一个迭代求解的方程组。这个方程组没办法直接闭式求解,只能迭代逼近,每一步都根据当前滤波器系数计算新的恢复信号,然后更新滤波器系数,循环往复。
标准MED迭代步骤大致如下:
- 初始化逆滤波器系数,通常设为全零序列中心位置的单位冲激,即g = [0,...,1,...,0]。
- 根据当前g对观测信号x做卷积,得到y。
- 根据y的统计量计算目标矩阵,更新g。
- 归一化g,使滤波器能量保持不变。
- 判断迭代前后滤波器系数的变化量或目标函数的变化量是否小于阈值,若满足则停止。
我在实际使用中发现,MED对滤波器长度L非常敏感。L取小了,逆滤波器无法充分刻画路径响应的逆;L取大了,滤波器的自由度太高,容易把单个随机脉冲当成冲击性成分放大,导致过拟合噪声。常见的经验范围是30~300之间,具体要结合采样率和故障特征频率来定。
2.2 滤波器长度对结果的影响
滤波器长度L是MED最核心的参数。可以这样理解:L相当于逆滤波器的时间跨度,只有在这个跨度内,路径响应反卷积的效果才能体现出来。如果路径响应持续时间较长,L设置得太小,逆滤波器宽度不足,反卷积效果就很有限。
反过来,L如果设置得太大,逆滤波器有足够的自由度去拟合噪声中的随机尖峰,结果就是输出信号的峭度确实变得很大,但冲击间隔完全混乱,周期估计完全失效。在实践中我曾经遇到过L从100加到200后,MED输出信号从清晰的等间隔冲击变成了一堆杂乱无章的单点尖峰的情况。
一个比较实用的调试思路是:把L从小到大扫描,观察输出信号的包络谱特征频率幅值变化,取特征频率幅值最稳定的区间。单纯用峭度最大化作为选参指标非常容易走偏,因为噪声的个别异常值可以把峭度抬得很高。
2.3 MED的典型失效场景
MED最典型的失效场景是信号中存在多个周期性冲击源,且周期不同。比如齿轮箱里既有齿轮啮合冲击又有轴承故障冲击,MED无法区分它们,会把两者都“盲反卷积”出来,结果是输出信号里有不止一种冲击间隔,包络谱上既有齿轮啮合频率也有轴承故障频率,频谱很混乱。
再一个场景是随机冲击干扰较强的场合。锤击、瞬时过载都会产生大幅值的稀疏冲击,MED的“最小熵”目标恰好喜欢这种东西,它会优先放大这些随机冲击,从而掩盖了周期性的故障冲击。
这里多说一句,MED对信号中的直流分量和超低频趋势也比较敏感。在使用前,最好先对原始信号做一次高通滤波或者趋势项移除,把低频漂移去掉,否则MED迭代时目标函数会被低频大能量成分主导,收敛结果质量会很差。
3. MCKD最大相关峰度反卷积:显式引入周期先验
3.1 相关峭度的定义与MED目标函数的本质差别
MCKD方法由McDonald等人于2012年提出,它的目标函数是相关峭度而不是普通峭度。相关峭度的定义是:
CK_M(T) = (Σ_{n=1}^{N} (∏_{m=0}^{M} y[n - mT])^2) / (Σ y[n]^2)^(M+1)
其中T是目标冲击的周期(以样本点数表示),M是移位数(shift numbers)。这个公式可以理解为:同时衡量信号在相隔T、2T、...、MT这M+1个时刻是否都有较大幅值。如果信号的冲击严格以T为间隔周期出现,那么相关峭度就会很大。
这个定义和MED目标函数有本质区别。MED只关心信号整体的稀疏性,不关心冲击之间有没有固定间隔。MCKD因为显式地引入T和M,等于把“周期性”作为先验信息强加给了求解过程。这个先验信息利用得好,能从噪声中“定向”提取出周期为T的冲击。
3.2 T参数与移位数M的选取
T直接从旋转频率和故障特征系数算出来。例如滚动轴承外圈故障特征频率是BPFO,采样率是fs,那么T = fs / BPFO,单位是样本点数。T不一定恰好是整数,使用的时候一般取最接近的整数。T取偏之后,相关峭度的对齐效果会变差,反过来影响恢复效果,因此在做周期估计时,最好对T在一小段范围内做扫描,取相关峭度最大的那一个值,这一步可以理解为“用MCKD做周期精细估计”。
M默认取1或2就够用。M越大,对周期的约束越严格,抗干扰能力越强,但求解窗口内的约束条件也更苛刻,峰值搜索反而可能错失真实的微弱冲击。我的经验是先用M=1跑通流程,信号干净且周期稳定时可以试M=2,M超过3的情况在工程中很少需要。
MCKD的迭代流程比MED稍复杂。每轮迭代需要先根据当前滤波器系数计算恢复信号y,再根据y与观测信号x的互相关矩阵更新滤波器系数。滤波器系数的更新公式里还会出现T、T-1等移位索引的相关运算,写代码时最容易出错的就是索引越界,需要在MATLAB里对边缘索引专门处理。
3.3 迭代过程中的数值稳定性
MCKD的数值稳定性整体优于MED,因为相关峭度考虑了周期匹配,对随机冲击不那么敏感。但在低信噪比或者T较大时,矩阵求逆过程有出现病态的可能。解决办法是更新滤波器系数时加一个很小的正则化参数λ,通常取1e-3~1e-6量级。
迭代收敛判据我觉得不宜只看目标函数的变化量,还要看滤波器系数变化量的模。有几次目标函数已经基本不变了,但滤波器系数还在缓慢漂移,结果输出信号出现“折叠”现象——最前和最后一段的冲击波形是相反的。这种情况下建议强制在相邻两次迭代的滤波器二范数变化小于阈值时终止,并且在输出时丢弃信号前后各几十个样本,避免边缘效应干扰周期测量。
4. MATLAB源码实现:从零搭建MED与MCKD函数
4.1 MED核心函数实现
下面这段代码是我在项目中实际使用的MED实现,注释比较完整,可以直接复制到MATLAB里调试。
matlab复制function y = med_filter(x, L, maxIter, tol)
% MED: 最小熵反卷积
% 输入:
% x - 观测信号,列向量
% L - 逆滤波器长度
% maxIter - 最大迭代次数
% tol - 迭代终止阈值
% 输出:
% y - 反卷积后的信号
x = x(:);
N = length(x);
% 滤波器初始化:中心位置为1,其余为0
g = zeros(L, 1);
g(round(L/2)) = 1;
X = zeros(N - L + 1, L);
for i = 1:L
X(:, i) = x(i : N - L + i);
end
for iter = 1:maxIter
y = X * g;
% 计算统计量
y2 = y .* y;
y4 = y2 .* y2;
y_sum2 = sum(y2);
y_sum4 = sum(y4);
denom = (y_sum2)^2;
if denom < eps
break;
end
b = (y_sum4 / denom) * y .* y2 / y_sum2;
% 最小二乘求解滤波器系数
g_new = (X' * X) \ (X' * b);
% 归一化
g_new = g_new / norm(g_new);
if norm(g_new - g) < tol
g = g_new;
break;
end
g = g_new;
end
y = X * g;
end
这段代码用了一个滑动矩阵X来替代显式的卷积,好处是后续最小二乘求解可以直接用矩阵运算,适合教学演示。矩阵构造部分要注意:当L很大时,X的尺寸是(N-L+1)×L,内存占用会比较大。处理长信号时建议改用conv或fftfilt来降低内存压力。
4.2 MCKD核心函数实现
MCKD的实现比MED复杂,核心是滤波器更新公式中要对T移位后的信号做互相关。下面给出一个可直接运行的版本,适用于单通道观测信号。
matlab复制function [y, g] = mckd_filter(x, T, L, M, maxIter, tol)
% MCKD: 最大相关峰度反卷积
% 输入:
% x - 观测信号,列向量
% T - 目标周期(样本点数)
% L - 逆滤波器长度
% M - 移位数,默认1或2
% maxIter - 最大迭代次数
% tol - 迭代终止阈值
% 输出:
% y - 反卷积后的信号
% g - 滤波器系数
x = x(:);
N = length(x);
g = zeros(L, 1);
g(round(L/2)) = 1;
% 构造前向移位索引,用于计算 x_{n-mT}
T_max = M * T + L + 1;
if T_max > N
error('信号长度不足以支持当前T和M');
end
validN = N - T_max + 1; % 可以完整计算的采样点数
X = zeros(validN, L);
for i = 1:L
X(:, i) = x(i : i + validN - 1);
end
alpha = 1e-4; % 正则化系数
for iter = 1:maxIter
y_all = X * g;
% 这里只取有效段参与统计
y_len = length(y_all);
% 计算相关峭度分子中的乘法项
% 核心思路:构造 y_{n-mT} 的矩阵形式
denom = 0;
numer = zeros(L, 1);
y2norm = sum(y_all.^2);
if y2norm < eps
break;
end
% 先生成移位数矩阵的各个分量
y_shift = zeros(y_len, M+1);
for m = 0:M
shift = m * T;
for n = shift+1:y_len
y_shift(n, m+1) = y_all(n - shift);
end
end
% 相关峭度分子累乘项,实际是 y_{n}*y_{n-T}*...*y_{n-MT}
prod_shift = ones(y_len, 1);
for m = 0:M
prod_shift = prod_shift .* y_shift(:, m+1);
end
% 分母
denom = y_len * (y2norm)^(M+1);
numer = zeros(L, 1);
% 对每个滤波器系数位置构造互相关向量
for k = 1:L
tmp = X(:, k) .* prod_shift;
numer(k) = sum(tmp) / denom;
end
% 更新滤波器系数
A = X' * X;
g_new = (A + alpha * eye(L)) \ numer;
g_new = g_new / norm(g_new);
if norm(g_new - g) < tol
g = g_new;
break;
end
g = g_new;
end
y = X * g;
end
这里我给出一版偏教学性质的实现,把移位矩阵和互相关向量都显式构造出来了,方便理解每一步在干什么。实际工程中完全可以用更紧凑的向量化写法替代层叠for循环,但核心逻辑不变。特别提醒:MCKD的迭代结果对初始值有一定的敏感性,如果初始化滤波器差异太大,个别情况下会收敛到局部极值。稳妥的做法是先用MED处理一遍,把MED输出信号的周期粗估出来,再用这个周期作为MCKD的T初值,效果通常更稳定。
4.3 周期估计的完整调用流程
有了MED和MCKD函数之后,周期估计的完整流程可以这样走:
- 读入振动信号,先做去均值和采样率确认,必要时重采样。
- 对原始信号做带通滤波,过滤掉低频大能量成分,保留结构共振附近的频段。滤波器类型我习惯用零相移的filtfilt,避免相位偏移对周期检测的干扰。
- 运行MED,得到恢复信号y_med。
- 对y_med做Hilbert包络谱,观察频谱中峰值明显的频率f1。如果f1和理论故障特征频率对得上,那基本可以直接用。
- 如果f1不明确,或频谱中含有干扰频率,以理论特征频率f_th为核心,计算T = fs / f_th,在0.8T~1.2T范围内扫描,对每个T运行MCKD并计算输出信号的包络谱特征频率幅值,取幅值最大的T作为周期估计结果。
- 画出时域波形和包络谱,判断冲击间隔是否为等间隔。
第二步的带通滤波参数选择,建议不要依赖谱峭度自动选带,而是结合结构模态和传感器频响范围进行经验选择。反正我自己的经验是,带通范围过宽时,MCKD会把中心频率较高的调制干扰当成目标冲击,反而更容易出错。
5. 仿真信号验证:好坏对比一下就知道
5.1 构造带噪的周期性冲击信号
理论讲得再多,不如跑一个仿真验证直观。下面构造一个模拟轴承外圈故障的信号:周期冲击串经过一个二阶谐振器,再叠加随机噪声和转频谐波干扰。
matlab复制fs = 20000; % 采样率
t = 0:1/fs:1; % 1秒信号
N = length(t);
% 故障特征频率
f_fault = 80; % 外圈故障特征频率
T = round(fs / f_fault); % 周期点数
% 冲击串
impulse = zeros(N, 1);
for k = 500: T : N
impulse(k) = 1;
end
% 谐振响应:二阶系统
fn = 3000; % 共振频率
zeta = 0.05;
[bb, aa] = butter(2, [1500 5000]/(fs/2), 'bandpass');
path_response = filter(bb, aa, impulse);
% 转频干扰:50Hz及其谐波
vib = 0.8 * sin(2*pi*50*t)' + 0.4 * sin(2*pi*100*t)' + 0.2 * sin(2*pi*150*t)';
% 叠加噪声
noise = 0.5 * randn(N, 1);
x = path_response + vib + noise;
这段代码构造的信号里,周期性冲击其实是相当弱的,包络谱分析很容易被转频谐波盖过去。接下来分别用MED和MCKD处理,对比包络谱中80Hz处的幅值。
5.2 MED/MCKD处理效果对比
直接在原始信号上做包络谱,80Hz处基本看不出明显峰值,因为冲击能量被谐振器分散到了2~4kHz频段,又被转频干扰压制。用MED处理之后,时域波形里能够看到一串稀疏尖峰,但尖峰间隔并不完全均匀,因为随机噪声里的个别高幅值样本也被MED当成了“稀疏冲击”放大了。
再用MCKD处理,T设定为实际周期250点(=20000/80),M=1。输出信号的时域波形明显“干净”很多,冲击间隔均匀,包络谱80Hz处出现清晰的峰值,二倍频160Hz也有比较明显的凸起。这个对比很直观地说明了引入周期先验的价值。
5.3 周期估计结果的可视化判读
判读结果时,我一般先看时域波形中的冲击间隔是否均匀,再看包络谱主峰是否突出。有时候包络谱里80Hz确实有峰,但旁边72Hz、95Hz附近也有峰,这时候需要结合时域波形做二次确认。真正的故障冲击在时域里应该是“等间距出现且幅值相对稳定”的,随机干扰则不具备这个特征。
如果把T当作未知量来扫描,扫描范围可以在理论值附近±20%展开。每跑一次MCKD计算一次输出包络谱目标频率处幅值,画一条“T值-幅值”曲线,曲线会在真实周期处出现明显尖峰。这个尖峰的位置就可以作为精细周期估计的结果。
6. 实战中的参数调试经验与常见误区
6.1 滤波器长度L到底怎么定
L的选择没有统一公式,但有一个经验参考:L应该大于路径脉冲响应的持续时间对应的样本数。换句话说,如果路径响应持续约50个样本,那么L至少要取到50以上。实际操作中可以先做一个初步试验:取L=50、100、150、200分别运行,比较输出包络谱特征频率幅值。
还有一个工程细节:L不能超过信号长度的1/3到1/2。因为MED和MCKD内部都要构造(N-L+1)×L的矩阵,当L接近N时,矩阵求逆必然病态,数值结果完全不可信。
6.2 迭代次数设多少合适
MED和MCKD的收敛速度通常比较快,大部分情况下30次以内就能收敛。我在代码里默认设maxIter=100,但实际经常在10~20次就到收敛阈值了。不要一味增加迭代次数,迭代过多且没有阈值限制时,滤波器会逐步拟合噪声中的细微结构,导致输出信号“过锐化”,反而丢失了真实的冲击周期结构。
另外一个重要的点:收敛阈值tol不要设置得太小。设成1e-6通常足够,设成1e-12不仅收敛慢,而且基本是在追尾噪声波动。
6.3 和包络谱、谱峭度怎么搭配使用
最后说一个方法论层面的建议。MED/MCKD不是包络谱的替代品,而是包络谱的“前置处理步骤”。正确的用法是:先用盲反卷积方法恢复冲击序列,再用包络谱分析恢复信号的周期性。反过来,谱峭度可以辅助确定带通滤波范围,但不建议直接作为唯一的选带依据。
在实际项目里,我常用的组合拳是:先做高通滤波和去趋势 → MCKD(T用理论特征频率粗算)→ 对输出做包络谱 → 用包络谱峰值精确修正T → 再用修正后的T跑一次MCKD → 最终包络谱。这个流程跑下来,对滚动轴承内圈、外圈、滚珠故障的周期估计都比较稳定。
有一点需要提醒:MCKD的周期先验是一把双刃剑。如果齿轮箱里同时存在两个特征频率很接近的故障源,MCKD很可能会“偏袒”其中一个。这种情况下需要把两个T都纳入扫描范围,分别运行MCKD后比较输出信号中对应频率成分的幅值,而不是奢望一次处理就能把两个周期同时提取出来。
我这些年在多个实测数据集上做过验证,盲反卷积方法对故障初期的微弱冲击提取效果明显优于直接用包络谱,但它的效果高度依赖参数选择和对信号背景的预判。建议大家在跑仿真验证时,把信噪比逐步调低,亲自感受一下MED和MCKD在光滑噪声下的表现差异,这样才能在实测信号里做出更有把握的参数判断。
最后再分享一个小技巧:MCKD跑完后,如果输出信号的包络谱在目标频率处幅值不够突出,不妨把原始信号先分段,每段单独做MCKD,然后把各段包络谱做平均。这种“分段处理-谱平均”的做法能有效压制随机干扰对周期估计的影响,比盲目调大滤波器长度要靠谱得多。
