@kariminem/dronesar-fl
flwr new @kariminem/dronesar-flMulti-Agency SAR Drone Swarm — Federated Survivor Detection
60% alone → 92% federated. Zero footage shared.
Five search-and-rescue drones, each operated by a different agency over different terrain, train a shared survivor-detection model without any agency handing over a single frame of thermal footage. Only model coefficients are exchanged, aggregated with FedAvg.
This is a federated learning problem for a reason that is easy to state and easy to measure.
Why a single drone fails
Every candidate detection is either a real survivor or a false positive, and each agency's terrain produces its own kind of false positive:
| Agency | Terrain | Its false positive | The cue that gives it away |
|---|---|---|---|
| Park Service | forest | campfire | doesn't move |
| Coast Guard | coastal | sun-heated rock | wrong thermal band |
| Fire Dept | urban edge | vehicle engine | too large |
| Mountain Rescue | alpine | geothermal vent | no human silhouette |
| Volunteer Corps | farmland | livestock | no human gait |
A real survivor scores well on all five human cues. Each false positive scores well on four and fails exactly one — a different one per agency.
So a drone that trains only on its own data learns to reject exactly one of the five. Fly it over unfamiliar ground — which is what a multi-agency search is — and it starts calling campfires survivors. On a mixed-terrain test set it scores 60%, which is just the arithmetic: it accepts all survivors and rejects one false-positive type in five.
Federating teaches every drone all five tells, without anyone sharing raw data. Same test sets, same model class: ~92%.
For reference, pooling everyone's raw data into one dataset scores 94.9%. Federation captures nearly all of the benefit while the footage never leaves the drone — which is the entire argument.
What is actually federated
client_app.py loads that drone's own sensor rows and never puts them in a Message. Only an ArrayRecord of model coefficients and a few scalar metrics go back. The privacy claim is enforced by what the file does and does not send, not by a promise in a README.
Each round, every drone reports both numbers — how the global model scores on its local test set, and what it would score training entirely alone — so the comparison is visible live rather than asserted at the end. The solo baseline is a fully-converged model on the same local data, i.e. the strongest possible "go it alone", making the comparison conservative.
A note on making FedAvg work here
The clients are pathologically non-IID by construction, and that broke the obvious approach. Letting each drone train to convergence locally and then averaging produces a model that accepts everything (~50%): each client converges on "only my one cue matters", and averaging five such models yields five weak weights against an intercept calibrated for one strong one.
What works is proper FedAvg — one local SGD epoch per round, so clients never drift that far — plus centering the cue scores so the intercept isn't doing all the work. With those two changes the accuracy climbs smoothly from 50% to ~92% over 40 rounds instead of oscillating or crawling.
Run it
uv sync uv run flwr run . supergrid --federation <your-simulation-federation> --stream
Fewer rounds for a quicker demo (the curve still reads clearly, it just lands a little lower):
uv run flwr run . --run-config 'num-server-rounds=20' --stream
The server prints a per-round accuracy line and finishes with a ##FL_RESULT## JSON summary containing the accuracy trajectory and the scored candidate detections, which is what the companion swarm-sar-commander AgentApp consumes to draft a rescue dispatch plan for a human to approve.
Honest scope
The sensor data is synthetic and deliberately constructed so the non-IID effect is legible. The point being demonstrated is the mechanism — that agencies with differently-biased local data get materially better together without pooling it — not a validated survivor-detection model. Real thermal SAR data would need real validation.