spectral_kurtosis — ACOUSTICS bearing op

데이터 종류: signaltable

호출: import acoustics; acoustics.spectral_kurtosis(x, rate, win=None, hop=None, window='hann')(또는 opsacoustics.get("spectral_kurtosis"))

사용법

어느 대역이 충격적인가 —— 즉 어디서 복조할 것인가.

> 아래 상세 설명은 원문입니다 —— 요약과 제목은 번역되어 있습니다.

:func: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

:func: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 :func: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

:func: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 :func:stft` refuses, plus a signal too

short for 8 frames at the chosen window.

자세한 사용 가이드

acoustic_condition_monitoring 패밀리 가이드

참고(샘플 데이터·문헌)

• 샘플 데이터 카탈로그(DL URL / 라이선스) —— 2-D 는 skimage.data(BSD/public)+ 합성, 3-D 는 실데이터 소스(Stanford/PDS 등)의 DL URL.

• 연산자의 내력·참고문헌 —— 이 연산자 족의 바탕이 된 연구/기법의 출처.

• 알고리즘의 정전(저자·연도)과 용도는 위의 패밀리 사용 가이드에 적혀 있습니다.

실행 가능한 예제(이 연산자를 실제로 호출하는 검증된 샘플)

acoustic_condition_monitoringpy -3.11 examples/acoustic_condition_monitoring.py

타입이 이어지는 다음 연산자(table 를 입력으로 받는 것)

istft

같은 카테고리(bearing)

envelope_spectrum · bearing_defect_frequencies · cepstrum


*Provenance: acoustics.py — ACOUSTICS 연산자 레지스트리. 이 op 노트는 tools/opdocs.py md 가 자동 생성합니다(직접 편집하지 마세요).*

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