Flight Log Anomaly Detector (UAV) — Hampel + Optional STL

A lean, explainable tool to flag battery voltage spikes, GPS jumps, and IMU outliers in your UAV telemetry. CSV in → anomalies JSON out.

Flight Log Anomaly Detector (UAV) — Hampel + Optional STL
Photo by Hacı Elmas / Unsplash

A lean and explainable detector that flags weird events in UAV flight logs: battery voltage spikes, GPS drift/jumps, and IMU outliers. Perfect for quick maintenance triage, post-flight QA, and demos.

Why this exists: you shouldn’t need a black box to spot obvious problems. This tool favors robust statistics, small dependencies, and clear reasons for each flag.

What it does

  • Hampel filter (rolling median + MAD) on numeric telemetry columns.
  • Optional STL decomposition to remove trend/seasonality before Hampel.
  • GPS jump detector comparing per-sample displacement against speed-based allowance.
  • Writes a single anomalies.json list with timestamps, columns, values, and reasons.

How it works (plain English)

  • Hampel filter: for each sample, compare the value to the median of its window. If the deviation exceeds n_sigma × 1.4826 × MAD, it’s flagged.
    • Robust to spikes and drift; much less sensitive to outliers than mean/std.
  • STL (optional): decompose a series into trend + seasonal + residual, then run Hampel on the residual only. Helpful for voltage sag curves or periodic speed patterns.
  • GPS jumps: compute Haversine distance between consecutive lat/lon, and allow up to gps_alpha × speed × dt + gps_margin_m. Exceed that? It’s a jump.

📦 Download

Contents:

  • flight_log_anomaly_detector.py — CLI tool
  • example_log.csv — synthetic 1 Hz, 10-minute log with planted anomalies
  • requirements.txt — pandasnumpystatsmodels
  • README.md — usage & notes

Quick start

# optional: create & activate a virtual env
python -m venv .venv && source .venv/bin/activate    # Windows: .venv\Scriptsctivate

pip install -r requirements.txt

# run the detector on the example data
python flight_log_anomaly_detector.py detect   --csv example_log.csv   --time-col timestamp   --cols batt_voltage_v,gps_speed_mps,imu_ax,imu_ay,imu_az   --out anomalies.json   --k 7 --n-sigma 3.0   --stl-cols batt_voltage_v --stl-period 60   --gps-jump 1 --gps-alpha 2.0 --gps-margin-m 30

You’ll get anomalies.json plus a summary in the console.

Expected CSV columns

Required

  • timestamp — ISO 8601 or parseable datetime at uniform frequency (e.g., 1 Hz)

Common numeric columns

  • batt_voltage_v — battery voltage (V)
  • gps_speed_mps — ground speed (m/s)
  • imu_ax, imu_ay, imu_az — accelerometer (m/s²) or normalized g
  • lat, lon — needed for GPS jump detection

Use the --cols flag to list which numeric columns to scan with Hampel.

CLI options (most used)

--csv <path.csv>            Input telemetry CSV
--out <anomalies.json>      Output JSON file
--time-col <name>           Timestamp column (ISO/parseable)
--cols <c1,c2,...>          Numeric columns to Hampel-scan (comma-separated)
--k <int>                   Hampel half-window (neighbors per side), default 7
--n-sigma <float>           MAD threshold, default 3.0
--stl-cols <c1,c2,...>      Columns to detrend/seasonal-remove via STL before Hampel
--stl-period <int>          Seasonal period (in samples), e.g., 60 for 1 Hz × 1 minute
--gps-jump <0|1>            Enable GPS jump detector (needs lat, lon, gps_speed_mps)
--gps-alpha <float>         Multiplier on speed×dt (default 2.0)
--gps-margin-m <float>      Extra fixed margin in meters (default 30)

Tuning tips

  • Start with --k 7 --n-sigma 3.0.
  • If you get too many flags, increase n-sigma or k.
  • If voltage has a strong curve, add --stl-cols batt_voltage_v --stl-period <your-period>.

Output format (anomalies.json)

Each anomaly is a small object:

[
  {
    "index": 120,
    "timestamp": "2025-01-01T12:00:10+00:00",
    "column": "batt_voltage_v",
    "value": 15.9,
    "reason": "hampel",
    "residual": -0.8
  },
  {
    "index": 300,
    "timestamp": "2025-01-01T12:05:00+00:00",
    "column": "gps",
    "value": 185.3,
    "reason": "gps_jump",
    "distance_m": 185.3,
    "allowed_m": 70.1
  }
]

Fields

  • index — row index in your CSV
  • timestamp — ISO time from --time-col
  • column — source column (gps for jumps, otherwise your numeric column)
  • reasonhampel or gps_jump
  • value — original value at that row (for Hampel)
  • residual — present when STL was applied to that column
  • distance_m, allowed_m — present for gps_jump

What it catches (examples)

  • Battery voltage spikes/dips: loose connector, bad pack cell, power transient
  • GPS jumps: poor lock, multipath, sudden coordinate glitch
  • IMU outliers: vibration bursts, impacts, sudden bias shifts

It’s triage, not a verdict. Review in your log viewer and consider context.

Integration ideas

  • CI check for flight logs in a repo; fail builds if anomalies > N
  • Webhook: push anomalies.json to a dashboard or Slack channel
  • CSV annotator: join flags back onto the CSV for quick visualization in charts

Validation & calibration

  • Run on a few clean flights to establish baseline false positive rate
  • Keep a labelled set of known issues to sanity-check recall
  • For seasonal/periodic behavior (e.g., patterned speed), try STL residuals

Roadmap

  • Optional annotated CSV writer (.csv with per-row flags)
  • Tiny HTML plot to visualize spikes and jumps
  • Per-column thresholds via config file (YAML/JSON)
  • Simple REST microservice wrapper for batch jobs

License

MIT — free to use, modify, and embed in your pipelines.

Questions?

Open an issue/PR or ping us — and if you ship something cool with it, tell us! 🚀