drone-propulsion-audio-cnn
A small multi-task CNN over log-mel spectrograms of drone propulsion audio, trained as a methodology baseline: the deliverable is not the accuracy, it is the gap between evaluation protocols.
Four heads, all from a 2 s / 16 kHz window:
| head | classes |
|---|---|
presence |
silence, drone |
drone |
drone1, drone2 |
throttle |
low, high |
dist |
25 cm, 50 cm |
1.18 M parameters. Trained on a single RTX 5090 in 3.1 min.
The result that matters
Same architecture, same data, same hyperparameters β only the train/test split changes.
| protocol | presence |
drone |
throttle |
dist |
what it measures |
|---|---|---|---|---|---|
naive_window |
0.948 | 1.000 | 1.000 | 0.999 | leaky β adjacent windows of one recording land on both sides |
by_run |
0.420 | 0.967 | 0.959 | 0.919 | no window sharing; same drone/distance/throttle/mic seen in training |
by_mic |
0.797 | 0.846 | 0.988 | 0.868 | unseen microphone placement |
by_distance |
0.479 | 0.814 | 0.988 | β | unseen distance / SNR regime |
Random-window splitting reports drone at 1.000. Holding out whole
recordings drops it to 0.967, and holding out a microphone configuration drops it to 0.846.
Nothing about the model changed. The first number is an artefact of adjacent 2 s windows
straddling the split β the model is re-identifying a recording, not recognising a drone.
This matters because published drone-acoustics results are frequently reported under the first protocol.
The two targets do not degrade the same way
This is the more useful half of the result. Under the two genuine distribution shifts β an unseen microphone and an unseen distance β the heads come apart:
| head | naive | by_mic | by_distance |
|---|---|---|---|
drone (identity) |
1.000 | 0.846 | 0.814 |
throttle (RPM proxy) |
1.000 | 0.988 | 0.988 |
Throttle holds at ~0.99 through both shifts. Drone identity falls to 0.846 / 0.814.
That is what the physics predicts. Throttle sets rotational speed, which sets the blade-pass frequency and its harmonic comb β a spectral position, and a different microphone at a different distance still sees the same position. Unit identity leans on timbre, relative harmonic weighting and channel colouration, all of which the microphone and the propagation path change.
Practical reading: RPM-linked targets transfer across rigs; identity-linked targets need the microphone and the geometry inside the training distribution. If you are choosing what to build first from a propulsion-audio rig, the regression target is the safer product.
Caveat on the presence head
presence is poorly conditioned here and its numbers should not be read as a detection result.
The corpus has 144 drone recordings against 24 silence recordings, and the silence ones are shorter
(~31 s vs ~91 s) β roughly an 18:1 window imbalance, trained with unweighted cross-entropy. Under
by_run only ~17 silence recordings remain in training and the head becomes unstable
(0.420 macro-F1). This is a class-imbalance artefact, not evidence about
leakage. Fixing it needs class weighting and more silence, not a different split.
Comparison β learned features vs. hand-crafted auditory features
Mobley, Campbell & Rasband (Classification of SUAS propellers with auditory feature extraction methods, NOISE-CON 2023) classify four small-UAS propellers from Timbre-Toolbox descriptors plus MOSQITO sound-quality metrics fed to a random decision forest, and find acoustic roughness to be the single most discriminative feature. Their evaluation is a random split at a fixed 50 ft radius.
We rebuilt that kind of feature vector β 30 descriptors, MOSQITO roughness/loudness/sharpness plus
reimplemented Timbre-Toolbox temporal and spectral descriptors β and ran it through the same four
protocols as the CNN. features.py and train_features.py are in this repo.
| model | naive drone |
naive throttle |
by_run drone |
by_run throttle |
by_mic drone |
by_mic throttle |
by_dist drone |
by_dist throttle |
|---|---|---|---|---|---|---|---|---|
| CNN (log-mel) | 1.000 | 1.000 | 0.967 | 0.959 | 0.846 | 0.988 | 0.814 | 0.988 |
| RF (30 features) | 0.990 | 0.998 | 0.943 | 0.982 | 0.345 | 0.564 | 0.627 | 0.811 |
| RF (level features dropped) | 0.978 | 0.986 | 0.942 | 0.924 | 0.353 | 0.566 | 0.631 | 0.660 |
In distribution the two are equivalent β the forest matches or beats the CNN under by_run
(0.982 vs 0.959 on throttle).
Under a microphone change the forest collapses and the CNN does not: throttle 0.564 vs 0.988, drone 0.345 vs 0.846.
It is not an absolute-level artefact. Dropping the four level-carrying descriptors
(level_db, level_a_db, loudness, spec_energy) changes nothing
(0.566 on throttle). The reason is that the descriptors themselves
are microphone-dependent β 12 of 30 shift by more
than one pooled standard deviation between the training microphones and the held-out one:
| descriptor | Cohen's d |
|---|---|
spec_spread |
+2.02 |
spec_rolloff85 |
+1.73 |
level_db |
-1.45 |
spec_flatness |
+1.40 |
am_depth |
-1.32 |
sharpness |
+1.31 |
A forest splits on absolute thresholds and has no machinery to absorb that shift; the CNN sees a normalised spectrogram and was trained with SpecAugment and Β±6 dB gain jitter.
Note what sits in that list: roughness (-1.14) and sharpness
(+1.31) β the two MOSQITO metrics the NOISE-CON feature ranking leans
on hardest. Their result stands for a fixed measurement chain, but a roughness threshold learned on
one microphone should not be expected to hold on another. For a rig with a microphone arc, where
each position is a different physical transducer, that is worth testing before trusting.
Caveat: the forest was fitted on 2424 windows
against the CNN's 18798 β roughness costs ~2 s of CPU
per 2 s window, which caps how densely it can be sampled. It is not data-starved in distribution
(it reaches 0.943 under by_run), but the comparison is not perfectly matched.
Side result β why a smarter split does not rescue a single-instance dataset
orhanyaman/Propeller is the dataset behind the only
published paper that classifies propellers by blade count from sound. It is five files β one
continuous recording per class (2/3/4/5/6 blades, underwater, ~5β6 min each).
Same CNN, two protocols:
| protocol | macro-F1 |
|---|---|
| random windows | 0.983 |
| time-block (first 70 % trains, last 30 % tests) | 0.950 |
The gap is only +0.032 β and that is the point. With one physical propeller per class, train and test come from the same propeller, same session, same hydrophone, no matter how you cut the time axis. Both numbers measure "can you re-identify this recording", not "can you recognise a four-blade propeller". A single-instance dataset cannot be fixed with a better split; it can only be fixed with more physical instances.
Data
DroneAudioSet drone-only
subset (MIT), NeurIPS 2025 Datasets & Benchmarks. A full factorial:
2 drones Γ 2 distances Γ 2 throttle levels Γ 3 mic configs Γ (6 runs + 1 silence) = 168 recordings,
16 kHz, 30β152 s each.
Note the dataset's own purpose is drone audition β a microphone on board the drone, detecting humans underneath. There the drone is the noise. Here it is the signal, which is a different task than the one the authors benchmark.
Channel 0 only. The 8array-* configs ship 8 channels, soundskrit ships 1; taking ch0
everywhere keeps one comparable stream across mic configs.
Intended use
A template and a sanity check, not a production detector. It exists to establish, before a partner lab's own measurements arrive, that:
- the evaluation protocol dominates the reported number;
- throttle level (a proxy for RPM) is recoverable from steady-state propulsion audio;
- an unseen microphone placement is a real distribution shift, not a formality.
Do not use it to detect drones in the field. Two drone identities at 25β50 cm in a controlled setting is not a field distribution.
Training details
- Features: log-mel, 128 mels,
n_fft1024, hop 160, 20 Hzβ8 kHz, computed on GPU. - Augmentation: SpecAugment (2 freq + 2 time masks) and Β±6 dB gain jitter. No pitch or time-stretch augmentation β pitch shift moves the blade-pass frequency, which is the throttle label; augmenting it away trains the model to ignore the target.
- AdamW, OneCycle, 12 epochs, bf16 autocast.
- Loss is masked per head, so silence windows train
presenceonly.
Released weights are the by_run checkpoint, so the by_run row above is this checkpoint's own
score.
Usage
import torch, torchaudio
from huggingface_hub import hf_hub_download
state = torch.load(hf_hub_download("makromaksym/drone-propulsion-audio-cnn", "model.pt"), map_location="cpu")
# Architecture: see train.py (SmallCNN) in the Files tab.
train.py, prepare.py, data_mod.py, features.py, train_features.py and
propeller_bladecount.py are in the Files tab β the split protocols live in data_mod.py.
Full project, including the briefing this came out of: https://github.com/dontriskit/ars-lab-drone-audio
Limitations
- Two drone identities.
dronehere is closer to "which of these two units" than to drone typing. - Binary throttle, not measured RPM. No tachometer ground truth in this dataset.
- 16 kHz. Propeller harmonics extend past 8 kHz; this ceiling is inherited from the source data.
- Single channel. The mic-array geometry is discarded.