Skip to main content

Battery health

The drone agent watches every battery pack the flight controller reports and turns the raw BATTERY_STATUS stream into a health view: per-cell voltages with the weakest cell called out, pack voltage and current, consumed capacity and energy, temperature, a time-to-reserve estimate and a set of anomaly rules. The engine runs inside ados-control on the drone, so the same result reaches Mission Control, the on-box dashboard and the REST API. Battery health is a drone profile feature. It needs a flight controller that sends BATTERY_STATUS (ArduPilot and PX4 both do). The agent advertises it with the battery.health capability.

How it works

Every 500 ms the engine reads the latest vehicle state. A new sample is ingested only when the state actually changed, and nothing is ingested while the flight controller link is down. Each pack is tracked by its MAVLink battery id. Up to 8 distinct packs are tracked; a ninth new id is ignored. When the flight controller sends no BATTERY_STATUS at all, the engine builds pack 0 from the SYS_STATUS battery fields. For each pack the engine keeps up to 300 seconds of samples (600 at 2 Hz), the live anomaly set and the last 100 raise and clear events.

Cells and pack voltage

A cell array is plausible when it is non-empty and every value is between 2.5 V and 4.5 V. Some flight controllers put the whole pack voltage into the first cell slot (a 4S pack shows up as a single 16.8 V “cell”). That array is implausible, so for that sample:
  • no cell rule runs (cell_critical, cell_low, cell_divergence);
  • the pack reports cells_plausible: false;
  • min_cell_v, max_cell_v, divergence_mv and weakest_cell_index are null.
Pack voltage (voltage_v) is chosen in this order:
  1. The sum of the cells, when the cells are plausible.
  2. The single reported value, when exactly one value is present.
  3. For pack 0 only, the SYS_STATUS battery voltage.
When none of these applies, voltage_v is null. For pack 0, remaining_pct falls back to the SYS_STATUS remaining value when the pack itself does not report one.

Anomaly rules

The rules run on every new sample, in this order. dt is the time since the previous sample of the same pack. Each anomaly carries the measured value and the threshold it was compared with, in the same unit:

Clearing

An anomaly clears with a 5 second hold. When a rule stops firing, the anomaly stays in the live list with cleared_at_ms set. If the rule fires again inside 5 seconds the stamp is removed and the anomaly carries on as the same event. If 5 seconds pass with no re-fire, the anomaly is removed and a cleared entry is added to the history. This stops a pack that sits right on a threshold from flapping.

Time to reserve

The prediction uses the samples inside the last predictive_window_s seconds. It needs at least 2 samples spanning at least 1 second; otherwise the state is idle with every number null. The engine computes the drop rate in percent per second from the remaining-capacity change across the window: eta_s is (remaining - reserve) / drop rate, rounded to whole seconds. mean_current_a is the mean absolute current over the same window.

Configuration

The battery: block in /etc/ados/config.yaml holds the thresholds. Every value is an integer. A value outside its range, or a critical_cell_mv that is not below low_cell_mv, falls back to its default and the agent logs a warning.
The engine checks the config file every tick and reloads the block when the file changes, so a threshold edit takes effect without a restart.

Where to see it

Mission Control

Open the drone, switch to the Agent tab, and pick Battery under the Node section. The page has two views:
  • Live: one card per pack with the cell bar (weakest cell highlighted), voltage, current, consumed mAh and Wh, temperature, the time-to-reserve strip, active anomalies and recent history. A pack with implausible cells shows “Per-cell data not reported by the FC” in place of the cell bar.
  • Setup: the threshold fields from the table above.
A newly raised anomaly also shows a toast (critical anomalies as an error, warnings as a warning), and while a recording is running for that drone it drops a marker on the recording timeline.

On-box dashboard

On the drone’s own web dashboard, open Settings and pick Battery (listed under System and safety, drone profile only). It shows the same live pack cards and the threshold fields, and saves to the same battery: block.

REST API

The route always answers 200. Example response:
Top-level fields: Pack fields:

Log events

Every raise and final clear is written to the node log as a battery.anomaly event from ados-control, with the fields rule, pack_id, state (raised or cleared), value and threshold. A raised critical anomaly logs at error, a raised warning at warn, and a clear at info. With the Black Box store turned on, query them with:

See also