Phase 0 · sim-first · nothing on the ground yet

A one-wheeled board that's learning to balance itself.

One fat wheel, a deck bolted around it, and a small computer that checks which way it's tipping a thousand times a second — then spins the wheel to catch itself. It's the same thing your legs do when you stand still, except it has to be written down. So far it only does this inside a simulation, and nothing has touched the ground. This is the attempt, in the open, including the runs that ended on the floor.

18.6°how far it can tip before it hits the ground
1,000×a second — how often it will check its balance
0custom electronics

The route so far.

Four stops between “it falls over” and “someone rides it.” We are standing on the first one. The grey ones are what it should look like when we get there.

CLEARED YOU ARE HERE
Stop 1 · happening now Balance it in a simulation No hardware at all. The balance code now holds it up in the physics engine — but on perfect sensors. Doing it with realistic, noisy ones is the rest of this stop.
Stop 2 A real one, off the ground A real board, held still, wheel spinning in open air, nobody near it.
Stop 3 Someone stands on it Only after a safety review it has to pass first.
Stop 4 The ambitious stuff Steering, waypoints, sending it somewhere from a phone.
travelled not yet — the grey ones show where we're trying to get to

Right now: it stays up, and it goes where it's told — as long as it's carrying a rider's weight.

Three runs, in the order we got them working. Each step changes exactly one thing, and says which — first the code, then the load it's carrying. Watching them in sequence is watching the problem get solved.

No balance code attached. The shove lands at 0.5 s; the nose reaches the ground after tipping 18.6°.

Where this goes next: the route so far →

The log.

Newest first. Every entry has something to look at — a picture, a plot, or a number. If there's nothing to show, it isn't an entry.

26 July 2026

A number that should have been +1

Before the balance code can do anything it has to be told which way is forward and which way is down. Our own written spec says those directions must be right-handed — the ordinary arrangement where fixing two of them fixes the third. The conversion we had shipped was mirrored instead.

There is a one-line check for exactly this. It produces +1 for a real rotation and −1 for a mirror image. Ours was −1. Nobody needed to understand the physics to catch that; it contradicted a document we had already written, by arithmetic.

Why it lasted. It hides at the angle everything was tested at: the error is zero when the board is level and grows as twice the tilt, so with the board sitting quietly upright the readings come out identical whether the conversion is right or mirrored. Every check we had run was in the one place the fault could not appear. The comment above the code named a test as the thing keeping it honest — and that test had never been written. It was cited, not skipped: no such check existed in the project, and none ever had.

Two of the sensor directions were mirrored, not one. The estimator was reading a correct tilt-rate against a tilt-angle that came out backwards, which is why it looked fine standing still and fell apart in motion. The second one is still wrong today and nothing has noticed, because nothing reads it yet: it is the roll rate, the sensor that matters when the board is steered by leaning. Left as it was, it would have shown up as a board that steers the wrong way — with somebody standing on it.

What it cost. A result we had already recorded. The board used to come back to within 2 cm of where it started; measured through a corrected conversion it is 23 cm. The 2 cm figure was taken through the mirror and is void. That also means we now miss a target we had set ourselves — and that miss is written down as a test we expect to fail, rather than dealt with by quietly moving the target.

The replacement check does not run the simulation at all: no motion, no ground, no sensors. The old conversion had been worked out by running the simulator and comparing against it, and that is precisely the method that failed here — which way round the axes go is a definition, not something you measure. It is now checked as arithmetic instead: the +1, where each axis lands, and the angle coming back correctly across the whole range of tilts the board can reach.

the check that would have caught it: +1 or −1
26 July 2026

A knob that changed nothing was the clue

Balancing from guessed tilt instead of the simulator's perfect answer kept failing. The giveaway: turning a tuning dial from one extreme to the other produced exactly the same number every time — and a dial that changes nothing means you aren't turning the thing you think you are. The tilt calculation was being fed zeros because of a wiring mistake, and confidently reporting the board was upside down. Fixed, and it now balances from its own estimate rather than from the answer key.

the identical result, at every setting
26 July 2026

The balance code ran for the first time

The real control code — the actual Rust that's meant to run on the board — took over in the simulation instead of a stand-in. Same shove that used to put the nose in the ground at 18.6°: it now holds inside 1.4°. Worth being precise about what that proves, which is less than it sounds. It's reading perfect, invented sensor values, and there's no tyre.

before / after pitch trace
26 July 2026

We made the simulation worse on purpose

Every measurement so far came from perfect sensors — no noise, no lag, no rounding. That makes a simulator an optimism machine: the numbers look good because nothing is allowed to go wrong. There's now a deliberate profile of realistic sensor nastiness, and the tests have to pass with it switched on.

clean signal vs. corrupted signal
26 July 2026

With nobody on it, there's almost nothing to steer with

Empty, the board's weight hangs slightly below the wheel's axle, so leaning it over barely pushes it along — about 200 times less shove than the same lean gives when there's a rider's worth of weight up top. So the empty board holds itself flat beautifully and then just rolls off. That's not a bug to tune out; it's the shape of the thing.

lean-vs-push, empty and loaded
25 July 2026

The build now fails if the board falls differently

Every change re-runs the shove-and-watch test automatically, and the build goes red if the response drifts from what's expected. It's the first thing standing between a bad change and a wasted week.

CI run — pass / fail
25 July 2026

Left was right

The model had the motor's direction backwards: positive current drove the board backwards and pitched the nose down, the opposite of what every design document specified — and the comment sitting directly above the code claimed it did the right thing. Two tests now pin it so it can't silently flip again.

before / after pitch trace
25 July 2026

A test that could never have failed

The plan said the board counts as fallen when it tips past 45°. It physically cannot reach 45° — the bumper hits the ground at 18.6°. The test would have passed forever, no matter how hard the board was hit.

geometry diagram — 18.6°
25 July 2026

Nearly got that number wrong too

The first attempt at the tipping angle gave 14.9°, by assuming the two furthest corners of the bumper meet at a point. They don't — it curves up, so the part that lands first is about 90 mm further in.

bumper cross-section
25 July 2026

We stopped faking the falling over

The old model got its instability from an 8 kg weight on a stick standing in for a rider, so every result was really about the stick. The real board is stable at rest, because the battery and motor sit below the axle.

old model / new model
25 July 2026

It got a real body

Swapped the placeholder shapes for proper meshes and a real tire, so contact with the ground behaves like the actual board instead of an approximation.

render — the model

One board. Two machines.

The same hardware, doing two completely different jobs depending on whether anyone is standing on it.

Nobody aboard

The one that teaches

Held still, wheel off the ground, and the computer has to do the entire job by itself. This is the real project, and the hard half.

Someone aboard

The one that's fun

Stand on it and it's just a very good onewheel — the balancing is done by the same open-source firmware DIY riders already use. Our code comes along as a passenger: it watches, it takes notes, and it physically cannot touch the motor.

Either way

The one you can copy

Ordinary parts, no custom electronics, and every design document public — including the ones that turned out to be wrong.

On safety. Nothing here is a product and none of it is certified, and nobody has stood on anything yet. The rules for when that day comes are already fixed, in writing: the code we wrote will not be allowed to command the motor at all — decided by which firmware is loaded, not by a setting anyone could flip by accident — and a physical kill switch sits in the power line either way, because a promise made in software isn't a safety system.

Built with an AI, with receipts.

Almost all of this was designed and written alongside an AI model. The interesting part isn't that it wrote code — it's where it was confidently wrong, and what caught it. Three from the record so far.

01 · caught by reading

The docs disagreed with the code

The design said positive current pitches the nose up. The model did the exact opposite, and the comment above it claimed otherwise. No test was failing. What found it was reading the document against the code, line by line.

02 · caught by computing

A number that looked right

The tipping angle came out at 14.9° from a reasonable-sounding shortcut. The real answer is 18.6°. Nothing about the wrong one looked wrong — it only broke when the value was computed from the actual geometry instead of written down.

03 · caught by asking

A test that could never fail

An acceptance criterion was set at a threshold the machine physically cannot reach. It would have passed forever and proved nothing. Being specific about the geometry is what exposed it.