简介:暂态提取变换(TET)是面向工业故障诊断与状态识别场景的一维信号转二维图像方法,特别适用于需要结合深度学习的非线性、非平稳信号分析任务。压缩包提供完整Matlab实现,共18个文件,包含7个m脚本、9个png示例图与2个mat数据文件,整体仅3.15MB,轻量易部署。m脚本覆盖主控制流、暂态提取核心变换、奇异谱分解、Renyi熵计算及二维积分等关键环节;png示例图用于展示不同样本的变换结果;mat文件提供带噪声的原始信号与中间变量,方便直接运行验证。文件结构按处理流程组织,可支撑从信号预处理、TET变换到特征评估与可视化的完整链路,便于用户修改参数或替换自有数据。已有75人学习下载,适合机械、电力、航空等领域的研究者与工程师快速搭建故障诊断预处理模块,为深度学习模型提供高质量的二维图像输入,缩短算法落地周期。
1. 暂态提取变换到底是什么:为什么故障诊断要先做一维转二维
做过轴承故障诊断的人应该都有这种体验:直接拿原始振动信号——一维时间序列——扔给深度学习模型,十有八九效果不稳定,换一个工况就掉点。原因很直接:一维信号里,故障引起的暂态冲击波形短、能量分散,而且和噪声、转频分量混在一起,CNN在这种原始波形上能学到的判别性特征非常有限。而暂态提取变换做的事情,就是把一维振动信号转换成一幅二维图像,让冲击特征变成图像上的纹理、亮斑和边缘,再交给深度学习模型去识别。这样故障诊断问题就被翻译成了图像分类问题,能直接用上成熟的CNN架构。
这篇文章面向的是做旋转机械故障诊断、状态识别的工程师和研究生。我会从TET的原理讲起,给出Matlab实现一维转二维的完整流程,再到把时频图喂给CNN做训练,最后把我踩过的坑一条条列出来。目标很明确:照着做,你能在现有数据上复现出一套可用的诊断方案。
2. TET的时频分析原理与参数选型:中心频率、带宽和分辨率的取舍
2.1 为什么短时傅里叶变换不够,非要再做一次“提取”
暂态提取变换(Transient Extraction Transform,以下简称TET)不是凭空出现的新变换,它是在短时傅里叶变换(STFT)基础上做后处理的一种时频分析方法。STFT大家都熟:用一个滑动窗把信号切成一段段,每段做FFT,得到时间和频率的二维能量分布。但STFT有个老毛病——时频模糊。窗函数的能量泄漏会让一个本该集中在某个频率点的冲击成分,在时频图上变成一片模糊的亮带。做故障诊断时,两个故障特征频率如果靠得近,模糊的时频图很容易让CNN学到错误的纹理。
TET的核心思路是:瞬态信号在频域里具有特定的幅值特性,在瞬时频率附近幅值变化最剧烈。利用这个特性,可以在每个时间点上沿着频率方向寻找瞬态成分所在的频点,把STFT结果中该频点附近的模糊能量“提取”出来,集中到一条窄带里。效果就是时频图从“糊的”变成“锐的”,每个冲击在图上都是一条细亮的轨迹。
从数学上看,TET对STFT结果施加了一个“瞬时频率提取”操作。具体做法是用STFT系数的相位信息构造一个频率偏移估计,再把能量按这个偏移量重新分配。这个过程中有两个关键参数需要自己定:STFT的窗长,以及提取时的阈值。后面我会详细说怎么调。
2.2 中心频率和带宽在诊断里到底指什么
做故障诊断时,TET分析出来的“中心频率”不是随便一个频点,它对应的是故障冲击激励起的结构共振频率。以滚动轴承为例,外圈、内圈、滚动体故障会分别激起不同频段的共振,通常落在几千赫兹到几十千赫兹。这个共振频率是由轴承结构、安装方式和传感器位置决定的,和故障特征频率(BPFO、BPFI、BSF)是两回事。TET图上看到的亮带位置是共振频段,两条相邻亮带之间的时间间隔才是故障特征频率对应的周期。
在Matlab里,你会拿到一个尺寸为频率点数×时间点数的复数矩阵,取幅值后就是时频图。这个矩阵的纵轴频率范围依赖两个东西:采样率fs和STFT窗点数N。频率分辨率是fs/N,时间分辨率取决于窗的平移步长。这里有个硬约束:窗长越长,频率分辨率越好,但时间分辨率越差。短促的冲击信号需要好的时间定位能力,所以窗口不能太长,这是我做参数选型时第一个会纠结的点。
2.3 TET工具箱和自实现的取舍:参数怎么设才不出错
TET在Matlab里的实现目前有学术界公开的版本,做诊断方向的人应该也见过。但拿别人工具箱时有两个问题:一是输入输出格式不一定跟你的数据Pipeline兼容;二是工具箱里的默认参数往往是作者为了论文效果调的,换到你的数据上很容易翻车。我的习惯是:读懂核心函数,然后把提取那一步拆出来改写成自己能控制的代码。这样出问题时至少知道坑在哪一层。
TET最主要的参数有三个:
| 参数 | 作用 | 我的经验值 |
|---|---|---|
| STFT窗长 | 决定时频分辨率平衡 | 采样率的1/100~1/50,比如fs=12kHz时取256点 |
| 窗类型 | 影响旁瓣泄漏 | Hann窗,无特别理由不换 |
| 幅值阈值 | 抑制噪声和弱能量 | 归一化后取0.01~0.05,按信噪比调 |
窗长用256是常见做法,但如果你的故障冲击很短(比如齿面点蚀),我会降到128。阈值设大了会把弱冲击滤掉,设小了会留下噪声条纹。我一般先把信号归一化到零均值单位方差,再在0.01到0.05之间试,用肉眼观察时频图上的冲击轨迹是否连续来判断。
3. 一维振动信号转二维时频图:Matlab完整实现与预处理细节
3.1 从load到imwrite:一段能跑通的TET转换代码
本节给出我常用的TET转图核心流程,输入是一段振动信号,输出是一张可以直接进CNN的灰度图。
% TET一维转二维主流程 % 输入:x为振动信号(列向量),fs为采样率 % 输出:归一化后的时频灰度图 load('bearing_data.mat', 'x', 'fs'); % 加载单段信号 % ---- 预处理:去均值和去趋势 ---- x = x(:); % 强制列向量 x = x - mean(x); % 去直流 x = detrend(x); % 去线性趋势,消除转速漂移影响 % ---- STFT参数 ---- winLen = 256; % 窗长,频率分辨率 = fs/winLen nfft = winLen; % FFT点数,不小于窗长 noverlap = winLen - 4; % 重叠率很高,提升时间分辨率 win = hann(winLen, 'periodic'); % 周期Hann窗 [S, f, t] = spectrogram(x, win, noverlap, nfft, fs); % ---- TET核心:逐时刻沿频率方向提取瞬态能量 ---- Te = zeros(size(S)); % 初始化暂态提取矩阵 thresh = 0.02; % 幅值阈值,归一化后按信噪比调 % 以下是对每个时间切片做瞬时频率估计并提取能量 for k = 1:size(S, 2) spec = S(:, k); % 当前时刻的复数频谱 mag = abs(spec); % 幅值谱 % 沿频率方向做差分,找到局部峰值位置(瞬态冲击的特征) dMag = diff(mag); % 一阶差分 peakIdx = find(dMag(1:end-1) > 0 & dMag(2:end) < 0) + 1; % 对每个峰值位置,把附近指定带宽内的能量压缩到峰值点 for p = 1:length(peakIdx) idx = peakIdx(p); if mag(idx) > thresh * max(mag) % 只保留显著冲击 % 提取该峰值及其左右各2个频点的复数能量 lo = max(1, idx-2); hi = min(nfft, idx+2); Te(idx, k) = sum(spec(lo:hi)); % 能量压缩到中心频点 end end end % ---- 取幅值并归一化到图像范围 ---- TET_map = abs(Te); img = mat2gray(TET_map); % 归一化到[0,1] img = imresize(img, [224, 224], 'bilinear'); % 统一尺寸,适配CNN输入 % ---- 保存为PNG(不要用jpg,原因见第5章) ---- imgOut = uint8(img * 255); imwrite(imgOut, 'bearing_sample_001.png');这段代码的核心逻辑在TET提取那一段。它不是完整的TET工具箱实现,但保留了对瞬态信号最关键的处理:找频谱峰值、按峰值位置压缩能量。实际使用中,如果你下载到完整的TET函数,可以把它替换到上面代码里,输入输出格式不变,只把Te = TET_func(S, fs, thresh)这一行替换掉循环体即可。
需要特别注意:spectrogram返回的S是复数矩阵,TET提取时不能只取幅值,必须保留相位信息做复数能量叠加,否则提出来的时频图会丢失冲击的时间定位精度。
3.2 频率裁剪与图像增强:直接影响CNN收敛速度的两步
得到完整的TET时频图后,不要直接resize。振动信号里真正对故障诊断有用的频段通常是共振频段及其附近,低频转频分量和高频噪声反而是干扰项。我一般会在转图像前先把频率轴裁剪到关注区间。
% 频率裁剪:只保留关注频段 freqLow = 500; % 低端截止频率,滤除转频分量 freqHigh = 5000; % 高端截止频率,滤除高频噪声 idxLow = find(f >= freqLow, 1, 'first'); idxHigh = find(f <= freqHigh, 1, 'last'); TET_cropped = TET_map(idxLow:idxHigh, :); % 裁剪后再次归一化,避免低频大能量压扁冲击纹理 img = mat2gray(TET_cropped); img = imresize(img, [224, 224], 'bilinear');这一步有两个直接好处。第一,CNN输入图像里噪声纹理减少,模型收敛速度明显变快;第二,去掉了不同工况下差异很大的低频分量,模型泛化能力更强。我之前做一个多工况轴承诊断项目时,不加频率裁剪的模型在验证集上准确率只有82%,加上之后到了91%,原因就是不同转速下转频分量在时频图上的位置不同,模型误把转频位置当成了故障特征。
图像增强方面,我强烈建议在训练时对时频图做轻度随机扰动,提升CNN鲁棒性。常用的两个操作:随机水平平移几个像素(模拟冲击时刻的微小波动),以及小幅缩放(模拟转速波动导致的周期伸缩)。Matlab里可以用augmentedImageDatastore配合randomAffine2d实现,代码不多,但对泛化帮助很大。
3.3 批量转换与数据拆分:训练集测试集这样建才不会泄漏
实际项目里你不会只有一段信号。每个故障类别至少准备几十段信号,每段长度要覆盖至少10个以上冲击周期,这样TET图里才能看到清晰的周期纹理。单段信号越长越好,但要注意:转换完成后,同一个原始信号里不同片段生成的图,不能同时出现在训练集和测试集里,否则会数据泄漏——这正是后文坑列表里的第一条。
批量转换时先把文件清单整理好,再按照“先按原始样本分组,再整体拆分”的顺序做。常见做法是:每个故障类别的每段原始信号只生成1张时频图,然后按类别随机划分训练测试集,比例7:3。我一般还会单独留出两个完整工况的数据不参与训练,专门用来测泛化。
4. 把时频图喂给CNN:Matlab深度学习训练流程与效果验证
4.1 CNN结构怎么选:时频图像分类不需要ResNet那么深
TET时频图是灰度纹理图,结构相对简单,不需要ResNet这种深度网络。网络太大在小数据集上反而容易过拟合——这是血泪经验。我常用的结构是4层卷积的轻量CNN,对应Matlab的layerGraph组织方式如下:
% 构建轻量CNN,输入224x224灰度图 numClasses = 4; % 正常、外圈、内圈、滚动体 layers = [ imageInputLayer([224 224 1], 'Name', 'input') convolution2dLayer(3, 16, 'Padding', 'same', 'Name', 'conv1') batchNormalizationLayer('Name', 'bn1') reluLayer('Name', 'relu1') maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool1') convolution2dLayer(3, 32, 'Padding', 'same', 'Name', 'conv2') batchNormalizationLayer('Name', 'bn2') reluLayer('Name', 'relu2') maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool2') convolution2dLayer(3, 64, 'Padding', 'same', 'Name', 'conv3') batchNormalizationLayer('Name', 'bn3') reluLayer('Name', 'relu3') maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool3') fullyConnectedLayer(numClasses, 'Name', 'fc') softmaxLayer('Name', 'softmax') classificationLayer('Name', 'output') ]; % 设置训练参数 options = trainingOptions('sgdm', ... 'InitialLearnRate', 0.01, ... 'MiniBatchSize', 32, ... 'MaxEpochs', 15, ... 'Shuffle', 'every-epoch', ... 'ValidationData', imdsValidation, ... 'Plots', 'training-progress');关于网络结构,几个值得说的点:卷积核统一用3×3,两层卷积之间加BN层,池化在第三层后终止——再压下去特征图就太小了。全连接层只用一层,因为时频图的纹理特征经过三层卷积已经高度抽象,多加深层FC只增加过拟合风险。这个结构在轴承诊断场景下,GPU训练一轮大约几十秒到几分钟,普通CPU也能跑动,属于性价比比较高的选择。
4.2 数据读取的坑:imageDatastore自动处理标签但要注意文件名
训练数据准备好了,读取用Matlab的imageDatastore最常见——它会根据子文件夹名字自动生成标签。文件夹结构建议这样组织:
dataset/ train/ normal/ outer_race/ inner_race/ roller/ test/ normal/ outer_race/ inner_race/ roller/对应读取代码:
% 用imageDatastore读取训练和测试图像 imdsTrain = imageDatastore('dataset/train', ... 'IncludeSubfolders', true, ... 'LabelSource', 'foldernames'); imdsTest = imageDatastore('dataset/test', ... 'IncludeSubfolders', true, ... 'LabelSource', 'foldernames'); % 确认每个类别的样本数量一致 countEachLabel(imdsTrain)这里最容易踩的坑是样本不均衡。我见过有人转图时某个类别生成失败了一半,导致这个类别的训练样本只有其他类的三分之一,CNN训练时准确率震荡得很厉害。所以countEachLabel这一步不能省,发现不均衡先回去补样本,而不是硬着头皮训练。
如果你要做数据增强,在训练时用augmentedImageDatastore包一层:
% 随机水平平移不超过5像素,随机缩放0.95~1.05 aug = augmentedImageDatastore([224 224], imdsTrain, ... 'DataAugmentation', randomAffine2d(... 'XTranslation', [-5 5], ... 'YTranslation', [-5 5], ... 'Scale', [0.95 1.05]));注意augmentedImageDatastore不能直接做验证集的数据增强,验证集要保持原始图像,否则验证指标失真。
4.3 训练过程观察什么:准确率曲线之外还有三个信号
训练时不要只盯准确率。我一般同时观察三个东西:训练损失曲线是否平滑下降、验证准确率是否在某个epoch后开始回落、以及混淆矩阵在验证集上的分布。第一个信号判断学习率是否合适——如果损失曲线震荡剧烈,把学习率从0.01降到0.003;如果前两个epoch损失几乎不动,说明学习率偏低或者数据预处理有问题。
第二个信号关乎过拟合。时频图数据集通常不大(每类一两百张),15个epoch足够CNN记住训练集的所有图。如果验证准确率在第8到10个epoch之间开始下降而训练准确率还在涨,这就是过拟合信号,需要提前停止或加大正则化。Matlab里可以手动设置'ValidationPatience'参数控制早停,但我习惯自己观察曲线来调,因为自动早停往往停得太早。
第三个信号才是诊断模型真正的关键:看混淆矩阵里哪些类别互相混淆。轴承诊断里最常见的是内圈故障和外圈故障互相错分,因为两者的暂态冲击时频纹理相似度很高。如果出现这种混淆,不要急着换网络结构,先回去看这两类信号的TET图是否有肉眼可分的差异——如果肉眼都分不出来,那说明是信号处理参数的问题,不是网络的问题。
5. 这5个坑我全踩过:TET转图与深度学习诊断的常见问题排查
5.1 数据泄漏:训练准确率99%,测试准确率却只有70%
现象:训练曲线漂亮,验证集上第一轮就崩溃。原因几乎可以肯定是训练集和测试集里混入了来自同一段原始信号的片段。你把一段几十秒的振动信号切成几十个短段,每段生成一张时频图,然后随机分配训练测试——相邻片段高度相似,测试集里相当于混进了训练集的近亲样本,评估结果完全失真。
解决:按原始信号文件而非按片段来拆分数据集。一个信号文件的所有片段全部进训练集或全部进测试集,两者不交叉。代码上可以先对文件编号做splitpartition,再用文件名匹配回图像路径。这个坑隐蔽,翻车概率极高,而且一旦数据跑完很难后悔药可吃,一开始就要做对。
5.2 时频图上出现竖直条纹:冲击时刻被噪声吞噬了
现象:TET图上有贯穿整个频率轴的亮条纹,冲击轨迹反而不明显。原因通常是归一化过了头——用全局最大值归一化时,某一段强噪声(比如电磁干扰)占据了主导,弱冲击被压成了背景。
解决:先用分位数而不是最大值做归一化,用99.9%分位数作为归一化上限。如果条纹仍然明显,提高TET提取的阈值参数,从0.02往上调到0.05再试。注意观察时把原始时频图和提取后的图放一起对比,确认条纹不是源信号里真实存在的冲击。
5.3 换一个采样率,模型就不认了
现象:训练集12kHz采样率下效果不错,测试数据换成48kHz,诊断准确率崩盘。原因是TET频率图上的纹理缩放变了,CNN学到的纹理尺度对不上。
解决:统一所有数据到相同采样率。在转换前先resample到固定值,比如统一到12kHz或16kHz。顺便把频率裁剪区间也跟着定死,不要用百分比裁剪,直接用绝对频率值。
5.4 用jpg保存时频图,诊断精度掉了3到5个点
现象:同样的数据和流程,jpg格式的图比png掉点明显。原因是有损压缩在时频图里产生伪纹理,CNN把这些压缩噪声学进去了。
解决:一律用imwrite输出PNG格式。PNG文件体积大一些,但对灰度图像无损,不引入额外纹理。另外不要为了省空间把图缩到128×128以下,时频图里的冲击轨迹本来就细,分辨率损失直接对应诊断信息损失。
5.5 不同工况数据混训:模型学会了转速,没学会故障
现象:模型在你的数据集上准确率极高,放到现场新工况上直接失灵。原因是不同转速下冲击的时频纹理疏密不同,模型学到的是“转速纹理”而不是“故障纹理”。
解决:训练集和测试集必须按工况分开。常见的做法是拿A转速下全部数据训练,B转速测试;或者多工况各取一部分训练,再留一个完整工况做最终验证。如果模型在“留出工况”上表现不佳,说明TET提取或频率裁剪参数过拟合了特定转速,优先检查频率裁剪边界是否把不同转速下的共振频段都覆盖了。这个是诊断落地的核心难题,没有玄学,只有把工况维度拆开一遍遍验证。
6. 用混淆矩阵和t-SNE验证诊断模型:判断TET方案是否值得投入
6.1 先看混淆矩阵,再算F1分数
准确率是汇总指标,掩盖了细节。我习惯在Matlab里用confusionchart直接画出混淆矩阵,重点看两类:对角线是否干净,以及非对角线集中在哪些位置。轴承诊断的常见模式是内圈和外圈故障混淆,因为两者都是高频率冲击,纹理形态接近。这时候我回去对比两类信号的TET图,看冲击间隔和纹理宽度差异——如果图上区分度确实不够,那就是TET参数问题,调整窗长重新生成图,而不是加复杂的网络分支。
6.2 t-SNE可视化特征分布:看一眼就知道有没有学歪
Matlab里可以用activations把网络倒数第二层(全连接层)的输出抽出来,再用tsne降到二维散点图。我比较看重这个验证,因为特征分布的可视化能直接暴露两类问题:如果同类样本在散点图上聚成一堆、不同类之间分界清晰,说明TET转图质量合格;如果散点图上一团乱,先检查是不是数据泄漏导致的假象——泄漏会让散点图看起来完美,但换工况就废。
% 提取测试集在倒数第二层(全连接层)的特征 features = activations(net, imdsTest, 'fc', 'OutputAs', 'rows'); % 降到二维做可视化 Y = tsne(features, 'Perplexity', 30, 'NumDimensions', 2); gscatter(Y(:,1), Y(:,2), imdsTest.Labels);t-SNE的Perplexity建议设置在20到40之间,太小样本点会碎成很多小簇,太大不同类别的边界会被模糊。看散点图时我会确认每个类别都形成独立的大簇,而不是在某个方向上重叠。
6.3 Grad-CAM看模型到底关注时频图哪个区域
最后一步我强烈建议做:用gradCAM函数画出网络对某张时频图的热力图,看模型决策时关注的是冲击轨迹还是背景噪声。如果热力图高亮区域集中在冲击亮带附近,说明TET提取后的纹理确实被模型利用;如果高亮区域集中在背景区域,说明模型走了捷径——可能是数据泄漏,也可能是图像预处理引入的伪纹理在主导决策。
这一整套验证做完,你对“TET+深度学习”的方案是否值得投入就有把握了。我自己的经验是:如果TET图在肉眼层面能看出故障类别差异,那么这套方案落地价值很高,后续不断积累数据就能持续优化;如果肉眼都分不出来,那说明这个传感器位置或采样参数根本不适合做该故障的诊断,换方案比硬调网络更现实。最后说一句个人习惯:每调整一次参数,我会把旧的TET图和新的图并排保存下来做对比,三个月后回头看,那是一本比笔记本好用的参数调试记录。这个习惯帮我少走了很多弯路,希望帮到你。
本文还有配套的精品资源,点击获取