4. 特征工程(上):时域特征提取与频域特征提取
各位同学,欢迎来到特征工程环节。说实话,很多做寿命预测的朋友,模型选得再好,数据预处理做得再漂亮,如果特征没提好,那基本就是白忙活。我见过太多人,上来就扔一堆原始数据给模型,结果模型学了半天,学到的全是噪声。
特征工程,说白了就是帮模型“划重点”。今天我们先聊时域和频域这两大块。这是最基础、最常用的特征提取手段,也是我入行时最先啃透的部分。
核心观点:时域特征反映信号随时间变化的统计规律,频域特征揭示信号在不同频率上的能量分布。两者结合,才能全面刻画设备的退化过程。
一、时域特征提取:从波形里“抠”信息
时域特征,就是直接从原始信号的时间序列里算出来的统计量。你想想看,一个轴承从健康到故障,它的振动波形会怎么变?振幅会变大,波形会变得“毛糙”,偶尔还会出现很大的冲击。这些变化,都能用一些数值指标捕捉到。
1. 均值与方差
均值反映信号的直流分量或整体趋势。对于旋转机械,如果均值发生漂移,往往意味着传感器出了问题,或者设备发生了结构性变化。
方差反映信号的波动程度。设备磨损越严重,振动幅度通常越大,方差自然就上去了。
import numpy as np
def extract_time_features(signal):
"""
提取时域特征
signal: 一维数组,振动信号
"""
mean_val = np.mean(signal) # 均值
var_val = np.var(signal) # 方差
rms_val = np.sqrt(np.mean(signal**2)) # 均方根值
return mean_val, var_val, rms_val
# 举个栗子
vibration_data = np.random.randn(1000) * 0.5 + 10 # 模拟振动信号
mean, var, rms = extract_time_features(vibration_data)
print(f"均值: {mean:.3f}, 方差: {var:.3f}, 均方根: {rms:.3f}")
我的经验:均方根值(RMS)在工程中比方差更常用。为什么?因为RMS的量纲和原始信号一致,物理意义更直观。我在做风电齿轮箱监测时,RMS值从0.5慢慢涨到1.2,我就知道该安排检修了。
2. 峰值与峭度
峰值就是信号的最大绝对值。这个指标对早期故障特别敏感——设备刚开始出现裂纹时,偶尔会蹦出一个很大的冲击,峰值一下子就上去了。
峭度(Kurtosis)是个很有意思的指标。它衡量信号分布的“尖锐程度”。健康设备的振动信号接近正态分布,峭度值在3左右。一旦出现故障,信号中会出现很多“尖刺”,峭度值就会飙升。
def kurtosis(signal):
"""
计算峭度
"""
n = len(signal)
mean = np.mean(signal)
fourth_moment = np.sum((signal - mean)**4) / n
variance = np.var(signal)
# 峭度 = 四阶中心矩 / 方差^2
kurt = fourth_moment / (variance**2)
return kurt
# 模拟一个带冲击的信号
healthy_signal = np.random.randn(1000) # 健康信号,峭度≈3
faulty_signal = np.copy(healthy_signal)
faulty_signal[::50] += 5 # 每50个点加一个冲击
print(f"健康信号峭度: {kurtosis(healthy_signal):.2f}")
print(f"故障信号峭度: {kurtosis(faulty_signal):.2f}")
避坑指南:我曾经在一个项目中,只靠峭度做故障判断,结果误报率很高。后来发现,设备启停阶段也会产生冲击,峭度值同样会飙升。所以,峭度要结合其他特征一起看,别单打独斗。
3. 其他常用时域特征
除了上面这些,还有几个指标我经常用:
| 特征名称 | 计算公式 | 物理含义 |
|---|---|---|
| 波形因子 | RMS / 整流平均值 | 反映波形形状,故障时通常增大 |
| 峰值因子 | 峰值 / RMS | 检测冲击性故障,早期故障敏感 |
| 脉冲因子 | 峰值 / 整流平均值 | 类似峰值因子,但更稳定 |
| 裕度因子 | 峰值 / 方根幅值 | 对磨损类故障敏感 |
二、频域特征提取:换个角度看信号
时域特征虽然直观,但有个问题——它把不同频率成分混在一起了。你想想看,一个轴承故障,它的特征频率可能是几百赫兹;而齿轮啮合频率可能是几千赫兹。时域上根本分不清。这时候,就得请出频域分析这个神器了。
1. FFT(快速傅里叶变换)
FFT是频域分析的基石。它把时域信号分解成不同频率的正弦波分量。说白了,就是告诉你“信号里都有哪些频率的成分,每个成分有多强”。
import numpy as np
import matplotlib.pyplot as plt
def compute_fft(signal, sampling_rate):
"""
计算FFT并返回频率和幅值
"""
n = len(signal)
fft_vals = np.fft.fft(signal)
fft_abs = np.abs(fft_vals[:n//2]) # 取正频率部分
freqs = np.fft.fftfreq(n, 1/sampling_rate)[:n//2]
return freqs, fft_abs
# 模拟一个包含50Hz和120Hz成分的信号
fs = 1000 # 采样率1000Hz
t = np.arange(0, 1, 1/fs)
signal = 2 * np.sin(2*np.pi*50*t) + 1.5 * np.sin(2*np.pi*120*t)
freqs, magnitude = compute_fft(signal, fs)
# 找到幅值最大的前3个频率
top_indices = np.argsort(magnitude)[-3:][::-1]
print("主要频率成分:")
for idx in top_indices:
print(f" {freqs[idx]:.1f} Hz, 幅值: {magnitude[idx]:.2f}")
我的习惯:做FFT之前,一定要先做去趋势和加窗处理。去趋势是为了消除直流分量,加窗(比如汉宁窗)是为了减少频谱泄漏。我刚开始做时没注意这些,频谱图上一堆莫名其妙的旁瓣,排查了半天才发现是没加窗。
2. 功率谱密度(PSD)
PSD比FFT更进一步。它描述的是信号功率在频率上的分布。对于随机振动信号,PSD比FFT的幅值谱更稳定、更有统计意义。
常用的PSD估计方法有:
- 周期图法:直接对信号做FFT然后取平方。简单,但方差大。
- Welch法:把信号分段,每段加窗,然后平均。方差小,是我最推荐的方法。
- 自相关法:先算自相关函数,再做FFT。适合短数据。
from scipy import signal as scipy_signal
def compute_psd_welch(signal, fs, nperseg=256):
"""
使用Welch法计算功率谱密度
"""
freqs, psd = scipy_signal.welch(
signal,
fs=fs,
nperseg=nperseg,
window='hann'
)
return freqs, psd
# 计算PSD
freqs_psd, psd_vals = compute_psd_welch(signal, fs)
# 提取频域特征
# 1. 频谱峰值(最大功率对应的频率)
peak_freq = freqs_psd[np.argmax(psd_vals)]
# 2. 重心频率(功率加权平均频率)
centroid = np.sum(freqs_psd * psd_vals) / np.sum(psd_vals)
# 3. 频带能量(比如0-200Hz的能量)
band_energy = np.trapz(psd_vals[(freqs_psd >= 0) & (freqs_psd <= 200)],
freqs_psd[(freqs_psd >= 0) & (freqs_psd <= 200)])
print(f"峰值频率: {peak_freq:.1f} Hz")
print(f"重心频率: {centroid:.1f} Hz")
print(f"0-200Hz频带能量: {band_energy:.3f}")
3. 频域特征的实际应用
在实际项目中,我通常会提取以下频域特征:
- 频谱峰值及其对应频率:直接反映主要振动源的频率。比如轴承外圈故障频率。
- 重心频率:反映能量集中的频段。设备退化时,重心频率往往会向高频移动。
- 频率方差:反映频谱的分散程度。故障越严重,频谱越“散”。
- 特定频带能量:比如提取1倍频、2倍频、3倍频附近的能量。这对齿轮箱故障诊断特别有用。
一个真实案例:我之前做离心泵的寿命预测。泵的转速是3000RPM(50Hz)。健康状态下,频谱上50Hz及其谐波占主导。随着叶轮磨损,50Hz附近的能量逐渐下降,而高频段(200-500Hz)的能量开始上升。我提取了“高频能量比”这个特征,用它训练的模型,提前3天预测到了故障。
三、时域与频域特征的融合策略
说了这么多,你可能要问:到底该用哪些特征?我的建议是:
- 初期探索:把所有时域和频域特征都算出来,做个相关性分析,剔除高度相关的特征。
- 特征筛选:用随机森林或XGBoost的特征重要性,选出Top 10-20个特征。
- 最终组合:保留5-8个时域特征 + 5-8个频域特征,构成最终的特征集。
记住,特征不是越多越好。我见过有人提取了100多个特征,结果模型过拟合得一塌糊涂。少而精,才是王道。