Lab 8.4 — Vibration Anomaly Detection (Autoencoder)
← Course 2 syllabus · Bonus Module 8 · Prev: « Lab 8.3 · Next: Lab 8.5 »
Goal
Build a predictive-maintenance-style anomaly detector: train an autoencoder only on normal machine signals (vibration from an accelerometer, or a fan/motor audio proxy), then flag anomalies at run time when the reconstruction error exceeds a threshold. The skill built is the unsupervised edge-ML pattern that shows up in industrial and robotics firmware everywhere — you rarely have labeled fault data, so you model “normal” and detect deviation. You’ll pick the threshold properly from an ROC curve rather than eyeballing it, and run the detector on the edge boards. This ties the course’s statistical-DSP thread (random processes, spectral features) to a deployed, decision-making system.
Recommended reading
- Hayes Ch. 3 — discrete-time random processes: stationarity, autocorrelation, and the power spectrum of a random signal. A healthy machine is (roughly) a stationary process; a fault changes its statistics. → ../books/hayes-statistical-dsp/index.qmd
- Hayes Ch. 4 — signal modeling (AR/ARMA): the classical “model the normal, watch the residual” detector, which is the linear ancestor of the autoencoder residual you’ll use.
- B&P (Random Data) — the canonical treatment of vibration/random-data analysis: classifying the data, estimating spectra with quantified error, and the measurement procedures for turning a raw accelerometer stream into trustworthy features.
- Course 1 Weeks 5–6 — probability: distributions of the reconstruction error, choosing a threshold as a tail probability / false-alarm rate, and the ROC as a likelihood-ratio trade-off. → ../course1/index.qmd#week-5
Equipment & parts
- Host Mac (PyTorch) for training; ONNX export.
- Jetson Orin Nano and/or Raspberry Pi 5 for the on-device detector.
- A vibration/audio source — pick one:
- Audio proxy (simplest): a small fan or DC motor recorded through the USB/I²S mic. “Normal” = healthy running; “anomaly” = imbalance (a bit of tape on a blade), a stalled/loaded condition, or a bad bearing sound.
- Optional accelerometer/IMU: a BNO055 / ICM-20948 (I²C) mounted to the machine gives true 3-axis vibration; sample it over I²C on the board. This is the “real” predictive-maintenance sensor path.
- The ADS1115 reading an analog accelerometer or a piezo disc — low rate, fine for low-frequency mechanical vibration.
Safety & don’t-break-it
- Mechanical safety first. A spinning fan/motor with an intentional imbalance can throw the added mass. Secure the machine, keep fingers and leads clear of blades, and use a light, well-stuck imbalance mass. Eye protection if anything can fly off.
- Sensor mounting matters more than the model. A loosely mounted accelerometer measures its own rattle, not the machine. Rigidly couple it; keep the axis orientation fixed between the “normal” training data and the test data or the features shift and everything looks anomalous.
- I²C rules for the IMU/ADC: within-rails, common ground, correct address, and level-shift if the sensor is 5 V (Lab 3.5). Don’t exceed the ADS1115 input range with a piezo’s voltage spikes — piezos can output large transient voltages; clamp/divide.
- Motor back-EMF / inrush on the supply: drive the motor from a separate supply rail or add a flyback diode; don’t let motor transients reset the Pi/Jetson sharing the rail.
Background
Model the normal. Featurize a window of the signal the same way as the rest of the module — a log-mel or magnitude spectrogram, or a vector of band energies / RMS / spectral centroid, or (for an IMU) per-axis spectral features. Call the feature vector \(x \in \mathbb{R}^d\). Train an autoencoder \(x \mapsto \hat{x} = g_\theta(f_\theta(x))\) with a bottleneck, on normal data only, minimizing reconstruction error
\[ \mathcal{L}(\theta) \;=\; \frac{1}{d}\,\lVert x - \hat{x} \rVert_2^2 . \]
Because the bottleneck forces the network to learn the low-dimensional manifold of normal operation, it reconstructs normal windows well (small error) and fails to reconstruct patterns it never saw (large error). The per-window anomaly score is the reconstruction error
\[ e(x) \;=\; \lVert x - \hat{x} \rVert_2^2 , \]
and the detector fires when \(e(x) > \tau\).
Choosing the threshold \(\tau\). Fit the distribution of \(e\) on held-out normal data. A principled default sets \(\tau\) from a target false-alarm rate — e.g. mean-plus-\(k\)-sigma,
\[ \tau \;=\; \mu_e + k\,\sigma_e , \]
or a high empirical quantile (say the 99th percentile) of the normal-error distribution. With even a little labeled anomaly data, sweep \(\tau\) and plot the ROC curve (true-positive rate vs false-positive rate); pick the operating point from the cost of a missed fault vs a false alarm, and report the AUC as a threshold-independent quality measure. The ROC is exactly the probability trade-off from Course 1 Weeks 5–6 — each \(\tau\) is a likelihood-ratio cut.
Why an autoencoder over a classical AR residual. The Hayes Ch. 4 approach fits an AR/ARMA model to the normal signal and watches the prediction residual grow when the machine changes — a linear special case. The autoencoder generalizes it to nonlinear, multi-band structure, which matters for machines whose fault signature is a spectral-shape change, not just a power change.
Procedure
Part A — Collect normal data.
- Run the machine in its healthy state and record a long “normal” dataset across its real operating variation (warm-up, speed changes if any). Featurize into windows. Split into train / normal-validation.
Part B — Collect anomaly data.
- Induce faults you can create safely: imbalance (small mass on a blade), load/stall, a rough-bearing proxy. Record and featurize. Keep these out of training — they’re only for threshold selection and ROC.
Part C — Train on the host.
- Train the autoencoder on normal windows only; monitor the normal-validation reconstruction error (it should be low and stable). Export to ONNX.
# illustrative — owner writes/trains the real model
z = enc(x) # bottleneck
xhat = dec(z)
loss = F.mse_loss(xhat, x) # trained on NORMAL windows only
# at inference:
score = ((x - xhat)**2).mean(dim=1) # anomaly score e(x)Part D — Threshold, ROC, deploy.
- On normal-validation errors, compute \(\mu_e, \sigma_e\) and candidate \(\tau\); on the mixed normal+anomaly set, sweep \(\tau\) to build the ROC and pick the operating point. Record AUC.
- Deploy the encoder+decoder to the Pi 5 (onnxruntime) and Jetson (PyTorch/TensorRT). Stream live windows, compute \(e(x)\), apply \(\tau\) with a debounce (require \(N\) consecutive anomalous windows to fire — mechanical faults persist; single-window spikes are usually sensor noise).
- Log detection latency (feature + inference) and the live false-alarm rate over a clean run.
Deliverable & expected results
- The normal-error histogram with \(\tau\) overlaid; an ROC curve with AUC and the chosen operating point marked.
- A live-run log showing the detector staying quiet on normal operation and firing (after debounce) when you induce a fault.
| Quantity | Predicted / target | Measured |
|---|---|---|
| Mean normal reconstruction error \(\mu_e\) | small, stable | … |
| Threshold \(\tau = \mu_e + 3\sigma_e\) | (value) | … |
| False-alarm rate at chosen \(\tau\) | low (e.g. < 1%) | … |
| True-positive rate on induced faults | high | … |
| ROC AUC | → 1.0 (well-separated) | … |
| Detector latency, Pi 5 | (ms) | … |
| Detector latency, Jetson | (ms) | … |
Analysis & reconciliation
Interpret the normal-error histogram: a tight, low-variance distribution means the autoencoder learned the normal manifold well and \(\tau\) can sit close to the bulk; a heavy right tail on normal data means your feature or mounting is unstable and will cause false alarms. Read the ROC — if the anomaly and normal error distributions overlap (AUC well below 1), the fault doesn’t change the features you chose, so revisit the featurization (band energies vs full spectrogram, add an IMU axis). Justify the debounce from the physics: a real mechanical fault is persistent, so requiring consecutive anomalous windows trades a little detection latency for a large drop in false alarms — the same reasoning as contact debounce in firmware. Note the classical connection: your autoencoder residual is the nonlinear generalization of the Hayes Ch. 4 AR-model residual, and on a simple power-change fault the linear model might do just as well for far less compute — a point you quantify in Lab 8.5.
Going further
- Add the AR-residual detector (Hayes Ch. 4) as a classical baseline and compare its ROC to the autoencoder’s — find the fault type where the nonlinear model actually helps.
- Track the reconstruction error as a trend over time (a slow rise = incipient wear) instead of a hard threshold — the actual predictive-maintenance use case.
- If you used the audio proxy, redo it with a real IMU accelerometer and compare which sensor separates the fault better.
- Fuse multiple sensors (mic + IMU) into one feature vector and see whether the AUC improves.