4. 故障特征提取:时域、频域与统计特征

各位同学,咱们接着往下聊。上一节我们把传感器数据采集上来了,但原始数据就像一堆没加工的矿石——你得提炼出金子才行。这节我就讲讲怎么从这些波形里「挖」出能代表故障的特征。

说白了,特征提取就是给数据做「体检报告」。你想想看,一个电机正常运行时和轴承卡死时,振动波形肯定不一样。但不一样在哪里?是幅度变了?还是频率变了?或者波形形状变得不对称了?这些就是我们要找的特征。

核心思路:把高维的原始信号,压缩成几个有物理意义的数值。这样故障诊断就从「看波形」变成了「看数字」。

4.1 时域特征:最直观的「第一印象」

时域特征,说白了就是直接在时间轴上算统计量。我刚开始做售货机故障诊断时,第一个看的指标就是时域特征——因为它计算快,实时性好。

4.1.1 均值(Mean)

均值反映信号的直流分量或趋势项。对于振动信号,均值通常接近0。如果均值明显偏移,说明传感器可能零漂了,或者机械结构有静态变形。

// 均值计算(C语言示例)
float calculate_mean(float *data, int len) {
    float sum = 0.0;
    for (int i = 0; i < len; i++) {
        sum += data[i];
    }
    return sum / len;
}

我的经验:在售货机出货口振动监测中,如果均值从0.02V漂到0.15V,大概率是传感器安装座松动了。我曾经遇到过一台机器,换了三次传感器都没解决,最后发现是安装螺丝滑丝了。

4.1.2 方差(Variance)与标准差

方差描述信号偏离均值的程度。说白了就是「抖得厉不厉害」。电机正常运行时方差较小,一旦轴承磨损,方差会明显增大。

// 方差计算
float calculate_variance(float *data, int len, float mean) {
    float var = 0.0;
    for (int i = 0; i < len; i++) {
        float diff = data[i] - mean;
        var += diff * diff;
    }
    return var / len;
}

标准差就是方差的平方根,单位跟原始信号一致,更直观。我一般用标准差做阈值判断——超过正常值的3倍,基本可以判定异常。

4.1.3 峰值(Peak Value)

峰值就是信号的最大绝对值。这个指标对冲击性故障特别敏感。比如售货机取货口被卡住时,电机堵转瞬间会产生很大的冲击峰值。

// 峰值计算
float calculate_peak(float *data, int len) {
    float peak = 0.0;
    for (int i = 0; i < len; i++) {
        float abs_val = fabs(data[i]);
        if (abs_val > peak) peak = abs_val;
    }
    return peak;
}

注意:峰值容易受噪声干扰。我建议先做一次低通滤波再算峰值,否则一个毛刺就能让阈值误触发。我曾经因为这个原因,让售货机在半夜频繁误报警,运维人员差点崩溃。

4.2 频域特征:看「节奏」而不是看「幅度」

时域特征只能告诉你「抖得厉害不厉害」,但频域特征能告诉你「怎么个抖法」。比如电机正常运行时,振动频率主要是基频和倍频。如果出现了非整数倍频的峰值,那基本就是故障信号。

4.2.1 FFT频谱分析

FFT(快速傅里叶变换)是把时域信号转换到频域的利器。我习惯用1024点FFT,采样率设为1000Hz,这样频率分辨率刚好1Hz左右,够用。

// FFT频谱分析(简化版,使用CMSIS-DSP库)
#include "arm_math.h"

float32_t input[1024];   // 时域数据
float32_t output[1024];  // FFT输出(复数)
arm_rfft_fast_instance_f32 fft_instance;

// 初始化FFT
arm_rfft_fast_init_f32(&fft_instance, 1024);

// 执行FFT
arm_rfft_fast_f32(&fft_instance, input, output, 0);

// 计算幅值谱
float32_t magnitude[512];
arm_cmplx_mag_f32(output, magnitude, 512);

得到频谱后,我一般关注三个频段:

  • 低频段(0-50Hz):反映机械结构松动、不平衡
  • 中频段(50-200Hz):反映轴承、齿轮故障
  • 高频段(200Hz以上):反映摩擦、气蚀等

避坑指南:我曾经在分析一台售货机压缩机振动时,发现频谱里有个明显的100Hz峰值。一开始以为是轴承故障,后来才发现是电源工频干扰。所以做FFT前,一定要先做去直流和去工频处理。

4.2.2 频谱特征提取

从频谱里提取特征,我常用的有:

特征名称 计算公式 物理意义
频谱重心 Σ(f·A(f)) / ΣA(f) 能量集中的频率位置
频谱均方根 √(ΣA(f)² / N) 频域能量大小
峰值频率 max(A(f))对应的f 主要振动频率

4.3 统计特征:看「形状」而不是看「大小」

时域和频域特征看完了,有时候还不够。比如两个信号的均值和方差都一样,但一个波形对称,一个不对称——这时候就需要统计特征出马了。

4.3.1 偏度(Skewness)

偏度描述信号分布的不对称程度。正常振动信号接近正态分布,偏度接近0。如果偏度为正,说明信号有向上的尖峰;为负则有向下的尖峰。

// 偏度计算
float calculate_skewness(float *data, int len, float mean, float std) {
    float skew = 0.0;
    for (int i = 0; i < len; i++) {
        float diff = (data[i] - mean) / std;
        skew += diff * diff * diff;
    }
    return skew / len;
}

我记得有一次,售货机出货电机的声音听起来「不对劲」,但时域和频域特征都在正常范围内。最后算偏度发现从0.1变成了0.8——原来是电机转子有轻微扫膛,产生了不对称的冲击。

4.3.2 峭度(Kurtosis)

峭度描述信号分布的「尖锐程度」。正态分布的峭度是3。如果峭度大于3,说明信号有更多的极端值——说白了就是有冲击。

// 峭度计算
float calculate_kurtosis(float *data, int len, float mean, float std) {
    float kurt = 0.0;
    for (int i = 0; i < len; i++) {
        float diff = (data[i] - mean) / std;
        kurt += diff * diff * diff * diff;
    }
    return kurt / len;
}

实用技巧:峭度对早期故障特别敏感。轴承刚开始出现点蚀时,峭度会先于均方根值发生变化。我一般用峭度做「预警指标」,均方根做「报警指标」。

4.4 特征融合:把鸡蛋放在一个篮子里

单个特征往往不够可靠。我习惯把时域、频域、统计特征组合成一个特征向量,然后送入分类器。比如对于售货机出货口故障,我常用的特征向量是:

// 特征向量结构体
typedef struct {
    float mean;        // 时域均值
    float variance;    // 时域方差
    float peak;        // 时域峰值
    float skewness;    // 偏度
    float kurtosis;    // 峭度
    float freq_peak;   // 频谱峰值频率
    float freq_energy; // 频谱能量
} FaultFeatureVector;

嗯,这里要注意一点:不同特征的量纲不一样。均值可能是0.01,峭度可能是5.0。直接放一起,峭度会主导结果。所以一定要做归一化处理。

我的习惯:用Z-score归一化,让每个特征的均值为0,标准差为1。这样所有特征都在同一个量级上,分类器才能正常工作。

好了,这一节的内容就到这里。特征提取是故障诊断的「眼睛」——你提取的特征好不好,直接决定了后续诊断的准确性。下一节我们聊聊怎么用这些特征做故障分类,到时候会用到一些机器学习的方法。


公众号:蓝海资料掘金营,微信deep3321