Skip to content

Sampling model predictive control slalom

nexus_uav/examples/controllers/sampling_mpc/obstacle_slalom.py flies the astro-max through an obstacle slalom with a sampling + gradient differentiable-simulation MPC, the framework port of NVIDIA's example_diffsim_drone.

  • The regime where differentiable simulation earns its keep: the obstacle-avoidance cost is a signed-distance field evaluated directly from the pillar geometry. That's trivial to differentiate through the simulator, but impractical to hand to an analytic Nonlinear Model Predictive Control (NMPC) solver. The landscape is non-convex: go left or right around a pillar. That's why it samples.
  • The method, mirroring NVIDIA: each control tick samples 16 noisy control trajectories around the nominal and evaluates them in parallel as a batched differentiable rollout. It refines every one by back-propagating the obstacle-aware cost through SolverSemiImplicit with Adam, and keeps the lowest-cost trajectory. Every step is CUDA-graph-captured.
  • The task: three waypoints staggered into a zigzag, with a pillar mid-way along each leg. The active waypoint advances once the drone reaches it, and the recording ends 2 s after the goal.
  • The diffsim-sampling counterpart to the analytic acados NMPC.

The drone weaves through the three pillars, advancing waypoint to waypoint: green start, gold waypoints, red goal. The orange MPC-horizon prediction updates on each re-plan and the blue trail grows behind it. A blank viewer means the recording isn't uploaded yet. Run scripts/ci/evaluate_examples.py --upload.

Key stats

Configuration:

Metric Value
Method 16 batched differentiable rollouts, sample → gradient-refine → pick the lowest cost
Collision cost signed-distance field from the pillar geometry
Horizon 80 steps of 10 ms, about 0.8 s, with 5 control knots

CI-measured on the pinned runner, see Benchmarking:

Metric Value
reached 3
final_dist_m 0.002 m
min_clearance_m 0.938 m
max_tilt_deg 35.0°
path_wiggle 1.258
nose_err_deg 16.1°
rtf 0.30×
ape_trans_rmse_m 0.606 m
ape_trans_max_m 1.167 m
final_err_m 0.002 m

Measured on NVIDIA A10G · 2026-08-16 · commit b7d6504465c6

Run it

uv run -m nexus_uav.examples sampling_mpc            # run + assert it flies the slalom
uv run -m nexus_uav.examples sampling_mpc   # flies + asserts + writes the .rrd

Needs a CUDA device for the batched rollout and the graph-captured optimization.