Skip to content
v0.4.0 · Draft GitHub ↗ Apply for review →

From ACKs to altitude: URML programs fly, and the green we threw out

A robot language that stops at “the validator accepted it” has not shown that it can move anything. Since the last release, URML programs have flown on the two open autopilots most drones run, PX4 and ArduPilot. They flew in simulation. Nothing flew outdoors, and this post says exactly what did and did not happen.

The green that did not count

On 2026-09-27 the PX4 flight test passed in 2.37 seconds. It ran against PX4 v1.17.0 in software-in-the-loop simulation (SITL), with a four-step program: take off to 30 m, fly to a point 15 m north, return home, land.

Two seconds is too fast for that flight. PX4’s own log from the run shows “Ready for takeoff!”, “Returning to launch” and “Landing at current position”. It shows no arming and no take-off. The adapter took every command acknowledgement from PX4 as success. PX4 acknowledged each command, the simulated drone sat on the ground, and the test went green.

We threw that result out. The run record says so in writing: no green result from before the fix stands as evidence that the PX4 adapter flies.

The fix confirms each flight step from PX4’s telemetry instead of its acknowledgement. A take-off is done when the heartbeat shows the vehicle armed and its altitude reaches the target. A landing is done when PX4 reports the vehicle on the ground.

A witness that cannot steer

To check the adapter, the test runs a second MAVLink connection that only listens. It sits on the port PX4 streams to a ground station, never sends a message, and computes its numbers with its own arithmetic, not the adapter’s. The same program, with the fixed adapter:

CheckRequiredMeasured
Vehicle armedyesyes
Highest altitude on the 30 m take-offat least 27.0 m31.79 m
Closest approach to the waypointwithin 3.0 m0.92 m
Landed state at the endon the groundon the ground

The flight took 69.3 seconds. A second record the same day repeated it with the adapter sending a ground-station heartbeat. PX4 only sends its status text to a link that sends a heartbeat, so this is also the run where a refused command started carrying PX4’s own reason. The status text PX4 sent during that flight:

Armed by external command
Takeoff detected
Returning to launch
RTL: start return at 519 m (31 m above destination)
RTL: land at destination
Landing at current position
Landing detected

ArduPilot, on a bench

The ArduPilot runtime is new since the last release. It drives ArduCopter over MAVLink, with no ROS.

On 2026-08-29 it ran on a Pixhawk-class flight controller with ArduCopter 4.6.3, on USB, propellers off. A read-only probe identified the board. A battery-check program ran to success. Then the bench-hop program, which does fly, passed URML’s validator, and the autopilot refused to arm: compass not calibrated, GPS bad fix, battery low-voltage failsafe. URML reported the autopilot’s reason and stopped. The vehicle stayed disarmed.

That refusal is the design working. There are two gates: URML checks the program against the robot’s declared limits, and the autopilot runs its own pre-arm checks. URML does not replace the second one.

In ArduCopter SITL, built from the Copter-4.6.3 tag, three of three programs passed: a flight-only test, a photogrammetry survey (take off to 100 m, five stations, a photo at each), and a parcel delivery. The delivery run surfaced one defect. ArduCopter 4.6 rejects MAVLink’s winch deliver and retract actions, so the adapter now controls the winch by line length. Payload release by winch or latch is now part of the spec (RFC-0684).

A language model wrote two of those programs. Gemini 2.5 Pro, through URML’s LLM bridge, turned the survey and delivery sentences into programs that flew unchanged in SITL. The delivery sentence needed one change on our side first: the prompt had no delivery example, and the first program never released the payload. With a delivery example added, it did:

Winch: lowering 15.0m
Gripper load released
Winch: raising

For contrast, qwen2.5:7b on a CPU produced the same invalid program four times for each sentence, and the validator refused it every time. A weak model is wrong, not dangerous.

The same bug, elsewhere

An adapter that reports success without checking is a general hazard, so we looked for more. The scan step on several reference adapters reported success with made-up results, and grasp and capture calls could raise errors. Both are fixed, and a new conformance test reads every adapter’s source to keep them fixed. On the ROS 2 side, scan now drives a real route through Nav2’s loopback simulator, and a goal off the map comes back as an abort instead of a success.

The same stretch brought first real adapters for four more kinds of behavior. Autoware covers trajectories, Reachy Mini covers expression (tested without the robot so far), GoPiGo3 covers relative drive and turn, and the Spot adapter now drives its arm. The changelog lists all of it.

What this is not

This is simulation, and a bench with the propellers off. No URML program has flown a real aircraft. Both SITL runs were on a developer machine. The CI jobs that would fly SITL on every change have not run yet. A passing SITL flight is evidence under one simulator’s physics, not a safety case.

Where to look

The registry lists three entries with their evidence and their limits: ArduCopter on a Pixhawk-class board, PX4 in SITL, and the GoPiGo3 example. Each entry names what was run, on what, by whom, and what the evidence does not show.

urml-validator and urml-llm-bridge 0.4.0 install from PyPI. So does the ArduPilot runtime, at a build from before the payload-release work. PX4 and the other runtimes in this post install from source for now. Everything on main since 0.4.0 is on the releases page, under “On main”.


Sources

  1. PX4 SITL run record, 2026-09-27: the first flight · github.com · accessed Sep 29, 2026
  2. PX4 SITL run record, 2026-09-27: with the ground-station heartbeat · github.com · accessed Sep 29, 2026
  3. Registry entry: ArduCopter on a Pixhawk-class board · github.com · accessed Sep 29, 2026
  4. Walkthrough: from a sentence to a Pixhawk · github.com · accessed Sep 29, 2026
  5. CHANGELOG: work on main since 0.4.0 · github.com · accessed Sep 29, 2026