怎么用Python实现振动信号的包络谱故障诊断

陆遥
陆遥
发布时间:2026-11-09 | 8人浏览过
原创

本文详细讲解如何使用Python实现旋转机械振动信号的包络谱故障诊断。从局部冲击调制机理、特征频率计算、Butterworth带通滤波、Hilbert变换解调到包络谱绘制与故障定位,提供完整可运行代码与避坑指南。

机械故障诊断 • 信号解调算法

在电机或齿轮箱的振动监测中,轴承初期剥落或微小点蚀产生的撞击能量极微弱,直接对传感器采集到的振动加速度信号做快速傅里叶变换(FFT),故障特征往往完全被大功率的不平衡工频与背景噪底淹没。通过“共振带通滤波 + 希尔伯特(Hilbert)变换解调”提取信号幅值包络,再对其进行频域分析,是穿透干扰、精确提取故障特征频率的核心手段。本文将通过通俗的物理机制推导、完整的 Python(NumPy / SciPy)代码实现以及工程避坑排查,带你一步步掌握振动信号包络谱分析的全流程。

怎么用Python实现振动信号的包络谱故障诊断
图 0:Python 振动信号包络谱故障诊断概览

一、为什么直接 FFT 难以发现轴承早期损伤?

设想这样一种典型工况:一台转速为 1800 rpm(转频 30 Hz)的工业离心风机,其滚动轴承外圈出现了一个微小的点蚀斑点。每当滚动体碾过该缺陷点时,都会产生一次持续时间仅微秒级的机械瞬态撞击。

这种冲击在频域上具有宽带激励特性,会激发出传感器与轴承支撑结构的高频固有共振(通常在几 kHz 甚至十几 kHz 范围内)。因此,传感器拾取到的信号本质上是一个受到周期性冲击调制的衰减高频振荡波。如果直接对这段原始时域波形做快速傅里叶变换(FFT),常常会遇到两个致命困难:

  • 低频段工频能量遮蔽:电机的动不平衡、轴对中不良、偏心等宏观几何运动会产生极大的低频工频振动(1X、2X 转频),其能量常常比早期点蚀微弱冲击高出数十甚至数百倍,直接把低频处的轴承故障特征谱线彻底淹没。
  • 高频共振带能量分散:虽然冲击激发的共振能量集中在高频带,但它表现为以固有共振频率为载波中心、以故障特征频率为间隔的密集边频带。在强烈背景白噪声干扰下,各条边频微弱且信噪比低,根本无法肉眼辨识或阈值告警。

包络谱分析的破局思路在于“解调”:既然故障信息调制在高频共振载波的外轮廓(即幅值包络)中,我们首先使用带通滤波器扣出共振频段,再通过数学变换剥离掉高频振荡载波,仅保留随时间周期性涨落的“外包络线”。对这条纯净的低频包络做 FFT,原本隐藏在载波内部的故障特征频率及其倍频就会以高幅值谱峰的形式干净利落地显现出来。

怎么用Python实现振动信号的包络谱故障诊断
图 1:常规直接 FFT 频谱与带通包络解调谱的机理对比

二、轴承四类理论故障特征频率的计算基准

在执行 Python 解调之前,必须先根据轴承几何参数与工作转速,计算出构件出现点蚀损伤时的理论撞击频率基准。滚动轴承的核心几何参数包括:

  • fr:转轴旋转频率(Hz),若转速为 N rpm,则 fr = N / 60;
  • Z:单列滚动体(滚珠或滚子)数量;
  • d:滚动体平均直径(mm);
  • D:轴承滚道节径(Pitch Diameter,mm);
  • alpha:接触角(度数,向心推力轴承取非零角度,深沟球轴承纯径向取 0°)。
外圈故障特征频率 (BPFO) BPFO = (Z · fr / 2) · [1 - (d / D) · cos(α)]
内圈故障特征频率 (BPFI) BPFI = (Z · fr / 2) · [1 + (d / D) · cos(α)]
滚动体自转故障频率 (BSF) BSF = (D · fr / (2d)) · [1 - ((d / D) · cos(α))²]
保持架公转故障频率 (FTF) FTF = (fr / 2) · [1 - (d / D) · cos(α)]

在 Python 中,可以通过一个轻量函数快速封装上述计算公式:

import numpy as np

def calculate_bearing_frequencies(fr, Z, d, D, alpha_deg=0.0):
    alpha = np.radians(alpha_deg)
    gamma = (d / D) * np.cos(alpha)
    
    bpfo = (Z * fr / 2.0) * (1.0 - gamma)
    bpfi = (Z * fr / 2.0) * (1.0 + gamma)
    bsf  = (D * fr / (2.0 * d)) * (1.0 - gamma**2)
    ftf  = (fr / 2.0) * (1.0 - gamma)
    
    return {"BPFO": bpfo, "BPFI": bpfi, "BSF": bsf, "FTF": ftf}

# 示例:转速 1800 rpm (fr = 30 Hz), 滚珠数 Z = 9, 滚珠直径 d = 7.94 mm, 节径 D = 39.04 mm
freqs = calculate_bearing_frequencies(fr=30.0, Z=9, d=7.94, D=39.04)
# freqs['BPFO'] 约等于 107.5 Hz
怎么用Python实现振动信号的包络谱故障诊断
图 2:Python 振动信号包络谱故障诊断实施管线

三、包络解调的核心数学实现:Hilbert 变换

包络提取的核心技术是利用希尔伯特变换(Hilbert Transform)构建解析信号(Analytic Signal)。

对于一个经过带通滤波提取出的带通冲击信号 x(t),其希尔伯特变换定义为 x(t) 与 1/(πt) 的卷积积分:

希尔伯特变换正交移相 x̂(t) = H[x(t)] = (1 / π) · ∫ [x(τ) / (t - τ)] dτ
构造复解析信号 z(t) = x(t) + j · x̂(t) = A(t) · e^{j · φ(t)}
瞬时幅值包络模长 A(t) = |z(t)| = √[x²(t) + x̂²(t)]

希尔伯特变换在频域上的物理意义非常直观:它将输入信号的正频分量全部顺时针旋转 90°(乘以 -j),将负频分量全部逆时针旋转 90°(乘以 +j),而保持幅频特性不变。将原实信号作为实部,希尔伯特变换后的正交信号作为虚部构造成复数信号 z(t),就构成了单边谱解析信号。此时对 z(t) 求模值 |z(t)|,便完美抽离出了调制载波上的瞬时包络曲线 A(t)。

怎么用Python实现振动信号的包络谱故障诊断
图 3:希尔伯特变换与解析信号解调数学处理流程
⚠️ 最容易忽略的关键细节:包络信号必须去直流

瞬时幅值包络 A(t) = |z(t)| 是复数的模,因此它的数值恒大于或等于 0。这意味包络信号具有极大的正向直流偏置(DC Offset)。如果直接对 A(t) 进行 FFT,频谱在 0 Hz 处会出现一个能量惊人的超高直流尖峰,严重压缩 Y 轴显示动态范围,甚至把微弱的轴承故障特征谱峰压成一条直线。

正确做法:在送入 FFT 之前,务必执行去除均值操作:env_ac = env - np.mean(env)。

四、完整的 Python 实战代码与仿真验证

为了让每一位读者无需外接工业传感器就能直接调试并验证算法,下面的 Python 脚本构造了一个贴合真实工况的轴承外圈点蚀仿真信号:包含旋转工频低频杂散、周期性外圈冲击(BPFO = 105.0 Hz)、结构共振高频响应(中心频率 3000 Hz、阻尼指数衰减)以及高斯背景白噪声。

环境依赖仅需 numpy、scipy 和 matplotlib,在终端使用 pip install numpy scipy matplotlib 即可安装。

import numpy as np
from scipy import signal
import matplotlib.pyplot as plt

# ==========================================
# 1. 模拟生成具有微小外圈点蚀的振动信号
# ==========================================
def generate_synthetic_vibration(fs=20000, duration=1.0, fr=30.0, bpfo=105.0, f_res=3000.0):
    t = np.linspace(0, duration, int(fs * duration), endpoint=False)
    N = len(t)
    
    # (A) 转轴旋转工频干扰 (1X, 2X 转频,能量较大)
    rotational_noise = 1.5 * np.sin(2 * np.pi * fr * t) + 0.8 * np.sin(2 * np.pi * (2 * fr) * t)
    
    # (B) 构造外圈周期性冲击脉冲序列 (周期 T = 1 / BPFO)
    impact_train = np.zeros(N)
    impact_interval = int(fs / bpfo)
    for idx in range(0, N, impact_interval):
        impact_train[idx] = 1.0
        
    # (C) 结构高频共振冲激响应 (3000 Hz 衰减正弦波)
    decay_len = int(fs * 0.008) # 衰减时间常数约 8ms
    t_decay = np.linspace(0, 0.008, decay_len, endpoint=False)
    impulse_response = np.exp(-600 * t_decay) * np.sin(2 * np.pi * f_res * t_decay)
    
    # 冲击卷积共振响应
    fault_signal = signal.convolve(impact_train, impulse_response, mode='same') * 0.6
    
    # (D) 添加随机高斯背景白噪声
    white_noise = np.random.normal(0, 0.5, N)
    
    # 合成最终传感器拾取信号
    raw_signal = rotational_noise + fault_signal + white_noise
    return t, raw_signal

# ==========================================
# 2. 包络谱分析核心诊断流水线
# ==========================================
def compute_envelope_spectrum(x, fs, band_low=2000.0, band_high=4000.0, filter_order=3):
    # 步骤 A:设计 Butterworth 二阶/四阶带通滤波器 (使用 SOS 格式提高数值稳定性)
    sos = signal.butter(filter_order, [band_low, band_high], btype='bandpass', fs=fs, output='sos')
    # 双向零相位失真滤波
    x_filtered = signal.sosfiltfilt(sos, x)
    
    # 步骤 B:执行希尔伯特变换求取解析信号
    analytic_signal = signal.hilbert(x_filtered)
    
    # 步骤 C:提取幅值包络并去除直流偏置
    envelope = np.abs(analytic_signal)
    envelope_ac = envelope - np.mean(envelope)
    
    # 步骤 D:计算包络信号的单边快速傅里叶变换 (FFT)
    N = len(envelope_ac)
    fft_vals = np.fft.rfft(envelope_ac)
    freqs = np.fft.rfftfreq(N, d=1.0 / fs)
    amplitude_spectrum = np.abs(fft_vals) * (2.0 / N)
    
    return x_filtered, envelope_ac, freqs, amplitude_spectrum

# ==========================================
# 3. 运行执行与结果验证
# ==========================================
if __name__ == "__main__":
    fs = 20000         # 采样率 20 kHz
    duration = 1.5     # 采样时长 1.5 秒 (提供 0.67 Hz 的频率分辨率)
    target_bpfo = 105.0# 设定理论外圈故障特征频率 105 Hz
    
    # 生成测试振动信号
    t, raw_vib = generate_synthetic_vibration(fs=fs, duration=duration, fr=30.0, bpfo=target_bpfo, f_res=3000.0)
    
    # 执行包络谱算法 (选取 2000~4000 Hz 覆盖 3000 Hz 的结构共振带)
    x_band, env, f_axis, env_spec = compute_envelope_spectrum(
        raw_vib, fs=fs, band_low=2000.0, band_high=4000.0
    )
    
    # 在低频诊断区 [0 ~ 500 Hz] 查找最高谱峰
    diag_mask = (f_axis >= 10.0) & (f_axis <= 400.0)
    peak_idx = np.argmax(env_spec[diag_mask])
    detected_freq = f_axis[diag_mask][peak_idx]
    
    print("=== 诊断比对结果 ===")
    print(f"理论故障特征频率 BPFO: {target_bpfo:.2f} Hz")
    print(f"包络谱检出最高特征峰: {detected_freq:.2f} Hz")
    print(f"诊断判定: {'外圈局部剥落故障明确' if abs(detected_freq - target_bpfo) < 2.0 else '未见明显特征'}")

示例执行输出说明

运行上述代码后,控制台将输出如下诊断结论:

=== 诊断比对结果 ===
理论故障特征频率 BPFO: 105.00 Hz
包络谱检出最高特征峰: 105.33 Hz
诊断判定: 外圈局部剥落故障明确

检出频率(105.33 Hz)与理论值(105.00 Hz)的细微微差来自于 1.5 秒采样时长下的离散 FFT 频率栅格分辨率(Δf = 1 / 1.5 ≈ 0.67 Hz),诊断精度完全在工程允许的误差范围之内。

五、工程落地避坑指南与参数调优排查

在实验室仿真中,因为参数已知,诊断往往一击即中;但在工程现场实测时,许多工程师却常遇到“包络谱做出来全是一片平坦白噪声”或“谱峰对不上”的问题。结合现场实施经验,梳理如下四维排查决策指南:

怎么用Python实现振动信号的包络谱故障诊断
图 4:包络谱工程排查与调试决策矩阵
关键影响维度 常见误区与工程表现 标准推荐调试方案
滤波通带选取 随意按经验选取通带(如盲目选 1k~2kHz),未能准确覆盖传感器安装座与轴承的实际共振区,导致滤出物仅为无调制的纯噪声。 先做原始信号的全景 FFT 观察高频隆起包络;或结合快速谱峭度图(Fast Kurtogram)算法自动计算峭度最大化的中心频率与最优带宽。
采样长度与分辨率 仅采集 0.1 秒或 0.2 秒振动数据,频率分辨率仅 5~10 Hz,导致 BPFO、转频 1X 与电网工频混成一团无法分辨。 采样时间 T 建议至少保证 1 秒以上(Δf ≤ 1 Hz),对于低速重载设备(低于 600 rpm),采样时长建议提高至 3~5 秒。
轴承运转滑差 (Slip) 现场测得谱峰在 103.5 Hz,而理论计算为 105.0 Hz,因死板匹配理论值而误判为“无故障”。 理论公式是基于绝对纯滚动的理想几何推导。实际上受轴向预紧力不足、径向载荷及润滑油膜剪切影响,滚动体存在 1%~2% 的滑差率。因此实测谱峰往往略低于理论值,寻峰算法应保留 ±2% 动态容差。
转频边频带判定 将谱线上伴随的复杂密集侧频带误认为是齿轮箱啮合故障。 若特征峰是 BPFI(内圈故障),由于内圈随轴旋转进出高压承载区,其幅值包络本身还会受到转轴转速 fr 的二次调制,因此谱峰两侧必然伴随间距为 ±fr, ±2fr 的调制边频带,这是内圈故障的铁证。

六、总结与实践建议

利用 Python 实现振动信号的包络谱故障诊断,本质上是一个清晰的“物理滤波 + 数学解调 + 谱分析”闭环:

  1. 定位共振带:借由结构共振的高频载波放大微弱冲击能量,通过带通滤波剥离低频工频机械运动和大能量背景干扰;
  2. 提取外包络:使用 scipy.signal.hilbert 构造正交解析信号并取模,捕获由冲击引发的瞬态衰减包络曲线;
  3. 必须去直流:减除均值以消除幅值模值的恒正偏置,避免 0 Hz 直流巨峰遮盖;
  4. 对齐特征峰:执行 FFT 并与几何推导的 BPFO、BPFI、BSF 及其谐波进行对齐校核,结合滑差修正与边频带形态做出明确判断。

掌握这套简洁严谨的 Python 流水线,不仅能让你在离线分析科研中快速验证各种故障轴承数据集(如西储大学 CWRU 轴承数据、江南大学轴承数据等),也能便捷地集成到工业物联网(IIoT)边缘网关与自动化设备健康监测平台中。

相关专题:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表