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

Field notes: the first robot runs a validated URML program (and drives off)

This week a community member who goes by @slowrunner closed the loop the whole project is built around. A validated robot program, executed on real hardware, with the safety gate in front of the motor. His GoPiGo3, Lyrical-Dave, drove across the room. Then it drove under a chair. A day later he closed the rest of the loop, the English sentence and the language model included, fully offline.

Lyrical-Dave, a GoPiGo3 ROS 2 robot

Lyrical-Dave, @slowrunner’s GoPiGo3 ROS 2 robot. Photo: @slowrunner.

The headline

This is the first time the full URML loop has run end to end outside a maintainer’s machine: English, to a validated program, to a robot that moves, on an educational two-wheel buggy and a Raspberry Pi, with no cloud. The loop closed on someone else’s robot, in their own home. That is the milestone.

How it actually happened, which is the better story

We had just shipped an update to the GoPiGo3 example so it would notice a real robot and use it. @slowrunner pulled the update onto Dave, copied the example into his test folder, and ran it with -h, out of habit, expecting a help message. Instead he heard motors in the next room. He raced in to find Dave spinning ninety degrees and heading under a chair, his heart, in his words, racing as fast as Dave’s.

He told it himself, with good humor: be careful what you type, your robot may be listening.

The part we are not proud of, and fixed

URML’s entire pitch is validate before actuate. Nothing moves until it is checked. And our own example moved a robot when someone typed -h. That is precisely the failure the project exists to prevent, and we had built it into the demo.

So we fixed it the same day. The example is now a dry run by default. Run it, or run it with -h, and it validates the program and prints the wheel and speech commands it would issue, and moves nothing. To actually drive the robot you pass --execute, which warns you first. Actuation is opt-in, the way it should have been from the start.

The lesson is the project’s own, turned back on itself: a validated-safe program is only safe if running the tool cannot surprise you into executing it. The gate has to sit in front of the motor, including in the demo.

What the robot was actually told

The “validated safe” program Dave ran was a short patrol: turn ninety degrees, drive half a meter, turn back ninety degrees, drive another half a meter, announce “Patrol complete,” and write a log line. Here it is in full, because the shape of a URML program is worth seeing once:

profile: [educational]
behavior:
  type: sequence
  on_error: abort_and_report
  steps:
    - turn:  { angle: 90 }
    - drive: { distance: 0.5 }
    - turn:  { angle: -90 }
    - drive: { distance: 0.5 }
    - speak: { utterance: Patrol complete }
    - report: { to: run_log, facts: { patrol: done }, status: success }

Every step was checked against Dave’s declared capabilities, a frameless two-wheel buggy with wheel encoders, before a single wheel turned: each drive is within his declared per-move distance, turn is allowed because he declares odometric motion, and speak resolves to his speech output. The validator did its job. The demo’s invocation did not. Now both do.

(With thanks to @slowrunner for correcting an earlier, looser description of this program.)

One footnote: Dave moved but stayed silent. The example’s default speech path is espeak, and on Dave it produced no sound and, worse, no error. That was a gap too, so the adapter now says on its log when it cannot speak, and a robot with its own voice, like Dave’s ROS say node, can be wired straight in.

The full circle

That first run executed a program we had written. Within a day, @slowrunner closed the rest of the loop. He typed an English sentence, “drive forward ten centimeters,” and a language model running on his own hardware through Ollama translated it into a URML program. The validator checked that program against Dave’s manifest. A new option to run a program from a file sent the validated result to the wheels.

Sentence to motion, with the model on the edge and no cloud anywhere in the path: the model proposes, the validator gates, and the robot moves only what was declared possible and checked safe. That is the whole thesis of the project, demonstrated by someone who is not us, on a robot that is not ours.

The first translation timed out while the model loaded into memory, and a retry succeeded once it was warm. That was the last small gap, and it closed the same day: the bridge’s OpenAI-compatible path now honors a configurable request timeout, so a cold model load has room to finish.

Why we are writing this down

The validator-first design held: the robot did only what was declared possible and checked safe. What failed was operational glue around the demo, and a user caught it on his own hardware in the most memorable way possible. That is what early adoption looks like, and it is worth more than a clean launch.

The full thread, including Dave’s own account, is on the discussion board. Lyrical-Dave’s code is at github.com/slowrunner/LyricalDave.

Updated Jun 25, 2026


Sources

  1. Discussion #542: First GoPiGo3 robot runs a validated URML program · github.com · accessed Jun 25, 2026
  2. examples/gopigo3: the GoPiGo3 example runtime · github.com · accessed Jun 25, 2026
  3. PR #543: dry-run by default, --execute to actuate · github.com · accessed Jun 25, 2026
  4. @slowrunner's account on Mastodon · fosstodon.org · accessed Jun 25, 2026
  5. Discussion #523: the GoPiGo3 runtime thread (where the full circle closed) · github.com · accessed Jun 25, 2026
  6. Lyrical-Dave (the robot in this post) · github.com · accessed Jun 25, 2026