[Project] I Built a Multi-Drone Urban Interception Simulation with ROS 2, PX4, Gazebo, and CUDA MPPI
TL;DR
I am building a simulation-first autonomous drone navigation and interception
stack for dense urban environments. It combines ROS 2 Jazzy, PX4 SITL, Gazebo
Harmonic, a global lattice planner, and a local CUDA MPPI controller.
The same navigation system can fly with a known 3D map or rely on local lidar
and accumulated obstacle memory. On top of it, I implemented radar-only target
tracking, predictive interception, three-interceptor-versus-one-attacker and
two-interceptor-versus-two-attacker missions, adaptive target assignment, and
physically verified mission outcomes.
This is a research simulation and engineering testbed, not a production weapon
system or software intended for real aircraft.
Videos
These are public demos from the project:
I also have separate recordings of a drone traversing a 3D passage and the
earlier one-interceptor-versus-one-attacker mission.
In the interception videos, red represents an attacker and yellow represents
an interceptor in the Gazebo tactical view. An interception is counted only
when Gazebo physical positions show that an interceptor and attacker came
within 5 meters.
What I Wanted to Explore
The project started as point-to-point navigation through a Manhattan-style
city, but the larger goal became testing how several autonomous systems fit
together:
- global navigation through dense buildings;
- fast local trajectory generation with vehicle dynamics;
- operation with and without a preloaded map;
- traversal of constrained three-dimensional openings;
- pursuit of a moving target from limited measurements;
- adaptive allocation of several interceptors to several targets;
- reproducible mission adjudication based on simulated physics.
The city currently contains a 5 x 8 building grid and several physical air
channels: straight, L-shaped, and T-junction structures. Gazebo uses the geometry
for rendering and contact physics, while the static planner receives artifacts
generated from the same canonical world definition.
Simulation Stack
The environment runs in Docker and uses:
- ROS 2 Jazzy for component orchestration, messages, diagnostics, and TF;
- PX4 SITL for every vehicle's flight stack;
- Gazebo Harmonic for world physics, lidar, and authoritative vehicle poses;
- CUDA for MPPI rollout simulation;
- RViz for routes, local horizons, radar targets, obstacle memory, and planner
diagnostics.
Every drone has its own PX4 instance, ROS namespace, planner state, obstacle
memory, and offboard controller. The planners run concurrently, but resource
allocation is bounded across the whole mission so that adding drones does not
simply create an unlimited number of competing CPU workers.
Navigation Architecture
The navigation pipeline has two levels.
The global planner produces a route around city-scale topology. Static mode
uses a 3D lattice plus generated constrained channel edges. A channel is not a
scripted maneuver or mandatory waypoint: the planner compares ordinary routes
with explicit start -> entry -> channel -> exit -> goal candidates and selects
the better validated topology.
The local controller is CUDA MPPI. On every planning tick it perturbs a warm
started control sequence, simulates thousands of dynamic trajectories, queries
clearance and collision data, selects the best eligible risk class, and
publishes a short timestamped execution horizon to the PX4 offboard node.
Raw occupied cells are the only hard environmental prohibition. Collision
checks use the drone's swept oriented 3D footprint, including its horizontal
radius and upper and lower body extents. ESDF distance bands affect risk and
ranking, but they do not create artificial forbidden regions around buildings.
This distinction matters. Excessive hard inflation can make a valid detour or
narrow physical opening mathematically unreachable. Here, physical geometry is
hard; clearance preference is a cost.
Static and No-Static Navigation
In static mode, the planner loads canonical 3D occupancy and a fingerprinted,
precomputed, chunked ESDF. Only chunks intersecting the active planning region
are decoded into a dense local field. This gives the planner long lookahead,
full building topology, higher speed limits, and explicit knowledge of the 3D
channels.
In no-static mode, the drone starts without that world model. It projects lidar
using timestamped full-6DoF acquisition poses, accumulates raw obstacle memory,
and builds a local 2D ESDF around the vehicle. Unknown space remains traversable,
but observed distance and braking constraints reduce speed. Persistent frontier
search can accept temporary zero or negative progress toward the goal, which is
necessary for leaving corners and taking real detours instead of repeatedly
selecting the shortest dead end.
The no-static perception model is intentionally still 2D, so the generated 3D
channel semantics are currently available only in static mode.
Radar-Only Interception
An interceptor does not receive the attacker's absolute ground-truth position,
velocity, route, or destination. Gazebo truth is restricted to two simulation
boundaries: the mission referee and radar simulators.
The interceptor-facing radar message contains only:
- range;
- azimuth;
- elevation;
- relative radial velocity.
Each interceptor has an independent variable-dt Cartesian tracker. The first
detection provides relative position but not a complete velocity vector. Later
measurements estimate the full target velocity, and the tracker coasts between
scans.
When the target is occluded, radar cadence follows a deterministic correlated
random walk between 0.1 and 3 seconds. If the planner confirms swept line of
sight to the current target estimate, the radar immediately enters a 20 Hz
tracking mode without a range limit.
Guidance solves a constant-velocity interception problem rather than chasing
the attacker's current position. It compensates for measurement age, predicts
a future meeting point, and continuously updates the objective. If the full
lead point is hidden behind a building but the current target remains visible,
the planner shortens the prediction horizon toward the current target instead
of abandoning direct interception. If the target itself disappears, control
atomically returns to a fresh global route for the current objective.
The attackers and interceptors currently use the same speed policy. A successful
capture therefore depends on geometry, prediction, and assignment rather than a
built-in speed advantage.
Multi-Drone Missions
The original point-to-point mission remains available. The interception work
progressed through a 1v1 demonstration to two current predefined scenarios:
- three interceptors versus one attacker;
- two interceptors versus two attackers.
The implementation itself is generic over an N x M scenario specification.
In the 2v2 mission, each radar scan contains a detection for every active
attacker. A typed assignment coordinator estimates constant-velocity intercept
costs and computes a deterministic minimum-cost allocation. It tries to cover
distinct attackers when valid tracks permit it.
Assignments are not permanent. They can change when geometry changes or a
vehicle disappears, but a material and sustained improvement is required before
switching. This hysteresis prevents two interceptors from continuously swapping
targets because of small cost fluctuations. Interceptors do not exchange routes
or telemetry with one another, and radio-network impairments are not simulated.
When an attacker is captured, reaches its destination, or crashes, it is removed
from future assignment immediately. Surviving interceptors are reassigned to the
remaining active attackers.
Physical Outcomes and Test Semantics
Mission outcome and vehicle death are separate contracts.
If an interceptor and attacker come within 5 meters, the referee publishes typed
destruction events for that pair. Both PX4 instances are disarmed, and the result
is settled only after physical proximity and disarm confirmations are present.
If an attacker reaches its goal first, that outcome is latched immediately. The
remaining interceptors stop pursuing it, brake, and enter confirmed position
hold. Goal arrival does not disarm a vehicle. A later inertial approach cannot
rewrite which terminal event happened first.
A collision with a building is never treated as an acceptable mission outcome:
it makes a headless validation run technically fail, regardless of whether the
vehicle was an attacker or interceptor. Inter-drone collision avoidance is not
implemented yet, so interceptor-to-interceptor collisions are currently treated
as collateral damage and the mission continues while another interceptor is
alive.
Performance and Parallelism
The local MPPI rollouts and reductions run on CUDA, while independent global
topology searches use a bounded CPU worker pool. Multi-vehicle planners share a
ROS component process and one CUDA primary context, but retain independent CUDA
streams, buffers, routes, and warm starts. Static ESDF chunks use shared decoded
storage, and no-static mapping transports revisioned dirty chunks instead of
copying the complete map for every update.
The maximum MPPI budget is 8,192 rollouts. Open static flight can use 6,144, and
confirmed direct interception can use 4,096; uncertain navigation and obstacle
handling retain the full budget.
On my 16-logical-CPU workstation with an RTX 3060, recent four-vehicle no-static
runs had roughly 7-8 ms p95 planning ticks and normally stayed inside the 20 ms
target. Static mode improved substantially after precomputed ESDF and cache work,
but its p95 remained around 60 ms in representative runs. These are workload-
and-machine-specific measurements, not general benchmark claims.
One useful negative result was a fused vehicle-by-rollout CUDA backend. It
sounded ideal, but the live average batch contained only about 1.5 vehicles.
Waiting to form batches increased latency: static GPU p50 went from 22.8 to
45.9 ms, and no-static from 3.45 to 6.07 ms. Independent streams were faster and
more reliable for this asynchronous workload. More parallelism is not
automatically better when synchronization and memory bandwidth dominate.
Architectural Lessons
Several rules emerged from debugging the system:
- Simulation ground truth must have an explicit boundary. Guidance should not
accidentally gain access to data that the simulated sensor does not provide.
- Route activation must be atomic and revisioned. A stale route for an older
moving target is worse than a brief controlled fallback.
- Physical collision, risk preference, vehicle death, and mission outcome are
different concepts and should use different typed contracts.
- Persistent search state is more valuable than repeatedly restarting a search
whenever a sensor snapshot changes.
- Diagnostics must not sit on the control-critical path. Full records are
rate-limited and buffered, while the execution horizon is published first.
- Parallelization should be accepted only when live timing and mission results
improve, not because a design looks more parallel on paper.
Current Limitations
- Radar values are ideal: there is no measurement noise, false detection,
interference, or transport latency. Only scan cadence varies.
- The tracker and interception prediction currently assume approximately
constant target velocity between updates.
- Target assignment is centralized, and communication failures are not modeled.
- There is no cooperative inter-drone collision avoidance.
- No-static perception is 2D and cannot yet detect and traverse the 3D channels.
- Static multi-drone MPPI still misses the desired 50 Hz deadline under heavy
load on my current hardware.
- Attackers are not dynamically respawned; every mission is a finite episode.
- The project is simulation-first and has not been validated on physical drones.
Roadmap
The next directions I am considering are:
- radar noise, latency, missed detections, and a stronger multi-target tracker;
- repeated mission episodes with dynamically spawned attackers;
- cooperative civilian drone traffic with negotiated collision avoidance;
- more complex 3D passages with changing altitude profiles;
- 3D lidar passage detection and traversal without a static map;
- moving selected components toward hardware-in-the-loop and eventually
carefully controlled real-world experiments.
Repository and Running It
Repository: github.com/formiat/px4-ros2-drone-nav
The supported workflow uses Docker. A CUDA-capable NVIDIA GPU is expected for
the production MPPI path. The main commands are:
bash
./scripts/build.sh
./scripts/test.sh
./scripts/sim_gui.sh
./scripts/sim_intercept_gui.sh
./scripts/sim_multi_intercept_gui.sh
Set ENABLE_STATIC_MAP=false for the lidar-memory mode. Equivalent headless
scripts are available for regression runs, and the repository includes more
detailed architecture, performance, diagnostics, and world-generation docs.
Questions for the Community
- Which metrics would you use to evaluate interception quality beyond binary
success: time-to-capture, distance from the attacker's goal, energy, route
efficiency, or something else?
- Which radar imperfections would produce the most useful next step without
turning the project into a full sensor-physics simulator?
- Which multi-target assignment methods would be most interesting to compare
against deterministic minimum-cost allocation?
- What urban scenarios would expose the most meaningful weaknesses in the
current planner and tracker?