spectral_kurtosis — ACOUSTICS bearing op

• 数据种类:signal → table

• 调用: import fullseye as fs; fs.ledger.spectral_kurtosis(x, rate, win=None, hop=None, window='hann')(要直接调用实现,import acoustics; acoustics.spectral_kurtosis(x, rate, win=None, hop=None, window='hann');从台账取用则 opsacoustics.get("spectral_kurtosis"))

用法

哪个频带是冲击性的 —— 也就是该在哪里解调。

> 以下的详细说明为原文 —— 摘要与标题已翻译。

envelope_spectrum needs a band, and picking it by eye from a spectrum picks the *loudest* band, which is usually a gear mesh or a line harmonic rather than the bearing. Spectral kurtosis picks the *most non-stationary* band instead: for each frequency bin it measures the fourth-order behaviour of that bin's STFT coefficient across frames.

The normalisation is chosen so the two reference cases are exact:

• stationary complex circular Gaussian noise gives SK = 0,

• a pure tone (constant magnitude in its bin) gives SK = -1,

• a repetitive transient gives SK > 0, and the larger it is the more concentrated in time the band's content is.

Measured over 8192 samples at 16 kHz at the default window (64, 509 interior

frames): white Gaussian noise gives a mean SK of -0.0444 over the interior bins, against the estimator's own standard deviation 4/sqrt(509) = 0.1773, so it is zero; and a 2 kHz tone gives -1.0000 in its bin. Both reference cases land on their closed forms.

The answer is a band, and it depends on the window — measured, not asserted. The frame has to be *shorter than the gap between transients*, or every frame contains one and the band looks perfectly stationary. On the mode="impulse" bearing signal (25.6 kHz, resonance 3000 Hz, impulses every 9.35 ms, ring time constant 1.06 ms):

====== ========== ======= ========== =============

win frame (ms) max SK at (Hz) bin spacing

====== ========== ======= ========== =============

16 0.62 29.58 6400 1600 Hz

32 1.25 12.86 1600 800 Hz

64 2.50 5.38 2000 400 Hz

128 5.00 1.66 1600 200 Hz

256 10.00 -0.13 12200 100 Hz

====== ========== ======= ========== =============

The last row is the failure mode: at a 10 ms frame against a 9.35 ms impulse spacing, every frame holds exactly one impulse, the band is stationary by construction, and the operator reports a *negative* kurtosis at an unrelated frequency. Nothing raises. So window_seconds is returned, to be compared against the repetition period you expect, and sweeping win is part of using this operator rather than an optimisation.

What survives the sweep is the *band*, not the bin. On the same signal with noise_sigma=0.05 (the noiseless one is impulsive in every bin at once and its top six bins differ by 0.03, which is itself worth knowing), the six highest bins at win=64 are 2000, 2400, 1600, 4000, 3600 and 1200 Hz — bracketing the true 3000 Hz resonance without any of them being it. And that is enough: feeding the band this operator returns straight into envelope_spectrum recovers the defect rate exactly — measured 107.0000 Hz from the 1600-2400 Hz band the operator chose by itself, with no knowledge of the resonance.

The band is returned, not left to the caller to assemble. band_lo / band_hi are max_freq -+ bin_hz *clamped into the open interval* (0, rate/2) that envelope_spectrum accepts, so envelope_spectrum(x, rate, sk["band_lo"], sk["band_hi"]) is always a legal call. Assembling the band by hand is not: freqs runs up to and including Nyquist, so whenever the winning bin is the topmost interior one, max_freq + bin_hz lands exactly *on* Nyquist and envelope_spectrum refuses it — correctly, since no such band exists in the recording. Measured on the mode="am" bearing signal (25600 Hz, 1 s, 3 kHz carrier, 107 Hz defect, m = 0.5), whose kurtosis maximum lands on the top interior bin (12400 Hz, bin_hz 400):

=========================== ===================== ==============================

band handed to the consumer value envelope_spectrum

=========================== ===================== ==============================

max_freq -+ bin_hz 12000.0 - 12800.0 Hz ValueError (12800 = Nyquist)

band_lo / band_hi 12000.0 - 12600.0 Hz returns

=========================== ===================== ==============================

Returning is not the same as finding something, and this row is the honest

case: max_kurtosis is -0.2725 against noise_sigma 0.1001, so there was never a band to find — an amplitude-modulated tone is stationary in every bin, which is exactly what a negative SK says. Demodulating the band anyway gives peak_freq 1.0000 Hz at band_fraction

5.080e-05: nothing lives up there, and the returned diagnostics say so. (The same signal over the known resonance, 2000-4000 Hz, gives peak_freq 107.0000 at band_fraction 0.9999.) The contract this repair adds is only that the handoff is *legal* — refusing to answer a well-posed call is the caller's bug to hit, whereas judging the answer stays with max_kurtosis / noise_sigma / band_fraction.

The clamp margin is half a bin: an edge cannot be placed more finely than bin_hz in the first place, and half a bin is the smallest offset that is still a resolvable distance from the boundary — no epsilon, no rate-dependent fudge. band_lo < band_hi always holds, because max_freq is by construction an interior bin and therefore at least one full bin away from both 0 and Nyquist.

win defaults to the largest power of two that leaves at least 8 interior frames, clamped to [16, 64] — short, for the reason in the table — and the value used is returned. Fewer than 8 interior frames makes the fourth moment meaningless and is refused.

DC and Nyquist bins are excluded from max_kurtosis / max_freq: their STFT coefficients are real, not complex circular, so the -2 normalisation is the wrong one there and they read about -1 for noise. They are still present in kurtosis with the same formula, and real_bins names them.

Returns a dict: freqs, kurtosis, max_kurtosis, max_freq, band_lo, band_hi (the demodulation band, ready for envelope_spectrum), n_frames, win, hop, real_bins, window_seconds, bin_hz, noise_sigma (the estimator's own standard deviation, 4/sqrt(n_frames) — a peak below this is not a finding).

Raises ValueError: everything stft refuses, plus a signal too short for 8 frames at the chosen window.

详细使用指南

• acoustic_condition_monitoring 族使用指南

参考(示例数据、文献)

• 示例数据目录(下载 URL / 许可证) —— 2-D 用 skimage.data(BSD/公有领域)加合成图,3-D 给出真实数据源(Stanford/PDS 等)的下载 URL。

• 算子来历与参考文献 —— 该算子族所依据的研究/方法出处。

• 算法的正典(作者、年份)与用途见上面的族使用指南。

可运行的示例(实际调用该算子并已验证的样例)

• acoustic_condition_monitoring — py -3.11 examples/acoustic_condition_monitoring.py

• poc_bearing_diagnosis — py -3.11 examples/poc_bearing_diagnosis.py

类型可衔接的下一个算子(可接受 table 作为输入)

istft

同类别(bearing)

envelope_spectrum · bearing_defect_frequencies · cepstrum


*Provenance: acoustics.py — ACOUSTICS 算子登记表。本条目由 tools/opdocs.py md 自动生成(请勿手工编辑)。*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.