My Slip Detector Was Accurate. It Was Also Useless.
Correction, August 19, 2026. Everything below was measured on a robot that was tipping forward. The drive wheels sat on the centre axle with the only caster at the rear, so nothing carried the front half of the chassis. It rested on its front bottom edge at 13.30 degrees, which the ground-truth pose, the IMU orientation and the accelerometer all reported, and which the geometry predicts at atan(0.033 / 0.1435) = 12.96 degrees.
With a front caster added and the robot level, the same world at the same commanded speed produces no slip at all. The robot travels 9.64 m and the odometry reports 9.64 m, an error of 0.00 percent, against 6.44 m travelled and 9.61 m reported before. The slip this post is about was the chassis dragging, not the friction. The drag demanded traction the surface could not supply, so the wheels turned while the body stayed behind.
I am leaving the post up rather than deleting it. The reasoning about ablations still holds, and the way this was missed is the more useful lesson: every control arm was run correctly, and all of them assumed the rig was what it claimed to be. Nothing in phase 1 ever measured the robot’s own posture. The measurements are being repeated on a level robot.
I spent this summer on a small question. A wheeled robot drives onto a low-friction patch. The wheels turn, the robot barely moves, and the odometry keeps counting distance that was never travelled. Can the robot notice, and can noticing help?
The first half went well. I built a detector over thirteen proprioceptive features, wheel and IMU signals only, nothing that would leak the answer. It reached 0.869 accuracy and stayed calibrated, with an expected calibration error of 0.021. Then I wired it to a behaviour: slow down in proportion to the probability of slipping. Position error dropped by 72 percent.
That number was wrong, and the way it was wrong is the reason I am writing this.
The control arm I almost did not run
The detector arm drove slower on average than the baseline. So I ran the obvious control: no detector, constant speed, matched to the same average. It did better. On the slippery terrain the detector arm made 0.179 error per metre and the constant arm 0.126. On grippy terrain it was 0.020 against 0.001.
The 72 percent was real. It just did not belong to the detector. It belonged to driving slower, which needs no model, no features, and no training data. I rewrote the result.
The detector also turned out to be reading its training terrain rather than the world. Across four friction levels it reported almost the same probability everywhere, around 0.95, while the actual error per metre ranged from 0.459 to 0.002.
Fixing the detector, and losing again
Training on four terrains fixed the transfer problem. Evaluated leave-one-terrain-out, the new model beat the majority class on every unseen terrain, and it finally responded to the ground under it: mean probability 0.272 on the slippery patch against 0.163 on the grippy one.
Then I ran the ablation again. This time I could not use a single matched speed, because matching the commanded speed does not match progress. Under slip the same 0.28 m/s average moved the detector arm 3.40 m in 24 seconds and the constant arm 6.39 m. So I measured constant speed as a curve, four speeds from 0.10 to 0.40 m/s, and placed the detector on it. The question became simple: at the same distance covered, does the detector make less error?
| Terrain | Detector | Best measured constant arm reaching the same distance |
|---|---|---|
| μ = 0.02 | 6.43 m, 0.3297 error/m | 0.30 m/s: 6.40 m, 0.0857 |
| μ = 0.02 | 3.51 m, 0.0068 error/m | 0.20 m/s: 4.81 m, 0.0079 with 37% more ground covered |
| μ = 0.20 | 9.26 m, 0.0049 error/m | 0.40 m/s: 9.49 m, 0.0007 |
| μ = 0.20 | 7.89 m, 0.0041 error/m | 0.40 m/s: 9.49 m, 0.0007 |
Four comparisons, four losses, and the two test terrains were both in the model’s training set. The detector lost while running on its own data.
One comparison looked like a win at first. My summary script interpolated the constant-speed curve and reported the detector 19.9 percent ahead. The curve is nearly vertical at that point, because on slippery ground going from 0.30 to 0.40 m/s buys 0.04 m of distance and five times the error. The interpolated reference was an artefact of that cliff. A real measured arm at the same distance was four times better. The summary script is a measuring instrument too, and it needs checking like any other.
The question I should have asked first
Before asking which model to use, I should have asked whether the thing I wanted to estimate was visible at all. So I set up the sharpest version of that test. Two states with opposite consequences:
Stuck on the patch, ground-truth speed near zero, wheels spinning at 0.4 m/s. Driving normally on solid ground at 0.4 m/s.
The feature called wheel speed reads 0.4000 in the first case and 0.3999 in the second. Across all thirteen features the largest effect size is 0.56, which counts as weak. The best single feature separates the two states at 0.552 balanced accuracy. A coin gets 0.500.
The reason is not subtle once you look at it. The features are built on derivatives, mostly the residual between wheel acceleration and body acceleration. A robot stuck at constant wheel speed has zero acceleration everywhere. A robot cruising at constant speed also has zero acceleration everywhere. The two states are the same measurement.
This also explains why slip detection worked at all. It was catching the transient at the edge of the patch, the moment traction changed. Transitions are observable. States are not. A policy needs the state.
What I take from it
Every intervention I tried lived downstream of a quantity that was never observable: filter tuning, nonholonomic constraints, zero-velocity updates, adaptive covariance, better features, better policy, better generalisation. None of them could work, and measuring each one was the only way to find that out.
The fix is not a better model. It is a channel that measures body velocity without asking the wheels. The simulation has a lidar that this entire phase never used. Scan matching gives exactly that. That is the next measurement, and the first number I want is how much of the 279 percent odometry error it removes.
Two rules came out of this that need no detector at all. On slippery ground, do not command above roughly 0.30 m/s: above it the robot gains no distance and multiplies error by five. On grippy ground, do not slow down: it multiplies error per metre by seventeen. Four rows of a table outperformed a thirteen-feature model.
Limits
This is simulation only, one differential-drive robot, one nominal speed of 0.4 m/s, and friction values chosen to make the effect large rather than realistic. Three repetitions per arm. The observability result is about this sensor set, wheel encoders and an IMU, and says nothing about robots carrying anything else.
The code is in a private repository while a provisional patent application and a journal review are open. If you want to read it, write to me and I will give you access.