简介这份资源面向信号处理方向的学生、科研人员与工程技术人员聚焦局部特征尺度分解LCD这一非平稳、非线性信号分析方法。资源以MATLAB为实现环境演示如何通过三次样条插值获得光滑的内禀尺度分量帮助读者理解尺度空间构建、局部特征检测与分量提取的完整思路适用于异常检测、模式识别、信号恢复等场景。压缩包共6个文件以5个m脚本为主要内容另含1个系统隐藏文件整体约6KB体积轻量便于快速查阅与二次开发。目前已有382人学习下载说明其在同类方法中具备一定参考价值。读者可借助脚本理解LCD从信号预处理、尺度生成到分量提取的实现脉络并在此基础上调整参数、适配不同信号类型为后续算法优化与工程落地提供可复用的代码基础。1. 局部特征尺度分解到底在拆什么从一段轴承振动信号说起手里有一段轴承外圈故障的振动信号采样率 12.8 kHz长度 8192 点。你把它画出来看到的是密密麻麻的振荡故障冲击被淹没在背景噪声和转频调制里。想做包络谱得先把高频载波和低频趋势分开想算峭度指标又得把不同时间尺度的成分剥离开。这时候经验模态分解EMD是很多人的第一反应但一上手就翻车模态混叠、端点飞掉、筛分次数全靠拍脑袋。局部特征尺度分解Local Characteristic-scale DecompositionLCD就是在这个位置上出现的——它同样把复杂信号拆成若干个内禀尺度分量Intrinsic Scale ComponentISC但换了一套更“讲道理”的极值点定义和插值方式用三次样条插值去构造光滑的基线从而拿到物理意义更干净的分量。这篇笔记面向的是已经会用 MATLAB 做信号处理、但被 EMD 的模态混叠和端点效应折磨过的工程师。我会把 LCD 的核心机制、MATLAB 落地代码、参数怎么调、坑在哪按能直接复现的粒度讲清楚。你不需要先读完一堆论文跟着代码跑一遍再回头看原理反而更快。2. LCD 的基线构造逻辑为什么是三次样条插值2.1 从极值点到内禀尺度分量LCD 和 EMD 的分岔口EMD 的做法是找全部极大值和极小值分别做上、下包络取均值当基线反复筛分直到满足 IMF 条件。问题在于包络均值本身可能引入新的极值筛分次数一多分量就被“筛糊”了。LCD 换了个思路它不追求上下包络而是直接在相邻极值点之间定义一段“基线”让信号减去这条基线后剩下的部分成为 ISC。具体来说LCD 先找出信号的所有极值点极大和极小统一处理然后在每两个相邻极值点之间构造一条基线。这条基线要满足一个条件在极值点处基线值等于该极值点与相邻两个极值点连线在对应位置的某种插值。早期实现用的是线性插值基线是折线不够光滑分解出来的 ISC 会有毛刺。后来普遍改用三次样条插值因为三次样条在节点处一阶、二阶导数连续能给出光滑的基线ISC 的波形更干净后续做包络谱时虚假峰明显减少。这里的关键参数是极值点的判定方式。LCD 对极值点的定义比 EMD 宽松它允许在极值点之间插入“伪极值点”条件是相邻极值点的间隔超过某个阈值。这个阈值通常取信号长度的某个比例或者用极值点间隔的中位数来定。阈值太大会漏掉细节太小会引入噪声极值。我一般先用默认的间隔中位数跑一遍看 ISC 数量再微调。2.2 三次样条插值在 LCD 里的具体角色三次样条插值在 LCD 里承担的是“基线生成器”的角色。给定一组极值点 $(t_i, x_i)$以及每个极值点对应的基线约束值 $b_i$三次样条会构造一条分段三次多项式 $B(t)$满足 $B(t_i)b_i$且在内部节点处 $B(t)$ 和 $B(t)$ 连续。LCD 的基线约束 $b_i$ 通常取相邻极值点连线的中点值或者用极值点幅值的加权平均。这样构造出来的基线既不会像线性插值那样产生尖角也不会像高阶多项式那样在端点附近剧烈振荡。在 MATLAB 里spline函数就是干这个的。但要注意spline默认使用 not-a-knot 端点条件对于信号端点附近的基线这个条件可能让基线外推得比较离谱。LCD 的端点效应很大一部分就来自这里。常见做法是在信号两端各延拓若干个极值点延拓方式可以用镜像对称或者极值点周期延拓然后再做样条插值最后把延拓部分截掉。延拓长度一般取 1 到 2 个极值点间隔。下面这段代码展示了 LCD 单次筛分中如何用三次样条插值构造基线并得到第一个 ISC 的雏形。代码里包含了极值点检测、基线约束计算和样条插值三个步骤。function [isc, residual] lcd_sift_once(x, t) % x: 输入信号列向量 % t: 时间轴列向量与 x 等长 % isc: 本次筛分得到的内禀尺度分量雏形 % residual: 减去 isc 后的剩余部分 % 1. 检测极值点极大和极小统一索引 dx diff(x); ext_idx find(dx(1:end-1) .* dx(2:end) 0) 1; % 极值点位置 % 补充端点附近的极值避免端点无约束 if isempty(ext_idx) error(信号中没有检测到极值点请检查信号是否过于平滑); end % 2. 计算相邻极值点间隔用于伪极值点判定 ext_t t(ext_idx); ext_x x(ext_idx); intervals diff(ext_t); med_interval median(intervals); % 3. 在间隔过大的地方插入伪极值点线性插值 new_t ext_t(1); new_x ext_x(1); for k 1:length(intervals) if intervals(k) 1.5 * med_interval % 在中间插入一个伪极值点幅值取两端平均 mid_t (ext_t(k) ext_t(k1)) / 2; mid_x (ext_x(k) ext_x(k1)) / 2; new_t [new_t; mid_t; ext_t(k1)]; new_x [new_x; mid_x; ext_x(k1)]; else new_t [new_t; ext_t(k1)]; new_x [new_x; ext_x(k1)]; end end % 4. 端点延拓镜像对称延拓两个极值点间隔 left_ext 2 * new_t(1) - new_t(3:-1:2); right_ext 2 * new_t(end) - new_t(end-1:-1:end-2); ext_t_full [left_ext; new_t; right_ext]; ext_x_full [new_x(3:-1:2); new_x; new_x(end-1:-1:end-2)]; % 5. 计算基线约束值相邻极值点连线的中点 b zeros(size(ext_x_full)); for k 2:length(ext_x_full)-1 b(k) (ext_x_full(k-1) ext_x_full(k1)) / 2; end b(1) ext_x_full(1); b(end) ext_x_full(end); % 6. 三次样条插值构造基线 baseline_full spline(ext_t_full, b, t); % 7. 减去基线得到 ISC 雏形 isc x - baseline_full; residual baseline_full; end这段代码的逻辑是先找极值点再根据间隔中位数插入伪极值点然后做端点镜像延拓接着用相邻极值点连线的中点作为基线约束值最后用spline做三次样条插值得到基线。参数方面1.5 * med_interval这个阈值控制伪极值点的插入密度调大它会让 ISC 数量减少调小则增加。端点延拓的长度我固定延拓两个极值点间隔实际使用时可以根据信号长度调整信号短就延拓一个信号长就延拓三个。注意spline函数在极值点数量少于 4 个时会报错所以代码里加了isempty检查。如果信号本身极值点很少说明它可能已经是一个单调趋势不需要继续分解。2.3 筛分停止条件ISC 判据怎么设才不玄学EMD 的筛分停止条件通常是“连续两次筛分结果的标准差小于某个阈值”这个阈值取 0.2 到 0.3 之间但具体取多少很玄学。LCD 的 ISC 判据更直接一个分量如果满足“极值点数量与过零点数量之差不超过 1”并且“在整个数据段内任意一点的局部极大值包络和局部极小值包络的均值接近零”就认为它是一个 ISC。实际实现时为了效率通常只检查第一个条件第二个条件用基线幅值的均方根来近似。我一般用两个条件联合判断一是极值点数和过零点数之差小于等于 1二是本次筛分得到的isc的均方根与上一次筛分的均方根之比大于 0.95。第二个条件防止筛分次数过多把分量筛成常数。筛分次数上限设 10 次超过就强制停止避免死循环。function [isc_list, res] lcd_decompose(x, t, max_isc, sift_max) % x: 输入信号 % t: 时间轴 % max_isc: 最大 ISC 数量 % sift_max: 单次筛分最大迭代次数 isc_list []; res x; for i 1:max_isc % 对当前剩余信号做筛分 h res; for s 1:sift_max [isc_tmp, ~] lcd_sift_once(h, t); % 判据1极值点与过零点数量差 dx diff(isc_tmp); ext_num sum(dx(1:end-1) .* dx(2:end) 0); zero_num sum(isc_tmp(1:end-1) .* isc_tmp(2:end) 0); % 判据2均方根变化 if s 1 rms_ratio rms(isc_tmp) / rms(prev_isc); else rms_ratio 1; end if abs(ext_num - zero_num) 1 rms_ratio 0.95 break; end prev_isc isc_tmp; h isc_tmp; end isc_list [isc_list, isc_tmp]; res res - isc_tmp; % 剩余信号如果是单调趋势或幅值过小停止分解 if sum(diff(res(1:end-1)) .* diff(res(2:end)) 0) 2 || rms(res) 1e-6 break; end end end这段代码把单次筛分和整体分解串起来。max_isc一般取 5 到 8取决于信号复杂度。sift_max取 10 足够。判据里的0.95这个比例如果信号噪声大可以放宽到 0.9让筛分早点停避免把噪声当成 ISC 反复筛。3. 在 MATLAB 里跑通 LCD从单分量到多分量分解3.1 构造一个可复现的测试信号要验证 LCD 能不能用先别拿真实轴承信号那个太复杂出了问题不知道是算法还是数据。我习惯先构造一个已知成分的合成信号两个不同频率的调幅分量加一个趋势项。这样分解出来的 ISC 应该对应这两个调幅分量和趋势项顺序和幅值都能对得上。fs 12800; % 采样率 T 1; % 信号时长 t (0:1/fs:T-1/fs); % 时间轴 % 分量1200 Hz 载波被 20 Hz 调制 x1 (1 0.5*sin(2*pi*20*t)) .* sin(2*pi*200*t); % 分量2500 Hz 载波被 50 Hz 调制 x2 (0.8 0.3*cos(2*pi*50*t)) .* sin(2*pi*500*t); % 趋势项低频多项式 x3 0.5 * (t - 0.5).^2; % 合成信号 x x1 x2 x3; % 加少量高斯噪声 rng(0); x x 0.05 * randn(size(t));这个信号里200 Hz 和 500 Hz 两个载波成分的极值点间隔分别对应不同的时间尺度趋势项则是一个缓慢变化的基线。LCD 分解后前两个 ISC 应该分别对应这两个调幅分量第三个 ISC 或残余对应趋势项。如果分解出来的 ISC 顺序乱了或者某个 ISC 里混了两个频率说明参数需要调。3.2 调用 LCD 分解并画出 ISC 时域波形把第 2 章的lcd_decompose函数拿来用设置max_isc 4sift_max 10然后画图。max_isc 4; sift_max 10; [isc_list, res] lcd_decompose(x, t, max_isc, sift_max); % 画图原始信号 各 ISC 残余 figure(Position, [100 100 800 600]); subplot(max_isc2, 1, 1); plot(t, x); title(原始信号); ylabel(幅值); grid on; for i 1:size(isc_list, 2) subplot(max_isc2, 1, i1); plot(t, isc_list(:, i)); title([ISC , num2str(i)]); ylabel(幅值); grid on; end subplot(max_isc2, 1, max_isc2); plot(t, res); title(残余分量); xlabel(时间 (s)); ylabel(幅值); grid on;跑完这张图你会看到 ISC1 主要是 500 Hz 分量ISC2 是 200 Hz 分量ISC3 是趋势项残余接近零。如果 ISC1 和 ISC2 的顺序反了或者某个 ISC 里同时出现 200 Hz 和 500 Hz说明伪极值点插入阈值1.5 * med_interval需要调。调大阈值ISC 数量减少高频和低频容易混在一个 ISC 里调小阈值ISC 数量增加可能把噪声也拆出来。3.3 用包络谱验证分解效果时域波形只能看个大概真正要验证 LCD 有没有把故障特征分离出来得做包络谱。对每个 ISC 做 Hilbert 变换取包络再对包络做 FFT看故障特征频率处有没有峰。% 对每个 ISC 做包络谱 for i 1:size(isc_list, 2) env abs(hilbert(isc_list(:, i))); env env - mean(env); % 去直流 L length(env); Y fft(env .* hann(L)); P2 abs(Y / L); P1 P2(1:L/21); P1(2:end-1) 2 * P1(2:end-1); f fs * (0:(L/2)) / L; figure; plot(f, P1); xlim([0 1000]); title([ISC , num2str(i), 包络谱]); xlabel(频率 (Hz)); ylabel(幅值); grid on; end对于合成信号ISC1 的包络谱应该在 50 Hz 处有峰对应 500 Hz 载波的调制频率ISC2 的包络谱应该在 20 Hz 处有峰。如果峰的位置不对或者峰被淹没说明该 ISC 里混入了其他分量需要回到分解参数调整。真实轴承信号里外圈故障特征频率比如 BPFO会在包络谱里出现LCD 的作用就是让这个峰比直接对原始信号做包络谱更突出。提示包络谱的频率分辨率取决于信号长度1 秒 12800 点分辨率 1 Hz。如果故障特征频率间隔小于 1 Hz需要加长信号或提高采样率。4. LCD 落地时的避坑与排查端点、模态混叠和参数选择4.1 端点效应为什么你的 ISC 两端总是翘起来现象分解出来的 ISC 在信号两端幅值异常增大包络谱里出现低频虚假峰。原因三次样条插值在端点附近没有足够的约束点not-a-knot 条件会让基线外推导致端点处的 ISC 被“拉”起来。解决在分解前对信号做端点延拓延拓方式用镜像对称或极值点周期延拓延拓长度取 1 到 2 个极值点间隔。代码里已经在lcd_sift_once里做了镜像延拓但延拓长度是固定的两个间隔。如果信号端点本身极值点很少可以手动增加延拓长度或者改用“极值点预测延拓”用端点附近几个极值点的趋势外推一个极值点。4.2 模态混叠一个 ISC 里混了两个频率成分现象某个 ISC 的包络谱里同时出现两个不同频率的峰或者时域波形里能看到明显的拍频。原因伪极值点插入阈值太大导致高频成分的极值点被忽略低频成分的极值点被当成基线约束两个分量被分到同一个 ISC 里。解决把1.5 * med_interval里的系数从 1.5 降到 1.2 或 1.0让更多伪极值点被插入增加基线约束的密度。但系数太小会增加计算量而且可能把噪声极值也插进来。我一般从 1.5 开始如果发现混叠就降到 1.2再不行就 1.0。另一个办法是先用带通滤波把信号分成几个频段再对每个频段单独做 LCD但这会引入滤波器相位问题需要零相位滤波。4.3 筛分次数过多ISC 变成一条直线现象分解到后面几个 ISC 时波形几乎是一条直线幅值接近零但程序还在继续筛分。原因筛分停止条件里的均方根比例阈值0.95太宽松或者极值点与过零点数量差判据在低幅值信号上失效。解决在筛分循环里加一个幅值下限判断如果当前isc_tmp的均方根小于原始信号均方根的 1e-4直接跳出。另外max_isc不要设太大一般 5 到 8 足够设 10 以上容易产生无意义的 ISC。4.4 参数选择极值点间隔中位数受噪声影响大现象同一个信号加不同强度的噪声分解出来的 ISC 数量不一样。原因极值点间隔中位数对噪声敏感噪声极值点会拉低中位数导致伪极值点插入阈值变小ISC 数量增加。解决在检测极值点之前先对信号做一次轻微的平滑滤波比如 5 点滑动平均或者用smoothdata函数。但平滑会削弱高频成分所以平滑窗口不能太大一般 3 到 5 点。另一个办法是用极值点间隔的 75% 分位数代替中位数分位数对噪声更鲁棒。4.5 计算速度三次样条插值在大数据量下的开销现象信号长度 10 万点以上时LCD 分解跑一次要几十秒。原因每次筛分都要做一次三次样条插值而spline函数的计算复杂度是 O(n)筛分次数乘以 ISC 数量总开销很大。解决把信号分段处理每段 1 万点段间重叠 10%分解完再拼接。或者用interp1的spline方法替代spline函数两者结果一致但interp1在某些 MATLAB 版本里更快。另外如果只是做包络谱不需要完整分解可以只分解前 3 个 ISC后面的残余直接忽略。5. 把 LCD 用出效果从分解到故障特征提取的最后一公里LCD 分解本身只是手段最终要落到故障特征提取上。我一般不会把全部 ISC 都拿去做包络谱而是先算每个 ISC 的峭度指标挑峭度最大的那个 ISC 做包络谱。峭度对冲击成分敏感故障冲击越明显峭度越大。但峭度也会被噪声干扰所以我会同时看 ISC 的均方根和峭度选均方根不是最小、峭度最大的那个。% 计算每个 ISC 的峭度和均方根 kurt_vals zeros(1, size(isc_list, 2)); rms_vals zeros(1, size(isc_list, 2)); for i 1:size(isc_list, 2) kurt_vals(i) kurtosis(isc_list(:, i)); rms_vals(i) rms(isc_list(:, i)); end % 选峭度最大的 ISC [~, best_idx] max(kurt_vals); fprintf(选中的 ISC 编号%d峭度%.2f均方根%.4f\n, ... best_idx, kurt_vals(best_idx), rms_vals(best_idx));选好 ISC 后做包络谱然后跟理论故障特征频率对比。如果理论频率处有峰且峰高比相邻频段高 3 倍以上就认为特征提取成功。如果峰不明显可以尝试对选中的 ISC 做一次带通滤波滤波频带根据 ISC 的主频来定通常取 ISC 中心频率的 0.5 到 1.5 倍。还有一个技巧LCD 分解出来的 ISC 顺序是从高频到低频但有时候趋势项会混在第一个 ISC 里。这时候可以先把原始信号减去一个低通滤波后的趋势项再做 LCD。低通截止频率取信号主频的 0.1 倍用零相位滤波filtfilt实现。% 先去趋势再做 LCD fc 0.1 * 200; % 假设主频 200 Hz [b, a] butter(4, fc/(fs/2), low); trend filtfilt(b, a, x); x_detrend x - trend; % 对 x_detrend 做 LCD 分解 [isc_list2, res2] lcd_decompose(x_detrend, t, 4, 10);去趋势后ISC 的数量通常会减少一个而且前几个 ISC 的物理意义更清晰。这个步骤不是必须的但如果你的信号里趋势项幅值很大建议加上。最后说一个我自己的习惯每次用 LCD 处理新信号先拿一段已知成分的合成信号跑一遍确认分解逻辑和参数没问题再上真实数据。真实数据出问题时回头检查极值点检测和端点延拓这两个地方最容易翻车。LCD 不是黑匣子它的每一步都能在代码里找到对应调参的时候心里有数比盲目试要快得多。希望帮到你。本文还有配套的精品资源点击获取