November 6, 2026
Weight-Space Programming for Drones: Editing PX4's Neural Flight Controller Without Training
RAPTOR is the small recurrent controller that ships in PX4. I gave its weights, the vehicle physics and a recording of its behaviour to Claude Opus and asked it to explain what it saw and change the weights so the behaviour changed. No training, no gradients, every edit judged on starts the model never flew. A showcase of what came out, with the caveats attached.
At the start of November, MIT CSAIL published Sparks of Weight-Space Programming (Takehi, Teoh, Zhang and Khattab), in which a language model repaired a stalled car policy by reading its weights and editing them directly, and did the same for a looping language model. I work with drones and definetly wanted to give it a try to see if this is something I could use to hopefully fly a drone one day.
So the question here is narrow. Take the weights of a flight controller that has a release tag. Fly it in the simulator it was trained in. Pick a behaviour you can see and describe. Hand the weights, the physics and a recording to a model and ask it to change the weights so the behaviour changes. Then check, on starts the model never saw, whether it did and whether anything else got worse.
The trained model
RAPTOR (Eschmann et al., 2025) is one small recurrent network, a GRU with 16 units and 2,084 weights, trained to fly quadrotors from palm-sized to several kilograms without retuning. PX4 ships it as the experimental mc_raptor module. The weights operated on here are the checkpoint PX4 pins for that module. Throughout, the original RAPTOR weights are that checkpoint untouched, and the merged controller is the same network after the eleven edits described below, the output of this tool.
The network, in one figure
- It sees its own state relative to the reference: position error clipped to half a metre per axis, attitude as the full rotation matrix, velocity error clipped to a metre per second, body rates straight from the gyro, and its own output from the last step.
- It does not see absolute position, reference velocity, battery, motor speed, a map or a camera. Every trajectory looks like "where I am relative to where I should be right now".
- It commands motors directly. One value per motor from minus one to one, which PX4 maps to throttle and hands to the mixer. There is no attitude or rate loop underneath.
- The clips are why the fast eight fails. At the 3.5 s period the position error saturates, so the policy cannot tell a half-metre lag from a two-metre one. That is the wide orbit in the clip further down.
It is a good controller. It also has habits. On a 5-inch racing quad, the Flightmare racer from the rl-tools vehicle registry, which sits outside its training range, it hovers a hand's width above the setpoint and overshoots a step climb badly. On the 30 g Crazyflie it hovers a little low and drops a fraction of its recoveries from a tumbled release. On the 2 kg x500, PX4's own development quad, it is well behaved until pushed. Nobody injected these. They are what the original RAPTOR weights do, and they are the kind of thing a control engineer would normally go after with a gain table.
The loop
Every repair runs the same five steps.
Nobody told the model how to work, and in every run it did roughly the same thing. It rebuilt the controller and the vehicle dynamics in numpy from the brief's equations and checked its copy against the recording. It formed a physical hypothesis. It scanned weights in its own simulator, sometimes all of them, in the background while it kept reasoning. Then it spent one official rollout to confirm the harness agreed, and submitted with a paragraph naming the mechanism, the alternatives it rejected, and the vehicles the edit might hurt.
What it found
The racer's overshoot is the cleanest example of the kind of reasoning that came back. The module's input pipeline, which the harness reproduces for every vehicle, clips the position and velocity errors before the network sees them. On a step climb the network stares at a frozen input for most of the way up, keeps ramping collective, and only starts braking when the error unclips. A quadrotor brakes at roughly one g, so the overshoot scales with how hard the vehicle accelerated while blind, and the racer accelerates very hard. The fix was one entry of the GRU input matrix, routing a hidden unit that happens to fire only at the racer's low hover throttle into a gentler vertical gain. One number, and the other vehicles never notice it.
The fast figure-eight is my favourite, because the account reads like something out of a controls textbook and the fix is something you could actually think if you were tuning a real controller. The 2 kg x500 tracks a leisurely eight fine and then, a little faster, falls off a cliff: every start lands on the same wide lagging orbit. The model's explanation is a resonance. The position and velocity terms are clipped and stop growing with amplitude, the body-rate term is not, so at some speed rate damping becomes the largest thing in the input layer, the motors cannot keep up, and the loop jumps to the lagging branch. The edit scales the roll-rate input column down.
Some more testing before more cool videos
Six ordinary cells first, easiest behaviour first: two hovers, two steps, a figure-eight, a tumbled recovery. Then three scenes where the stock x500 breaks when pushed: the fast eight, a release near inverted, a tall step. Then two merges, because summing the individual edits over-corrects, and the model had to reconcile its own earlier work, which it did by recognising three of its racer fixes as the same fix and keeping it once. Every run passed its own cell without a regression elsewhere. The full grid is below; the recoveries are where the change is largest.
Testing with SITL and more realistic simulations
The racer route is the pretty one. Isaac Sim provides two rigid bodies with the racer's mass and inertia, a warehouse and a camera. Our numpy policy and our rotor model run on each body at 100 Hz, PhysX integrates, RTX renders. Before trusting any clip I compared the Isaac flights with the numpy simulator on the same starts, and for hover, the step and the figure-eight they agree to about a centimetre. Tumbles diverge after half a second, as chaotic things do. The videos show the controller, not the renderer.
The x500 route is the one that matters to anyone running PX4. The harness exports a policy.tar the module accepts as its own, self-test block included. The file goes where the module loads it, PX4 SITL boots with the x500's rotor geometry, and the real mc_raptor runs the inference in C++ with EKF2 estimating the state from simulated sensors. Isaac Sim is the plant on the other end of a MAVLink link, through the Pegasus Simulator backend, in lockstep. Nothing in PX4 knows the weights were edited. On the fast figure-eight the original weights settle on the same wide lagging orbit they found in numpy, and the merged controller flies the eight.
Some thoughts
All of this is simulation. The numbers come from the simulator RAPTOR was trained in, with the module's observation pipeline copied from the PX4 source, and the PX4 flights use a Pegasus Iris body carrying the x500's mass and thrust, not the training twin.
I would also not read too much into how easily the model found these. The brief hands it the physics, the recording and the weights as text, which is more than I usually get, and 2,084 numbers is few enough to scan one at a time. A controller with a million weights is a different problem, and I have not tried it.
Within those limits the result holds. The verdict was computed on seeds the model never flew, every run cleared it, and every edit arrived with a mechanism I could check against the recording. That is more than my gain tables usually come with.
Code, briefs, transcripts and every edit script: soon to be public.