ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

变转速工况下阶次分析为何优于FFT:MATLAB实现完整链路

变转速工况下阶次分析为何优于FFT:MATLAB实现完整链路

简介:这份资源面向从事振动分析与旋转机械故障诊断的工程师、研究人员及高校学生,提供一套基于MATLAB的阶次分析完整实现代码,帮助解决变转速工况下故障特征频率难以准确提取的问题。压缩包共3个文件,约13.72MB,包含MATLAB脚本、矩阵数据文件与一张阶次分析示意图,分别对应算法实现、待处理数据与概念可视化,便于直接运行与对照理解。资源围绕信号采集、预处理、阶次跟踪转换到阶次谱分析这一完整流程展开,读者可据此掌握将时间信号转换为角度域信号的核心思路,并区分机械故障特征频率与其他频率成分。目前已有357人学习,适合希望快速上手阶次分析、借助现成脚本验证算法并迁移到自身振动数据的中高级学习者参考。

1. 阶次分析为什么在变转速工况下比 FFT 更靠谱

做旋转机械振动测试的人,早晚会撞上同一个尴尬:电机升速、齿轮箱加减载、发动机扫频,只要转速一变,频谱上的峰值就糊成一片。你盯着 FFT 谱图想找啮合频率,结果发现 3000 rpm 和 3050 rpm 的峰值叠在一起,边带全糊了。这不是你仪器的问题,是 FFT 的底层假设——信号在分析窗内平稳——被打破了。

阶次分析(Order Analysis)解决的就是这件事。它把时间域等间隔采样的信号,通过转速信息重采样成角度域等间隔信号,于是所有与转速成整数倍关系的分量(1 阶、2 阶、齿轮啮合阶次)在阶次谱上变成固定位置的尖峰,跟转速怎么变没关系。matlab 实现阶次分析完整代码文件这个方向,本质上就是把这套「转速跟踪 + 角度重采样 + 阶次谱」的链路用脚本串起来,让做旋转机械测试的人能直接跑自己的数据。

适合谁:手里有振动加速度信号和同步转速脉冲(或键相)信号的测试工程师、做 NVH 的、搞旋转机械故障诊断的。如果你只有振动信号没有转速,那得先做转速估计,那是另一条路,这篇先不展开。

2. 阶次分析的核心链路:从等时间采样到等角度采样

2.1 为什么必须重采样,不能直接对频谱做插值

很多人第一反应是:我直接对 FFT 结果做插值不就行了?不行。FFT 的频率分辨率是固定的 Δf = fs/N,转速变化时,同一个阶次对应的频率在变,你在频率轴上插值等于在追一个移动靶,阶次能量会散开。正确做法是在时域先把「等时间间隔」的采样点,重采样成「等角度间隔」的采样点,让每个采样点代表转过固定角度,比如每转 1024 个点。这样转速信息就被「归一化」掉了,剩下的信号在角度域是平稳的,FFT 才有意义。

重采样的关键是角度-时间映射。你需要知道每个角度对应的时间戳,然后在这些时间戳上对原始信号做插值。转速脉冲信号就是干这个用的:每个脉冲代表转过一个固定角度(比如每转一个键相脉冲就是 360°),脉冲之间的时间间隔告诉你这段转过的平均转速。

2.2 转速脉冲到角度-时间映射的转换

假设你有一个键相脉冲信号,每转一个脉冲。用上升沿检测拿到每个脉冲的时间戳 t_i,那么第 i 转到第 i+1 转之间,角度从 0 到 2π 线性变化(假设匀速,短时内合理)。如果要每转 1024 个角度点,就在每转内插 1024 个角度值,对应的时间用线性插值算出来。

% 假设 keyphasor 是键相脉冲信号,fs 是采样率 % 检测上升沿:从负到正穿越阈值 threshold = 0.5 * (max(keyphasor) + min(keyphasor)); above = keyphasor > threshold; pulse_idx = find(diff([0; above]) == 1); % 上升沿位置 t_pulse = (pulse_idx - 1) / fs; % 脉冲时间戳 % 每转采样点数 samples_per_rev = 1024; % 构建角度-时间映射 % 每转内角度从 0 到 2*pi,时间线性插值 n_rev = length(t_pulse) - 1; theta = []; t_theta = []; for i = 1:n_rev t_start = t_pulse(i); t_end = t_pulse(i+1); theta_rev = linspace(0, 2*pi, samples_per_rev+1); theta_rev(end) = []; % 避免重复,下一转开头补 t_rev = linspace(t_start, t_end, samples_per_rev+1); t_rev(end) = []; theta = [theta, theta_rev + (i-1)*2*pi]; t_theta = [t_theta, t_rev]; end

这段代码的逻辑:先找键相脉冲的上升沿,拿到每转的起始时间。然后在每转内均匀分配角度点,用线性插值算对应时间。注意每转末尾点去掉,避免和下一转起点重复。参数 samples_per_rev 决定角度分辨率,1024 是常用值,阶次分析上限大概到 400 阶左右(奈奎斯特),实际看需求。

2.3 用 interp1 做角度域重采样

有了 t_theta 和原始振动信号,就可以插值了。原始信号是等时间采样的,t_theta 是等角度对应的时间点,直接 interp1。

% vib 是振动信号,t_vib 是原始时间轴 t_vib = (0:length(vib)-1) / fs; % 线性插值到角度域 vib_theta = interp1(t_vib, vib, t_theta, 'linear', 0); % 此时 vib_theta 是等角度采样信号,角度间隔 d_theta = 2*pi/samples_per_rev

interp1 的最后一个参数是外插值,超出范围补 0。这里要注意:t_theta 必须在 t_vib 范围内,否则插值会出 NaN 或补零。实际处理时,键相脉冲可能比振动信号晚开始或早结束,需要截取重叠段。

2.4 阶次谱计算与阶次轴构建

重采样后,对 vib_theta 做 FFT,得到的是「每转周期数」的谱,也就是阶次谱。角度域采样率是 samples_per_rev 点/转,所以阶次分辨率是 1/转数。假设总转数 n_rev,角度域总点数 N = n_rev * samples_per_rev,阶次轴从 0 到 samples_per_rev/2,分辨率 samples_per_rev/N = 1/n_rev。

N = length(vib_theta); V = fft(vib_theta .* hanning(N)); % 加汉宁窗减少泄漏 V = V(1:floor(N/2)+1); order_axis = (0:floor(N/2)) * (samples_per_rev / N); amplitude = abs(V) * 2 / sum(hanning(N)); % 幅值修正 % 画阶次谱 plot(order_axis, amplitude); xlabel('阶次'); ylabel('幅值'); xlim([0, 50]); % 通常看低阶

这里加汉宁窗是为了减少非整周期截断带来的泄漏。幅值修正用窗函数和的倒数,保证峰值幅值接近真实值。阶次轴上限是 samples_per_rev/2,但实际有意义的一般在 50 阶以内,看具体机械。

3. 用 matlab 脚本把整条链路串起来:从数据加载到阶次谱出图

3.1 数据准备与参数配置的推荐做法

实际数据可能是 mat 文件、csv 或者直接来自采集卡。我一般把参数集中写在脚本开头,方便改。

%% 参数配置 fs = 51200; % 采样率 Hz samples_per_rev = 1024; % 每转角度采样点数 order_max = 50; % 关注的最高阶次 window_type = 'hanning'; % 窗函数 %% 加载数据 % 假设 data.mat 里有 vib 和 keyphasor 两个变量 load('data.mat'); % 如果长度不一致,截取最短 L = min(length(vib), length(keyphasor)); vib = vib(1:L); keyphasor = keyphasor(1:L);

参数说明:fs 必须和采集设置一致,否则时间轴全错。samples_per_rev 决定阶次上限,1024 对应 512 阶,够用。order_max 只影响画图范围,不影响计算。

3.2 键相脉冲处理与转速曲线提取

键相脉冲可能带噪声,直接找上升沿会误触发。我一般先做简单滤波或设置滞回阈值。

% 滞回阈值:高于 high 才算高,低于 low 才算低 high = 0.7 * max(keyphasor); low = 0.3 * max(keyphasor); state = false; pulse_idx = []; for k = 2:length(keyphasor) if ~state && keyphasor(k) > high state = true; pulse_idx(end+1) = k; elseif state && keyphasor(k) < low state = false; end end t_pulse = (pulse_idx - 1) / fs; % 瞬时转速:相邻脉冲间隔对应一转 if length(t_pulse) > 1 rpm = 60 ./ diff(t_pulse); t_rpm = t_pulse(1:end-1); % 画转速曲线看看 figure; plot(t_rpm, rpm); xlabel('时间 s'); ylabel('转速 rpm'); end

滞回阈值能有效防止噪声引起的多次触发。转速曲线能帮你判断数据质量:如果转速跳变剧烈,可能是脉冲漏检或误检,需要回头检查键相信号。

3.3 角度重采样与阶次谱的完整函数封装

把前面几步封装成函数,方便复用。

function [order_axis, amplitude] = order_analysis(vib, keyphasor, fs, samples_per_rev, order_max) % 键相脉冲检测(滞回) high = 0.7 * max(keyphasor); low = 0.3 * max(keyphasor); state = false; pulse_idx = []; for k = 2:length(keyphasor) if ~state && keyphasor(k) > high state = true; pulse_idx(end+1) = k; elseif state && keyphasor(k) < low state = false; end end t_pulse = (pulse_idx - 1) / fs; % 角度-时间映射 n_rev = length(t_pulse) - 1; theta = []; t_theta = []; for i = 1:n_rev t_start = t_pulse(i); t_end = t_pulse(i+1); theta_rev = linspace(0, 2*pi, samples_per_rev+1); theta_rev(end) = []; t_rev = linspace(t_start, t_end, samples_per_rev+1); t_rev(end) = []; theta = [theta, theta_rev + (i-1)*2*pi]; t_theta = [t_theta, t_rev]; end % 重采样 t_vib = (0:length(vib)-1) / fs; vib_theta = interp1(t_vib, vib, t_theta, 'linear', 0); % 阶次谱 N = length(vib_theta); w = hanning(N); V = fft(vib_theta .* w); V = V(1:floor(N/2)+1); order_axis = (0:floor(N/2)) * (samples_per_rev / N); amplitude = abs(V) * 2 / sum(w); % 截取关注阶次 idx = order_axis <= order_max; order_axis = order_axis(idx); amplitude = amplitude(idx); end

调用方式:

[oa, amp] = order_analysis(vib, keyphasor, fs, 1024, 50); figure; plot(oa, amp); xlabel('阶次'); ylabel('幅值'); grid on;

这个函数把整条链路包进去了,输入原始信号和键相,输出阶次谱。注意 interp1 的外插值补零,如果 t_theta 超出 t_vib 范围,那段会变成 0,影响谱质量。实际用的时候最好截取有效段。

3.4 阶次谱的幅值修正与窗函数选择

幅值修正容易被忽略。不加窗时,如果信号非整周期截断,峰值会泄漏,幅值偏低。加汉宁窗后,主瓣变宽但泄漏小,幅值修正系数是 2/sum(w)。如果关注峰值精确幅值,可以用平顶窗,主瓣更宽但幅值精度高。

% 平顶窗对比 w_flat = flattopwin(N); V_flat = fft(vib_theta .* w_flat); V_flat = V_flat(1:floor(N/2)+1); amp_flat = abs(V_flat) * 2 / sum(w_flat);

窗函数选择看需求:汉宁窗通用,平顶窗适合校准幅值,矩形窗适合整周期采样。阶次分析里,转速波动导致非整周期是常态,汉宁窗是稳妥选择。

4. 避坑与排查:阶次分析代码最容易翻车的 5 个地方

4.1 键相脉冲漏检导致转速曲线断裂

现象:转速曲线出现尖刺或跳变到异常值,阶次谱上出现大量模糊边带。 原因:键相信号幅值波动或噪声导致滞回阈值误判,漏掉一个脉冲,相邻脉冲间隔变成两转,转速算出来减半。 解决:先画键相原始信号看幅值范围,调整 high/low 阈值;或者用中值滤波平滑后再检测;漏检严重时改用互相关或峰值搜索加最小间隔约束。

4.2 重采样后信号长度不对导致阶次轴错位

现象:阶次谱峰值位置和理论阶次对不上,比如啮合阶次应该在第 20 阶,结果出现在第 18 阶。 原因:t_theta 构建时每转点数不一致,或者 interp1 外插补零段被算进 FFT,等效于信号被截断。 解决:检查 length(vib_theta) 是否等于 n_rev * samples_per_rev;确保 t_theta 在 t_vib 范围内,超出部分截掉;打印 n_rev 和 N 确认。

4.3 转速变化剧烈时线性插值误差大

现象:升速很快的段,阶次谱峰值展宽,分辨率下降。 原因:每转内假设匀速,但实际转速在转内也有变化,线性插值引入误差。 解决:提高 samples_per_rev 到 2048 或 4096;或者用样条插值 interp1(..., 'spline'),但要注意样条在脉冲间隔大时可能过冲;极端情况用阶次跟踪滤波。

4.4 振动和键相通道不同步

现象:阶次谱相位信息混乱,或者峰值幅值偏低。 原因:多通道采集时通道间有采样延迟,或者键相通道有额外滤波导致延迟。 解决:查采集设置里的通道同步方式;如果延迟固定,可以在时间轴上补偿;用同一采集设备同一时钟是最稳的。

4.5 阶次谱幅值比预期低很多

现象:算出来的幅值和频谱对不上,差几倍。 原因:窗函数幅值修正系数用错,或者 FFT 后只取了半边谱但没乘 2。 解决:确认修正公式:单边谱幅值 = abs(V) * 2 / sum(w)。如果用了矩形窗,sum(w)=N,系数是 2/N。检查是否漏乘 2。

5. 进阶技巧:用阶次谱做故障特征提取与趋势监控

阶次谱算出来只是第一步,真正有价值的是从阶次谱里提取故障特征。齿轮箱的啮合阶次、轴承的故障阶次、不平衡的 1 阶,这些在阶次谱上位置固定,适合做自动监控。

我一般会做两件事:一是提取特定阶次的幅值随时间变化,画趋势图;二是算阶次谱的谐波能量比,比如啮合阶次及其谐波的总能量占整体能量的比例,比例升高往往意味着齿轮磨损。

% 提取特定阶次幅值(比如第 20 阶啮合) target_order = 20; [~, idx] = min(abs(order_axis - target_order)); amp_trend = amp(idx); % 单次分析 % 如果要随时间监控,需要分段做阶次分析 segment_time = 1; % 秒 n_seg = floor(length(vib) / (segment_time * fs)); trend = zeros(1, n_seg); for s = 1:n_seg idx_start = (s-1) * segment_time * fs + 1; idx_end = s * segment_time * fs; [oa_seg, amp_seg] = order_analysis(vib(idx_start:idx_end), ... keyphasor(idx_start:idx_end), fs, 1024, 50); [~, idx_o] = min(abs(oa_seg - target_order)); trend(s) = amp_seg(idx_o); end plot((1:n_seg)*segment_time, trend); xlabel('时间 s'); ylabel('第 20 阶幅值');

分段做阶次分析时,每段内转速变化不能太大,否则重采样误差累积。段长一般 0.5 到 2 秒,看转速变化率。趋势图能看出故障发展,比单次谱图更有工程价值。

另一个技巧是阶次谱的倒谱分析,能提取谐波族的结构,对齿轮故障很敏感。但那是另一个话题了。

最后说个血泪经验:阶次分析代码跑通不难,难的是数据质量。键相信号干净、通道同步、转速范围合理,这三点做到了,代码基本不会翻车。我见过太多人卡在键相脉冲检测上,回头查原始信号发现键相幅值只有 0.1V,噪声都比它大。先看数据,再调代码,这个顺序别搞反。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进