Battery Life Estimator (UAV) — Lean, Calibrate-and-Go
Predict total and remaining flight time for your UAV from a few past flights. Simple, explainable, and fast to calibrate.
Lean, calibrate-and-go endurance modeling for multirotors or fixed-wing UAVs.
Start with 5–10 flights, fit a tiny model, and get total and remaining flight time estimates in minutes.
⚡️ Why this exists: Operators need a quick, explainable estimate—not a black box. This MVP is deliberately small and extensible.
What it does
- Calibrate from a CSV of past flights (payload, speed, wind, flight time)
- Estimate total flight time for new payload/speed/wind conditions
- Estimate remaining time using current LiPo per-cell voltage → SOC
- Explainable coefficients you can inspect, version, and compare
How it works (MVP model)
We model the inverse of flight time (minutes⁻¹) as a linear function:

- Headwind is positive (increases airspeed/power); tailwind is negative.
- Remaining time is computed as:

using a conservative LiPo voltage→SOC curve.
Good enough when airframe + mission profile are consistent. You can extend later (hover ratio, density altitude, prop/disk loading, battery aging, interactions).
Quick start
1) Prepare your CSV
Include at least 5–10 lines like:
| flight_time_min | payload_kg | ground_speed_mps | headwind_mps |
|---|---|---|---|
| 18.5 | 0.2 | 8.0 | 0.0 |
| 16.2 | 0.4 | 9.0 | 1.0 |
| 14.8 | 0.5 | 10.0 | 2.0 |
Columns (required):
flight_time_min— total flight time (minutes)payload_kg— payload mass in kilogramsground_speed_mps— ground speed (m/s) during steady cruiseheadwind_mps— positive=headwind, negative=tailwind (m/s)
Tip: Use the steady-cruise portion or mission average. Keep logs consistent across flights.
2) Calibrate the model
python battery_life_estimator.py calibrate --csv example_flights.csv --out model.json
Outputs:
- Coefficients:
b0, b1, b2 - Fit metrics: MAE (min), MAPE (%)
- Saved model:
model.json
3) Estimate total flight time
python battery_life_estimator.py estimate-total --model model.json --payload-kg 0.6 --ground-speed-mps 10 --headwind-mps 2
4) Estimate remaining time (with voltage)
python battery_life_estimator.py estimate-remaining --model model.json --payload-kg 0.6 --ground-speed-mps 10 --headwind-mps 2 --voltage-per-cell 3.85
LiPo per-cell Voltage → SOC (conservative lookup)
| V/cell | ~SOC |
|---|---|
| 4.20 | 100% |
| 4.10 | 90% |
| 4.00 | 80% |
| 3.95 | 70% |
| 3.90 | 60% |
| 3.85 | 50% |
| 3.80 | 40% |
| 3.75 | 32% |
| 3.70 | 26% |
| 3.65 | 20% |
| 3.60 | 14% |
| 3.55 | 8% |
| 3.50 | 4% |
| 3.45 | 2% |
| 3.40 | 1% |
| 3.35 | 0.5% |
| 3.30 | 0% |
⚠️ Under load, measured voltage sags. Choose a conservative landing threshold (many operators target ~3.5–3.6 V/cell under load). Adjust the curve to match your packs.
When is the MVP “good enough”?
- Your MAE ≤ ~1.5–2.0 min on validation flights of ~15–25 min
- Missions are similar (airframe, props, weather range, and throttle profile)
- You need fast, transparent estimates to aid planning
If errors are larger or conditions vary widely, consider v2 features (below).
Sensible v2 upgrades (when ready)
- Hover ratio (time near hover/throttle band) — biggest gain for multirotors
- Density altitude / air density (from temp & pressure) — seasonal & elevation shifts
- Prop/disk loading (airframe/prop changes) — robustness across configurations
- Battery aging/sag (IR or sag proxy) — stabilize estimates across pack age
- Light non-linear/interaction terms + Ridge (L2) — polish without overfit
Keep it lean: add one or two features only when metrics tell you to.
Calibration tips
- Use consistent cruise segments across flights
- Mix a few payloads and wind conditions to avoid collinearity
- Keep an out-of-sample subset (or use simple K-fold) to validate MAE/MAPE
- Re-calibrate after major changes (new props, airframe, or season)
CLI Reference
calibrate
--csv <path.csv>
--out <model.json>
estimate-total
--model <model.json>
--payload-kg <float>
--ground-speed-mps <float>
--headwind-mps <float>
estimate-remaining
--model <model.json>
--payload-kg <float>
--ground-speed-mps <float>
--headwind-mps <float>
--voltage-per-cell <float>
Design principles
- Explainable: linear on inverse time; inspect coefficients directly
- Small data friendly: works with 5–10 quality flights
- Deterministic: no randomness; same inputs → same outputs
- Extensible: drop-in features when metrics require
FAQ
- Does this handle hover-heavy missions?
The MVP targets steady cruise. If you hover a lot (inspections, photogrammetry), add hover_ratio in v2. - What about temperature or altitude?
If errors drift with seasons or elevation, add air density/density altitude. - Multirotor vs fixed-wing?
Works for both as a first-order model. Fixed-wing may benefit from a throttle% or aerodynamic term later. - How often should I recalibrate?
When coefficients or MAE/MAPE drift after hardware/season changes—or every few months as a routine.
Safety & operational notes
- Always maintain conservative reserves; this is a planning aid, not a hard guarantee.
- Validate on your airframe + packs before mission-critical use.
- Use pack-specific thresholds if aging varies across your fleet.
Roadmap
- v0.2: helpers for density altitude & hover ratio extraction
- v0.3: Ridge option with 1–2 interactions
- v0.4: pack aging proxy (IR/sag) and per-pack calibration
License
MIT — free to use, modify, and embed.
Contact
Questions or ideas? Open an issue or propose a PR in the repo.
Happy (and safe) flying! ✈️