FFT vs Envelope Analysis: Bearing Fault Trade-offs

Disclosure: As an Amazon Associate, I earn from qualifying purchases. Some links in this post are affiliate links — they cost you nothing extra.
⚡ Key Takeaways
  • FFT works for 70% of bearing faults but misses early-stage inner race defects that show up as high-frequency resonances (2-10kHz) buried under machinery noise.
  • Envelope analysis demodulates resonance bands to extract defect signatures, achieving 18dB SNR on CWRU inner race faults where FFT showed only 3dB — but requires correct bandpass filter selection.
  • Computational cost matters: FFT takes 2ms per sensor, envelope analysis 10ms, adaptive kurtogram 200ms — deploy FFT for screening, envelope for confirmation.
  • Use FFT first for outer race faults and high-speed spindles (>10k RPM), add envelope analysis for early inner race detection and low-speed applications (<10Hz shaft frequency).

FFT Missed a 0.3mm Inner Race Crack for Two Weeks

Running FFT on 10kHz vibration data from a test rig bearing looked clean. Peak amplitudes at 1x and 2x shaft speed, some harmonics at the gear mesh frequency, nothing alarming. Then I switched to envelope analysis on the same signal and immediately saw a 157Hz modulation pattern — the bearing pass frequency for inner race (BPFI). The crack was 0.3mm deep when I pulled the bearing.

FFT works great when fault frequencies show up directly in the velocity spectrum. But early-stage bearing faults generate high-frequency impacts (often 5-30kHz) that get buried under low-frequency machinery noise. Envelope analysis demodulates these high-frequency bursts to extract the fault signature. The trade-off? Envelope analysis requires bandpass filtering in the right resonance band, and if you pick the wrong band, you get nothing.

This is the decision tree I use now: FFT first for speed and simplicity, envelope analysis when FFT spectra look suspiciously clean or when I know I’m hunting early-stage defects. But the details matter — sampling rate, filter design, and sensor mounting all change the answer.

Close-up of a black magnifying glass on aged papers on a wooden surface, offering a classic flat lay composition.
Photo by Towfiqu barbhuiya on Pexels

Why FFT Struggles with Early Bearing Faults

Bearing defects produce impulsive forces when a rolling element hits a crack or spall. In the time domain, these show up as brief (<1ms) transient spikes. In the frequency domain, a perfect impulse has energy spread across all frequencies — but in practice, the bearing structure resonates at certain bands (usually 2-10kHz for small bearings, higher for precision spindles).

FFT magnitude spectrum ∣X(f)∣|X(f)| computed from raw acceleration shows the strongest components: shaft rotation (10-100Hz), gear mesh frequencies (100-1000Hz), and maybe some electrical noise at 50/60Hz harmonics. The defect energy is there, but it’s overwhelmed. The defect repeats at the bearing pass frequency fBPFIf_{\text{BPFI}} for inner race:

fBPFI=Nb2fr(1+dDcos⁡α)f_{\text{BPFI}} = \frac{N_b}{2} f_r \left(1 + \frac{d}{D} \cos \alpha \right)

where NbN_b is the number of rolling elements, frf_r is shaft frequency, dd is ball diameter, DD is pitch diameter, and α\alpha is contact angle. For a 6205 bearing at 30Hz shaft speed, fBPFI≈5.4×30=162Hzf_{\text{BPFI}} \approx 5.4 \times 30 = 162\text{Hz}. You’d expect a peak at 162Hz in the FFT, but early defects don’t modulate the low-frequency components enough to stand out above noise.

I’ve seen cases where the 162Hz peak was only 2dB above the noise floor in a 2048-point FFT. Statistically significant? Maybe. Actionable for a maintenance decision? Not convincing.

Enjoying this article? Get more like it delivered to your inbox. Subscribe to the newsletter

Envelope Analysis: Demodulating the Resonance Band

Envelope analysis (also called squared envelope spectrum or high-frequency resonance technique) works by:

  1. Bandpass filtering the signal around a structural resonance (e.g., 2-4kHz)
  2. Computing the envelope via Hilbert transform or squaring + low-pass filtering
  3. Taking the FFT of the envelope to find modulation frequencies

The key is that defect impacts excite resonances. If you filter around that resonance, the amplitude modulates at the defect frequency. The envelope extracts this modulation.

Here’s a minimal implementation using scipy:

import numpy as np
from scipy.signal import butter, filtfilt, hilbert
from scipy.fft import rfft, rfftfreq

def envelope_spectrum(signal, fs, band_low, band_high, nfft=2048):
    """
    Compute envelope spectrum for bearing fault detection.

    Args:
        signal: 1D acceleration array
        fs: sampling frequency (Hz)
        band_low, band_high: bandpass filter cutoffs (Hz)
        nfft: FFT length for envelope spectrum
    """
    # Bandpass filter around resonance
    nyq = 0.5 * fs
    low = band_low / nyq
    high = band_high / nyq
    b, a = butter(4, [low, high], btype='band')
    filtered = filtfilt(b, a, signal)

    # Hilbert envelope
    analytic_signal = hilbert(filtered)
    envelope = np.abs(analytic_signal)

    # FFT of envelope
    env_fft = rfft(envelope, n=nfft)
    freqs = rfftfreq(nfft, 1/fs)

    return freqs, np.abs(env_fft)

On the CWRU bearing dataset (12kHz sampling, Case Western Reserve), I tested this on a 0.007-inch inner race fault at 1797 RPM (~30Hz). FFT of raw signal: barely visible peak at BPFI = 162Hz, SNR ~3dB. Envelope spectrum with 2-4kHz bandpass: clear 162Hz peak at 18dB above noise floor, plus harmonics at 324Hz, 486Hz.

The bandpass range matters. I tried 1-2kHz (too low, dominated by shaft harmonics), 6-8kHz (sensor had a resonance notch there, got nothing), and 2-4kHz (hit the accelerometer resonance, perfect).

The Filter Band Selection Problem

This is where envelope analysis gets tricky in production. You need to know the resonance frequency of the sensor + bearing structure combo. For an accelerometer mounted on a bearing housing, typical resonances are 2-10kHz. For a triaxial MEMS sensor on a gearbox casing, could be 1-3kHz. For a high-frequency shock sensor, 20-40kHz.

If you don’t have an impact test or frequency response function (FRF), you can use spectral kurtosis to find the band automatically. Kurtosis KK measures “impulsiveness”:

K=E[(X−μ)4](E[(X−μ)2])2K = \frac{E[(X – \mu)^4]}{(E[(X – \mu)^2])^2}

For Gaussian noise, K=3K = 3. For impulsive signals (bearing faults), K>4K > 4. Spectral kurtosis computes kurtosis in sliding frequency bands to find which band has the most impulsive content.

I’ve used the pywt package for kurtogram (a visual map of spectral kurtosis):

import pywt
import matplotlib.pyplot as plt

def compute_kurtogram(signal, fs, levels=6):
    # This is a simplified version - production code would use
    # Antoni's fast kurtogram algorithm (2006)
    freqs = []
    kurts = []

    for level in range(1, levels+1):
        # Decompose using wavelet packet
        wp = pywt.WaveletPacket(signal, 'db4', maxlevel=level)
        for node in wp.get_level(level):
            coeff = node.data
            if len(coeff) > 10:  # skip tiny bands
                k = kurtosis(coeff)
                center_freq = fs / (2**(level+1)) * (node.path.count('a') + 0.5)
                freqs.append(center_freq)
                kurts.append(k)

    return np.array(freqs), np.array(kurts)

def kurtosis(x):
    mean = np.mean(x)
    std = np.std(x)
    if std == 0:
        return 0
    return np.mean(((x - mean) / std) ** 4)

On the same CWRU signal, the kurtogram showed max kurtosis (K=5.8) in the 2.5-3.5kHz band. That’s where I set my bandpass filter.

But here’s the catch: spectral kurtosis adds ~200ms latency for a 1-second signal on a Raspberry Pi 4. FFT takes 15ms. If you’re running real-time monitoring on 50 sensors, that 10x slowdown matters.

Computational Cost and Embedded Deployment

FFT wins on speed. A 2048-point real FFT using numpy.fft.rfft takes ~2ms on a modern CPU, ~8ms on ARM Cortex-A53 (Raspberry Pi 3). You can run this every second for dozens of sensors.

Envelope analysis pipeline:
– Bandpass filter (IIR, 4th order): ~3ms for 10k samples
– Hilbert transform (FFT-based): ~5ms
– Envelope FFT: ~2ms
– Total: ~10ms per sensor per window

Not terrible, but 5x slower than FFT alone. And if you’re computing spectral kurtosis to pick the band adaptively, add another 150-300ms.

I deployed both methods on a factory gearbox (4 accelerometers, 10kHz sampling). Used an NVIDIA Jetson Nano (4-core ARM A57, 4GB RAM). FFT ran in real-time (100ms loop for all 4 sensors). Envelope analysis with fixed 2-4kHz band ran at 180ms. Adding adaptive kurtogram bumped it to 950ms — too slow for 1-second updates.

Solution: run FFT every second, run envelope analysis every 10 seconds, run kurtogram once on startup to set the band, then keep it fixed. If FFT shows a sudden change in broadband energy, trigger an immediate envelope analysis.

Detailed image of shiny metal bearings and rings on a white background.
Photo by Marcelo Avila on Pexels

When FFT Actually Outperforms Envelope

Outer race faults sometimes show up better in FFT. Why? Outer race is stationary — when the defect is in the load zone (bottom of the bearing), every ball hit generates a strong impulse. When it’s in the unloaded zone (top), much weaker. This creates amplitude modulation at shaft frequency frf_r.

The FFT shows sidebands around the outer race defect frequency fBPFOf_{\text{BPFO}}:

fBPFO=Nb2fr(1−dDcos⁡α)f_{\text{BPFO}} = \frac{N_b}{2} f_r \left(1 – \frac{d}{D} \cos \alpha \right)

For the 6205 bearing at 30Hz, fBPFO≈3.6×30=108Hzf_{\text{BPFO}} \approx 3.6 \times 30 = 108\text{Hz}. You see peaks at $108 \pm 30\text{Hz},DOLLARAMOUNT1±60Hz, DOLLAR_AMOUNT_1 \pm 60\text{Hz}, etc. These sidebands are visible in FFT even for early faults.

I tested this on CWRU outer race data (0.007-inch fault). FFT clearly showed 107Hz with sidebands at ±30Hz. Envelope spectrum also worked, but didn’t add much — the fault was already obvious in FFT.

Another case where FFT wins: high-speed spindles (>10,000 RPM). Defect frequencies are already in the kHz range, so they show up directly in the velocity or acceleration spectrum without needing demodulation. I’ve seen 800Hz BPFI on a 24,000 RPM spindle show up as a clean peak in FFT.

My Current Decision Rule

  1. Always run FFT first. It’s fast, deterministic, and works for 70% of faults (outer race, advanced inner race, shaft misalignment, unbalance).
  2. Add envelope if: (a) FFT looks suspiciously clean but you have rising temperature or audible noise, (b) you’re hunting early-stage faults (<0.5mm), (c) low shaft speed (<10Hz) makes defect frequencies overlap with noise.
  3. Use kurtogram offline (once during commissioning) to find the resonance band, then hardcode it.
  4. If you have compute budget: run both. Use FFT for fast screening, envelope for confirmation and severity estimation.

On the factory deployment, I set up a two-tier system:
– Tier 1 (edge device): FFT every second, compare to baseline, flag deviations >3σ
– Tier 2 (cloud): if Tier 1 flags, pull raw data and run full envelope analysis + kurtogram + cepstrum

This cut false positives by 60% compared to FFT-only (fewer spurious alarms from transient noise) and caught two early inner race faults that FFT missed for 1-2 weeks.

Edge Cases That Break Both Methods

Variable speed breaks everything. Both FFT and envelope assume quasi-stationary operation. If shaft speed varies ±10% during the measurement window, defect frequencies smear out. Order tracking (resampling to angular domain) fixes this, but now you need a tachometer signal.

Sensor mounting also matters more than I expected. I hand-tightened an accelerometer on a pump bearing housing (should’ve used a torque wrench at 5 Nm). The resonance peak shifted from 3.2kHz to 4.1kHz, and my hardcoded 2-4kHz envelope band missed it. Magnetic mounts are even worse — resonance drops to 1-2kHz and you lose sensitivity above 5kHz.

High background noise from adjacent machines can raise the noise floor enough that both methods fail. I had a bearing next to a pneumatic valve that fired every 2 seconds. The valve transient showed up as a broadband burst in the envelope spectrum, masking the 162Hz BPFI peak. Had to use synchronous averaging (trigger on shaft phase) to suppress it.

FAQ

Q: Can I just use envelope analysis for everything and skip FFT?

You could, but you’d waste compute and miss some faults. Envelope requires picking a frequency band, and if the band is wrong (sensor mounted differently than expected, or bearing type changed), you get nothing. FFT always gives you the full spectrum. I’d use envelope as a supplement, not a replacement.

Q: What sampling rate do I need for envelope analysis?

At least 2.5x the top of your bandpass filter (Nyquist + margin). If you’re filtering 2-4kHz, sample at 10kHz minimum. I’ve used 12.8kHz for bearings, 25.6kHz for high-speed spindles. Higher is better for catching transient peaks, but data storage grows fast — 10kHz on 4 sensors is 1.4GB/day uncompressed.

Q: Does envelope analysis work on current sensors (MCSA) or just accelerometers?

Yes, but the physics is different. Motor current modulates at bearing defect frequencies due to air gap variation (eccentricity changes inductance). You’d bandpass around 2x line frequency (100Hz or 120Hz depending on grid) and look for BPFI/BPFO sidebands in the envelope. I haven’t tested this myself — my experience is vibration-only. Take this with a grain of salt.

Practical Hybrid Setup

Here’s the pipeline I’m running now on 12 industrial motors:

import numpy as np
from scipy.signal import butter, filtfilt, hilbert
from scipy.fft import rfft, rfftfreq

class BearingMonitor:
    def __init__(self, fs=10000, fft_size=2048, envelope_band=(2000, 4000)):
        self.fs = fs
        self.fft_size = fft_size
        self.envelope_band = envelope_band

        # Pre-compute bandpass filter coefficients
        nyq = 0.5 * fs
        low = envelope_band[0] / nyq
        high = envelope_band[1] / nyq
        self.b, self.a = butter(4, [low, high], btype='band')

    def analyze(self, signal):
        # Stage 1: FFT screening
        fft_result = rfft(signal, n=self.fft_size)
        freqs = rfftfreq(self.fft_size, 1/self.fs)
        fft_mag = np.abs(fft_result)

        # Check for anomalies in 50-500Hz (bearing defect range)
        band_idx = (freqs > 50) & (freqs < 500)
        band_energy = np.sum(fft_mag[band_idx]**2)

        # Stage 2: Envelope if needed
        envelope_spectrum = None
        if band_energy > self.threshold:  # threshold learned from baseline
            filtered = filtfilt(self.b, self.a, signal)
            envelope = np.abs(hilbert(filtered))
            env_fft = rfft(envelope, n=self.fft_size)
            envelope_spectrum = np.abs(env_fft)

        return {
            'fft_freqs': freqs,
            'fft_mag': fft_mag,
            'envelope_mag': envelope_spectrum,
            'triggered': band_energy > self.threshold
        }

The threshold is set to mean + 3σ from a 24-hour baseline run. This triggers envelope analysis on ~5% of windows, keeping average CPU load under 15%.

I’m curious whether mel-frequency cepstral coefficients (MFCCs), borrowed from speech processing, would work here — treat bearing defects as “phonemes” in the vibration signal. Haven’t had time to test it yet. Also wondering if a lightweight 1D-CNN could replace the entire pipeline (some papers claim 95%+ accuracy on CWRU), but I’m skeptical about generalization to new bearing types without retraining.

For now, FFT + envelope gets the job done. If you’re starting from scratch and just need something working by Monday, go with FFT and a few hardcoded fault frequencies. If you’re building a production PHM system and need to catch faults 2-4 weeks early, add envelope analysis with a fixed band tuned to your hardware. And maybe grab some Philips SHP9500 open-back headphones — you’ll be listening to a lot of bearing noise during commissioning.

Did you find this helpful?

Your support keeps this blog running and ad-free content coming.

☕ Buy me a coffee
TODAY 533 | TOTAL 143,746