acoustic-anomaly-wind-turbine
:::warning [UNTESTED] — v0.6 paper-correct
The three DSP cores (acoustic_features, rotor_speed, bpf_modulation) are host-unit-tested on native_sim/native/64; the full app runs end-to-end on native_sim with synthetic acoustics + a canned RPM track. No model ships (stub + deterministic fallback). HiL on a real nacelle with a customer-trained model is bench-gated.
:::
Nacelle acoustic condition monitor for wind turbines: a PDM MEMS mic captures audible-band sound, DSP extracts spectral features, blade-periodic energy is normalised to rotor order (RPM-invariant via the blade-pass frequency), and a per-interval anomaly score + advisory subsystem/flag is emitted for both drivetrain tonal faults and gross blade aero-anomalies.
Source: examples/audio/acoustic-anomaly-wind-turbine/.
Honest scope
An airborne nacelle mic credibly detects drivetrain/gearbox/bearing tonals (loudest, most reliable), rotor imbalance (amplitude modulation at the blade-pass frequency), trailing-edge-crack whistle, severe leading-edge erosion, and icing. It does not detect early internal cracks / delamination / fiber breakage — those are Acoustic Emission (ultrasonic, structure-borne, requiring a contact piezo bonded to the blade) and out of scope for an airborne mic.
Blade-pass frequency invariant: BPF = N_blades × RPM / 60 (≈0.75 Hz for a 3-blade turbine at 15 rpm). Evaluating modulation in rotor orders at the current BPF makes the signature RPM-invariant under variable-speed operation.
Pipeline
PDM mic (<alp/audio.h>) --frame--> acoustic_features (FFT bands, flatness,
centroid, kurtosis) --band energy--> bpf_modulation (Goertzel at BPF orders)
tacho GPIO / tacholess --> rotor_speed --> rpm, BPF
--> <alp/inference.h> anomaly score (deterministic fallback) --> WTAC record
Why one hand-rolled Goertzel remains
acoustic_features.c takes its full-spectrum FFT and window statistics from <alp/dsp.h> — a one-stage ALP_DSP_STAGE_FFT chain plus alp_dsp_stats_f32(), backed by CMSIS-DSP arm_rfft_fast_f32 on the M55.
bpf_modulation.c deliberately keeps a single-bin Goertzel instead, and it is not a gap. It needs exactly one bin: the live blade-pass harmonic k × BPF, which shifts with every RPM estimate and therefore never lands on a fixed FFT bin boundary. An N-point FFT would compute O(N log N) work to produce N bins and discard all but one; the O(N) Goertzel recurrence computes only the bin actually needed, at an arbitrary non-bin-aligned frequency. CMSIS-DSP ships no Goertzel kernel to call instead, so it stays a portable loop — the cheaper, textbook-correct tool for this job.
board.yaml
libraries:
- name: tflite-micro
cores: [m55_he]
- name: cmsis-dsp
cores: [m55_he]
som:
sku: E1M-AEN801
preset: e1m-evk
cores:
a32_cluster:
os: "off"
m55_he:
app: ./src
inference:
default_arena_kib: 64
peripherals:
- gpio # tacho pulse input (rotor speed).
diagnostics:
log_level: info
PDM mic input is the Zephyr DMIC audio subsystem (via CONFIG_AUDIO_DMIC=y), not a peripheral enum. Flip som.sku to E1M-V2M101 for the DEEPX DX-M1 path.
Expected output
# WTAC,t_s,rpm,bpf_hz,anomaly_score,dominant_subsystem,top_band_hz,flags,rpm_src
WTAC,12.0,17.4,0.87,0.62,BLADE_BPF,3333.3,IMBALANCE,ESTIMATED
rpm_src ∈ {TACHO, ESTIMATED, CANNED}: TACHO = live GPIO pulse counting, ESTIMATED = tacholess envelope-based estimate (demo default when the estimate converges), CANNED = fixed look-up table fallback.
See also
<alp/audio.h>·<alp/inference.h>·<alp/dsp.h>ai-anomaly-detection-vibration— vibration-based condition monitor- Examples overview