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