Decentralized multi-drone autonomy for GPS-denied urban search & pursuit.
A swarm of quadrotors that explore an unknown city, build a shared map over a
bandwidth-limited link, and cooperatively intercept a fleeing target — with
no central node, each drone running onboard VIO → mapping → planning → control.
Related: flowswarm — the algorithms-from-scratch companion (pure-Python ORCA / A* / GNN-PPO, no ROS) for the multi-robot coordination ideas used here.
Decentralized cooperative pursuit in the headless swarm simulator — pursuers allocate
containment slots (CBBA), track the fleeing evader through a bandwidth-limited radio link,
and corner it with no central coordinator. Reproducible with sim/run_sim.py.
The same scenario in the full stack — two PX4 SITL x500 quadrotors close on the fleeing
evader (red) in the Gazebo Harmonic city world, detecting it with their downward cameras and
coordinating through the gated comms layer. Live capture;
sim/scripts/run_px4_swarm.sh runs it.
Swarm Autonomy is my simulation-grade research platform for decentralized multi-UAV autonomy. Every drone runs the same onboard stack — visual-inertial odometry, occupancy/ESDF mapping, motion planning, and control — and the swarm coordinates purely peer-to-peer over a modeled, bandwidth-limited radio. I built it by integrating proven open-source components (OpenVINS, nvblox, ego-planner-swarm, RACER, CBBA) around two cores of my own: a single inter-drone comms choke point that makes "bandwidth-limited radio" a measured quantity, and a coordination + pursuit layer for decentralized role allocation and target interception.
I separate algorithmic cores (pure, dependency-free, unit-tested Python) from ROS 2 node wrappers, so the coordination, comms, mapping, and planning logic is verified by 77 unit tests and reproduced by headless benchmark scripts that need no GPU and no manual piloting.
| Layer | What runs here | Origin |
|---|---|---|
| Comms middleware + link model | range/rate/dropout gating, bandwidth metering | mine |
| Coordination & pursuit | CBBA allocator, containment/interception geometry, MPC guidance, Kalman target tracker, reciprocal avoidance | mine (CBBA implements Choi et al. 2009) |
| Mapping & planning (CPU) | occupancy + ESDF, A* + elastic-band optimizer | mine, following the ego-planner formulation |
| Cooperative exploration | frontier detection + claim sharing over the gated link | mine, modelled on RACER |
| Headless swarm simulator & benchmarks | deterministic evaluation of all of the above | mine |
| Visual-inertial odometry | stereo VIO | OpenVINS (vendored) |
| GPU mapping backend | interface-compatible ESDF drop-in | nvblox (planned swap-in) |
| Flight stack & world | vehicle dynamics, EKF2, rendering | PX4 SITL + Gazebo Harmonic |
- GPS-denied flight on stereo VIO — I diagnosed and fixed a vision/state-estimation handover divergence; external-vision error held to 0.26 m mean on VIO alone.
- Cooperative exploration that scales — ×ばつ faster city coverage with 3 drones vs. solo, and a measured +27% penalty when the radio is throttled (coordination is comms-bound).
- Vision-only cooperative pursuit — onboard camera detection + decentralized CBBA role allocation corner a fleeing evader with no shared ground-truth target feed.
- Optimal control — my condensed-QP model-predictive controller tracks a noisy target estimate ×ばつ smoother than a reactive baseline.
- Navigation among buildings — a CPU ESDF + ego-planner-style optimizer keeps +0.9 m clearance where straight-line motion would pass 3.6 m through a building.
- Scales to 20 agents — coverage time falls 59 s → 8.4 s from 1 → 20 drones (headless sim) with the busiest comms link under 1% of its cap, and the saturation point is measured and reported, not hidden.
All figures are produced by deterministic, headless scripts under experiments/:
experiments/make_figures.sh regenerates the core set in under a
minute, and python3 sim/scaling.py reruns the swarm-size sweep (≈10 minutes). Full methodology,
metric definitions, and ablations are in docs/benchmarks.md.
| Capability | Metric | Baseline | Swarm Autonomy | Figure |
|---|---|---|---|---|
| GPS-denied VIO handover | EKF2↔vision error (mean) | mono diverges | 0.26 m | vio_stereo_handover.png |
| Cooperative exploration | time to 90% city coverage | 36.6 s (solo) | 15.6 s (3 drones, ×ばつ) | coop_exploration.png |
| Comms sensitivity | coverage time, throttled radio | 15.6 s | 19.8 s (+27%) | coop_exploration.png |
| Navigation among buildings | min. obstacle clearance | −3.6 m (straight line) | +0.9 m | plan_among_buildings.png |
| Pursuit guidance | command jitter (lower = smoother) | 0.91 (reactive) | 0.34 (MPC, ×ばつ) | control_compare.png |
| Swarm-size scaling (headless) | time to 90% coverage | 59.3 s (solo) | 8.4 s (20 drones) | scaling_vs_n.png |
Monocular VIO under-scales the trajectory by roughly ×ばつ when flight excitation is low (scale is unobservable), so the GPS→VIO handover diverges. A stereo camera restores metric scale, and a trajectory-fit (Umeyama) alignment between the VIO and EKF2 frames cuts the fused external-vision error to 0.26 m mean / 0.70 m max. The drone then holds a stable 5 m circle on vision only.
Drones sense with occlusion (line-of-sight ray-casting), maintain per-drone occupancy beliefs, and
share map deltas through the gated swarm_autonomy_comms link; a decentralized nearest-frontier rule
divides the city. Time to 90% coverage drops from 36.6 s (solo) to 15.6 s (3 drones), and a
throttled radio costs +27% — the shared map only accelerates coverage when the link can carry it.
A CPU Euclidean Signed Distance Field plus an A* front-end / ESDF-gradient elastic-band back-end (the ego-planner formulation, on CPU) routes through the city. Straight-line motion passes 3.6 m inside buildings; the planner holds +0.9 m clearance and stays smooth.
Tracking a noisy camera estimate of a maneuvering target, the condensed-QP MPC produces ×ばつ smoother velocity commands (jitter 0.91 → 0.34) with fewer reversals and lower tracking error than a tuned reactive lead-pursuit law.
The headless simulator sweeps swarm size N ∈ {1...20} (six seeded maps per point): coverage time falls 59 s → 8.4 s and saturates past N ≈ 12 — a measured sizing limit (the map runs out before the coordination does). Total gossip grows super-linearly to 133 kB/s at N = 20 while the busiest single link stays under 1 % of its cap — decentralized sharing scales because no link carries the aggregate. Capture is 100 % from N ≥ 3. The same stack, unchanged, completes a 2 m 20 s explore→detect→capture mission on a 100 m city. Scope: headless sim scales to 20 agents; the PX4 SITL demos fly 2–3 vehicles. Full details in docs/benchmarks.md.
[Gazebo: camera + IMU] → [OpenVINS VIO] → [VIO→EKF2 bridge] → PX4 EKF2 (GPS off)
│ │ state
▼ ▼
[ESDF / occupancy mapping] ──────────── [map merge] ◀── neighbour deltas ┐
│ local map │ shared map │
▼ ▼ │ all inter-drone
[ego-planner / RACER] ◀── goal/role ── [coordination: CBBA] ◀── bids ────┤ traffic passes
│ trajectory ▲さんかく │ through the
▼ [pursuit] ◀── target obs ────────────┤ comms middleware
[controller] → PX4 offboard ▲さんかく │ (range/rate/
[target detection] ┘ dropout + logging)
Two architectural principles run through the codebase:
- One comms choke point. All inter-drone traffic flows through
swarm_autonomy_comms, which gates on range, rate, and dropout and logs bandwidth — making the bandwidth limit a measured quantity rather than an assumption. The headless simulator gates every message type in-process; the ROS middleware brokers all four traffic classes (pose, target, map delta, task bid) from one topic registry. - Pure cores, thin nodes. Each algorithm (link model, CBBA, pursuit geometry, ESDF, planner, PID, Kalman tracker, ORCA-style avoidance) lives in a dependency-free module with plain-pytest tests; these cores drive the headless sim and the experiments directly. The logic is verified without a simulator in the loop — 77 unit tests.
Implementation status. The validated, demonstrated capabilities — GPS-denied VIO↔EKF2 handover, camera-guided pursuit, cooperative exploration, planning among buildings, and every benchmark — run through the headless simulator and the standalone
sim/scripts/*(PX4 SITL + Gazebo). The ROS 2 node graph integrates the same cores: the comms middleware brokers all topic types, the map-merge node fuses neighbour deltas into the planning grid, the target detector runs the shared blob/back-projection core, the VIO→EKF2 bridge applies the validated trajectory-fit alignment with capture-time stamping, and the offboard node follows planner routes through the tested PID + frame helpers. End-to-end ROS-graph flight has not yet been re-validated live, so the proven flight path remains the standalone scripts. Claims are scoped accordingly.
See docs/architecture.md for the full system design.
| Path | Contents |
|---|---|
ros2_ws/src/swarm_autonomy_msgs |
Shared message interfaces (poses, map deltas, bids, target observations, comms stats, roles). |
ros2_ws/src/swarm_autonomy_comms |
Range/rate/dropout link model + bandwidth-logging middleware. |
ros2_ws/src/swarm_autonomy_coordination |
CBBA role allocation, pursuit/interception geometry, MPC guidance, Kalman target tracker. |
ros2_ws/src/swarm_autonomy_control |
PX4 offboard PID position controller. |
ros2_ws/src/swarm_autonomy_perception |
OpenVINS bringup, VIO→EKF2 bridge, target detection. |
ros2_ws/src/swarm_autonomy_mapping |
CPU ESDF + occupancy grid, frontier detection, map-delta serialization, shared-map merge. |
ros2_ws/src/swarm_autonomy_planning |
CPU A* + ESDF-gradient elastic-band planner; ego-planner-swarm / RACER integration shim. |
ros2_ws/src/swarm_autonomy_bringup |
Launch files, per-drone namespacing, parameters. |
sim/ |
PX4 SITL + Gazebo worlds and scripts, plus a ROS-free headless swarm simulator. |
experiments/ |
Deterministic benchmark scripts → result figures. |
docs/ |
Architecture, benchmarks, design decisions, engineering notes, sim-to-real. |
The pure-Python cores and all result figures run with no GPU, no ROS, and no simulator:
# Clone, then regenerate every benchmark figure (headless, < 1 min) pip install -r requirements.txt ./experiments/make_figures.sh # writes experiments/plots/*.png # Run the unit-tested algorithmic cores python3 -m pytest -q ros2_ws/src/*/test sim/swarm_sim/test
Full ROS 2 + PX4 SITL flight (requires scripts/setup.sh, which vendors PX4, the uXRCE-DDS
agent, OpenVINS, and the planners):
source /opt/ros/jazzy/setup.bash cd ros2_ws && colcon build --symlink-install && source install/setup.bash sim/launch_sim.sh # PX4 SITL + Gazebo + XRCE agent ros2 launch swarm_autonomy_bringup multi_drone.launch.py num_drones:=3
| Document | Contents |
|---|---|
| docs/architecture.md | System architecture, the comms choke point, the pure-core/thin-node pattern, data flow. |
| docs/benchmarks.md | Evaluation methodology, metric definitions, full results, and ablations. |
| docs/engineering-notes.md | Technical deep-dives: the stereo-VIO handover fix, the pursuit-perception investigation, and hardening live pursuit under occlusion. |
| docs/design-decisions.md | Rationale behind the major component and architecture choices. |
| docs/TROUBLESHOOTING.md | PX4 SITL multi-vehicle + Gazebo Harmonic field notes: symptom → cause → fix for the failures that don't print errors. |
| docs/sim-to-real.md | The hardware path: what transfers, what breaks, BOM and timeline. |
Mapping (nvblox ESDF) and the RL extension target a 12 GB+ CUDA GPU. Swarm Autonomy ships a fully
unit-tested CPU implementation of the ESDF and planner so the mapping and exploration results
above run on any machine; the nvblox GPU backend drops in behind the same map_merge_node
interface. See docs/design-decisions.md for the GPU-budget rationale.
OpenVINS · nvblox · ego-planner-swarm · RACER · FUEL · CBBA (Choi, Brunet & How, IEEE T-RO 2009) · PX4 + ROS 2 + Gazebo
Released under the MIT License.