干设备状态监测这几年,我最大的感受是:真正的故障不会提前给你打报告,但一定会在振动信号里留下痕迹,关键是你有没有办法把痕迹找出来。前段时间接手了一套大功率液力变速器智能检测与诊断系统的开发任务,目标是给重载矿用自卸车、钻井平台这类大功率液力变速器做在线监测,把轴承磨损、齿轮点蚀、液力系统污染这类早期故障提前识别出来。整套系统从传感器选型开始,一路做到数据采集、信号预处理、特征提取、机器学习分类和现场部署,前后折腾了四个多月。这篇文章把项目里最有参考价值的部分整理出来,重点放在方案思路、故障机理对应关系、可以直接跑的Python代码,以及我现场踩过的坑。适合正在做旋转机械故障诊断、设备健康管理、预测性维护方向的朋友参考,刚入门的同学也能借此建立一个完整的技术框架。
1. 方案选型与整体设计思路
1.1 为什么盯上振动信号
大功率液力变速器的内部结构非常紧凑:液力变矩器、多挡行星齿轮、湿式离合器、液压阀体全部封装在同一个箱体里。它和普通减速机的最大区别,在于动力通过液力传动和机械传动混合传递,转速高、载荷冲击大,而且现场往往又是粉尘多、温度高的恶劣环境。这类设备的失效模式很典型:轴承疲劳剥落、齿轮齿面点蚀与断齿、离合器摩擦片烧蚀、油液污染导致阀芯卡滞、变矩器导轮叶片磨损等等,每一种故障都对应着不同的信号特征。
针对这些失效模式,可选的状态量有振动、油液温度、油液压力、转速、油液金属磨粒等。综合考虑传感器成本、安装可行性和诊断灵敏度之后,我把振动信号作为第一优先级。轴承和齿轮的局部损伤会产生周期性的冲击脉冲,这些脉冲会激励起箱体结构的高频共振,在振动信号里表现为带调制特征的边带;液力系统轻微泄漏和阀芯状态异常,也会在壳体表面留下由压力脉动引起的振动特征。再加上油温作为辅助通道用来判断散热和润滑状态,转速作为工况参考量,这套组合基本能覆盖大多数早期故障。
1.2 系统架构与阈值报警的取舍
整个系统架构我分成四层:感知层用加速度计、温度传感器、转速传感器把物理量变成电信号;采集层用数据采集仪或边缘工控机保证多通道同步采样和抗混叠滤波;分析层负责信号预处理、特征提取、故障模式识别,这是软件的核心;应用层输出报警、诊断结论、维护工单和趋势报表。
架构定下来之后,我在报警策略上纠结过一阵:是只做纯阈值报警,还是上模式识别模型。纯阈值报警就是监测振动总值的趋势,超过设定限值就报警,实现简单,但对早期轻微磨损几乎没有提前量。我对比过同一组劣化数据,振动总值还在缓慢爬升的时候,包络谱里的故障谐波已经很明显了。所以最终采用了两者结合:趋势阈值负责兜底,捕捉突发性严重故障;机器学习模型负责指认故障类型和严重程度,给维修人员提供判断依据。
1.3 先做随机森林,不急着上深度学习
现在很多帖子一上来就推CNN、LSTM,但我的习惯是先做简单模型。原因有三个。第一,工业现场故障样本本来就少,正样本数量往往只有正常样本的百分之几,深度模型在这种数据规模下很容易过拟合;第二,现场工程师需要能解释的判断依据,随机森林天然能输出特征重要性,告诉你“是哪个频段的能量上升导致了这个结论”,而深度学习模型的可解释性成本要高得多;第三,边缘部署算力有限,随机森林模型只有几十KB到几百KB,推理时间在毫秒级,一台工控机同时处理十几个通道毫无压力。等到数据积累到一定规模、特征工程确实不够用的时候,再上深度模型不迟。
2. 数据采集与信号预处理
2.1 传感器选型与测点布置
这一节我认为是整个项目里最容易踩坑的地方。振动测点我选了IEPE型压电加速度计,灵敏度50 mV/g,量程±50 g,频率响应范围0.5 Hz到10 kHz。选这个灵敏度是有讲究的:变速器壳体的振动既有低频大冲击,又有轻微的高频磨损信号,灵敏度太低容易削波,太高则在小信号频段容易被噪声淹没。
测点布置的原则是“靠近轴承承载区,路径越短越好”。我把加速度计布置在输入轴轴承座正上方和输出轴法兰水平方向的箱体加强筋上,这两个位置到振动源的结构路径最短,信号衰减小。实际对比测试过,布置在箱体平板正中央的测点,高频分量衰减比靠近轴承座的位置明显更大,故障特征频率经常被淹没在噪声里。传感器固定方式优先用螺装或者磁座,尽量不要用胶粘——胶层厚度会改变高频响应,导致特征频率幅值失真。油温传感器放在油底壳回油管路处,测的是实际参与循环的油液温度,而不是壳体表面温度,两者能差出十几度。
2.2 采样率与工况控制
采样率我设置为25.6 kHz,分析带宽取到5 kHz。大功率变速器最主要的齿轮啮合频率通常在2 kHz到3 kHz区间,轴承故障激发的结构共振频段集中在3 kHz到8 kHz,采样率要覆盖到分析带宽的10倍以上。每个样本记录10秒稳定工况数据,这样频谱分辨率能到0.1 Hz以下,又不会产生过大的数据量,一个样本就是25.6万个点,做一次FFT和特征提取非常快。
这里有一个现场经常被忽略的问题:变转速工况下的频谱模糊。发动机转速波动时,齿轮啮合频率会跟着漂移,直接把时域信号做FFT,特征峰会变成一片模糊的宽带,故障边带根本看不清。针对这个问题,要么要求司机保持稳定油门匀速运行一段时间再采集,要么引入转速计做阶比跟踪,把角度域信号重采样。项目里我两种方式都试过,工程现场优先选第一种,因为省掉一个转速通道的成本,而且操作上更容易和司机沟通。
2.3 预处理流程与零相位滤波
预处理我定义了固定流程:去直流、零相位带通滤波、剔除异常段、归一化。去直流是为了消除传感器温漂带来的零位偏移;带通滤波采用500 Hz到5 kHz,既滤掉低频大冲击分量,又保留轴承故障调制的高频段。滤波用filtfilt而不是lfilter,前者是零相位偏移,不会把特征峰的位置移偏;归一化用z-score,统一不同测点和不同工况下的幅值水平。
注意:零相位滤波会引入“未来数据”参与计算,因此只适合离线分析和事后诊断。如果要做实时的在线报警,需要用因果滤波器并补偿相位延迟,或者用滑动窗分段处理的方式。
预处理代码很简单,核心就几行:
import numpy as np from scipy import signal def preprocess(raw_ts, fs=25600.0): # 去直流 ts = raw_ts - np.mean(raw_ts) # 零相位带通滤波,保留轴承故障调制所在的高频段 b, a = signal.butter(4, [500.0/(fs/2), 5000.0/(fs/2)], btype='band') ts = signal.filtfilt(b, a, ts) # 剔除冲击异常段:超过6倍RMS的点直接置为RMS值 rms = np.sqrt(np.mean(ts**2)) ts[np.abs(ts) > 6*rms] = rms # z-score归一化 return (ts - np.mean(ts)) / (np.std(ts) + 1e-10)3. 特征工程与故障机理对应
3.1 时域统计特征
时域特征是最直观的一类,计算量小,物理意义明确。我保留了五个核心指标:均方根值反映振动能量总体水平,峰值反映最大冲击强度,峰值因子用于衡量波形中是否存在明显的冲击成分,峭度对早期局部损伤特别敏感,偏度则能反映波形不对称性。举个例子,轴承外圈出现一个微小的剥落坑时,振动总能量可能只上升了不到10%,但峭度往往能翻一倍,这个灵敏度是纯阈值报警做不到的。
| 特征 | 计算公式 | 物理含义 |
|---|---|---|
| RMS | sqrt(mean(x²)) | 振动能量水平,趋势监控主力 |
| 峰值 | max(abs(x)) | 瞬态冲击强度 |
| 峰值因子 | 峰值 / RMS | 冲击性成分占比 |
| 峭度 | E[(x-μ)⁴] / σ⁴ | 冲击脉冲的陡峭程度,早期损伤敏感 |
| 偏度 | E[(x-μ)³] / σ³ | 波形上下不对称程度 |
3.2 频域特征与特征频率计算
频域特征要和具体故障机理对应起来才有意义。齿轮类故障的核心是啮合频率及其边带:齿轮啮合频率GMF等于齿数乘以轴转频,当齿面出现点蚀或断齿时,啮合频率两侧会出现以轴频为间隔的边带,边带能量越高,故障越明显。轴承类故障则要看特征频率:外圈故障频率BPFO、内圈故障频率BPFI、滚动体故障频率BSF、保持架故障频率FTF,都可以用几何参数算出来。
这些理论特征频率一定要在设计阶段就算好,写进配置表里。现场看到频谱峰的位置和理论值对上了,故障定位就完成了一大半。我在项目里把主要轴承样本的型号参数输进去,算出了每一挡转速下的特征频率表,实测包络谱里的峰和理论值偏差基本都在1%以内——因为这个偏差,我现场排除过好几次“疑似故障”,其实是转速没对齐导致的谐波误读。
3.3 包络解调与数据标注
包络解调是轴承故障诊断的杀手锏。原理不复杂:轴承早期损伤产生的冲击会激励起箱体高频共振,这个信号像是一个高频载波被低频故障频率调制。用希尔伯特变换求信号的包络,再对包络做FFT,就能在包络谱里看到清晰的故障特征频率。我把包络RMS和包络谱峰值作为特征加入模型之后,轴承类故障的识别准确率提升非常明显。
数据标注是最花时间也最不能糊弄的环节。故障样本必须来自经过拆解确认的设备,而不是“听起来有点不对劲”就草率标记。项目里的样本分为四类:正常、齿轮磨损、轴承外圈故障、油液污染,每一条样本都保留了原始波形、工况记录和事后拆解照片,方便复核。数据不平衡问题在工业场景里几乎必然出现,我的处理方式是模型里设置class_weight='balanced',同时在划分训练测试集时用stratify保证各类别比例一致。
4. 智能诊断模型实现(附完整代码)
4.1 环境准备
代码基于Python 3.9,依赖库用pip安装即可。核心就四个:numpy做数组运算,scipy做滤波和希尔伯特变换,scikit-learn做分类模型和评估,pandas做特征表格管理。如果有可视化需求,再加上matplotlib。
pip install numpy scipy scikit-learn pandas matplotlib4.2 完整特征提取代码
特征提取模块把时域特征、频段能量占比、包络特征统一封装成一个函数,输入原始振动时域数组,输出特征字典。每个样本最终变成一条向量,长度不长,但信息密度足够。
import numpy as np from scipy import signal from scipy.stats import kurtosis, skew def envelope(ts): return np.abs(signal.hilbert(ts)) def extract_features(ts, fs=25600.0): feats = {} # ---------- 时域特征 ---------- rms = np.sqrt(np.mean(ts**2)) peak = np.max(np.abs(ts)) feats['rms'] = rms feats['peak'] = peak feats['crest_factor'] = peak / (rms + 1e-10) feats['kurtosis'] = kurtosis(ts) feats['skewness'] = skew(ts) # ---------- 频段能量占比 ---------- n = len(ts) freqs = np.fft.rfftfreq(n, 1.0/fs) spec = np.abs(np.fft.rfft(ts)) total_energy = np.sum(spec**2) + 1e-10 bands = [(0, 500), (500, 1500), (1500, 3000), (3000, 5000)] for i, (fl, fh) in enumerate(bands): mask = (freqs >= fl) & (freqs < fh) feats[f'band_{i}_ratio'] = np.sum(spec[mask]**2) / total_energy # ---------- 包络谱特征 ---------- env = envelope(ts) feats['env_rms'] = np.sqrt(np.mean(env**2)) env_spec = np.abs(np.fft.rfft(env)) # 记录包络谱在常见轴承故障频段(0~500Hz)的峰值 mask_low = freqs[:len(env_spec)] < 500.0 feats['env_peak_low'] = np.max(env_spec[mask_low]) if np.any(mask_low) else 0.0 return feats4.3 批量构建数据集与模型训练
假设数据目录按故障类别组织,每个类别文件夹下存放预处理后的npy格式振动波形。下面这段代码批量读取数据、提取特征、构建训练集和测试集,然后训练随机森林分类器并输出评估报告。
import glob import numpy as np import pandas as pd from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report, confusion_matrix LABEL_MAP = {'normal': 0, 'gear_wear': 1, 'bearing_outer': 2, 'oil_contamination': 3} FS = 25600.0 def build_dataset(data_dir): rows, labels = [], [] for label_name, code in LABEL_MAP.items(): for file_path in sorted(glob.glob(f'{data_dir}/{label_name}/*.npy')): raw = np.load(file_path) ts = preprocess(raw, FS) # 使用 2.3 节的预处理函数 feats = extract_features(ts, FS) rows.append(feats) labels.append(code) df = pd.DataFrame(rows) return df, np.array(labels) X_df, y = build_dataset('./data') feature_names = X_df.columns.tolist() X = X_df.values # 分层划分,保证训练集和测试集中各类别比例一致 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, stratify=y, random_state=42 ) # class_weight='balanced' 缓解故障样本偏少的问题 clf = RandomForestClassifier(n_estimators=300, max_depth=12, class_weight='balanced', random_state=42) clf.fit(X_train, y_train) y_pred = clf.predict(X_test) print(classification_report(y_test, y_pred, target_names=LABEL_MAP.keys())) print(confusion_matrix(y_test, y_pred))随机森林的两个核心参数我解释一下。n_estimators设为300,是因为工业样本总数不大,树太少不稳定,树太多又带来计算浪费,300在这个数据规模下已经足够收敛;max_depth设为12,是为了控制单棵树的复杂度,防止模型死记硬背训练集中的噪声。这两个值不是拍脑袋定的,我用交叉验证扫过一轮,深度超过15之后测试集准确率不再上升反而略有下降,典型的过拟合信号。
4.4 特征重要性与物理一致性验证
模型训练完别急着部署,先看一眼特征重要性,这一步相当于给模型做物理一致性体检。
importance = pd.Series(clf.feature_importances_, index=feature_names).sort_values(ascending=False) print(importance.head(10))在模拟项目X的试验台数据上,输出结果和机理分析基本对得上:gear_wear样本里频段能量占比和啮合频率边带能量排在前面,bearing_outer样本里包络RMS、包络低频峰值和峭度指标最靠前。如果特征重要性跟机理预期完全对不上,问题多半出在数据标注或者传感器通道接错,这时候要回头查标签、查接线,而不是急着调模型参数。
4.5 趋势阈值兜底逻辑
分类模型给出的是故障类型判断,但现场还需要一个“恶化速度”维度的报警。我保留了独立的趋势报警逻辑:用滑动窗口统计RMS序列的斜率,当斜率连续多个窗口超过阈值时触发预警,不管分类模型输出了什么结果。
def trend_alert(rms_history, window=30, slope_threshold=0.05): if len(rms_history) < window: return False arr = np.array(rms_history[-window:]) slope = np.polyfit(np.arange(len(arr)), arr, 1)[0] relative_slope = slope / (np.mean(arr) + 1e-10) return relative_slope > slope_threshold这么设计的原因很直接:分类模型擅长回答“这是什么故障”,趋势报警擅长回答“这故障在加速还是平稳”。两者输出合并之后,维修人员拿到的是一条可执行的信息——比如“输出轴轴承外圈故障特征明显,包络RMS连续上升,建议两周内安排更换”。
5. 现场部署与常见问题排查
5.1 从离线到在线的几个大坑
离线验证效果不错,不代表现场就能直接跑。我遇到的第一道坎是跨设备泛化。在试验台A上训练出来的模型,部署到另一台同型号设备B上,一开始误报率明显偏高。原因不复杂:每台设备装配公差不同、基础振动水平不同,特征分布的均值有偏移。解决办法是保留模型结构不变,用新设备上一段时间的正常数据重新校准特征阈值,而不是直接推翻模型重训。
第二道坎是报警抖动。分类模型对单条样本的判断偶尔会跳变,明明上一秒正常,下一秒报故障,过几秒又恢复。后来我改成连续表决机制:连续5个窗口中有4个判定为同一故障才触发报警,抖动问题基本消失。代价是报警延迟了几秒,对于轴承齿轮这类缓慢劣化故障来说,这点延迟完全可以接受。
第三道坎是推理耗时。一开始我在预处理和特征提取里写了不少Python循环,单条样本处理要几百毫秒,多通道并发时就扛不住了。改成numpy向量化之后,单条样本降到几十毫秒。工业现场部署不是比拼算法多先进,而是在有限算力下稳定产出可用的结果。
5.2 典型问题速查表
把现场调试阶段遇到的问题整理成一张速查表,后面再遇到类似情况可以直接对照排查。
| 现象 | 可能的根因 | 处理方式 |
|---|---|---|
| 频谱中出现大量宽带噪声 | 传感器固定松动或磁座接触不良 | 检查安装扭矩,优先改用螺装 |
| 特征频率峰值位置偏移 | 转速波动导致频谱模糊 | 增加阶比跟踪,或限制稳定工况采集 |
| 模型误报频繁 | 工况变化导致特征分布漂移 | 记录工况标签,按工况分组建模 |
| 故障样本极少,分类器偏向正常类 | 数据不平衡 | 启用class_weight,必要时做过采样 |
| 在线推理速度慢 | 特征提取存在低效循环 | 用numpy向量化,降低采样长度 |
| 包络谱无特征峰 | 滤波带宽没覆盖共振频段 | 扫频确定共振带,调整带通范围 |
5.3 折腾完这套系统的一些心得
最后说点个人体会。这套系统上线之后的头一个月,误报是最多的,但也是价值最大的——每一次误报都在帮你暴露数据质量问题或者工况标签缺失,认真复盘之后,模型准确率会明显上升。我踩过最重的坑是初期迷信先进算法,花了两周调深度学习模型,效果反而不如后来精心设计特征加随机森林的组合。工业诊断场景里,数据和机理永远排在模型前面。
还有一个小技巧:保存原始波形的时候,把工况、负荷、油温三个字段直接写进文件名,后续做故障归因时能少走很多弯路。诊断说白了是在跟不确定性打交道,先把数据质量和故障机理这两件事做扎实,模型反而是整个链路里最省心的那一环。