Public: anyone can read this; the crew can change it.
Migrated from github.com/Trevato/SO-ARM100
A jam is load without progress: hard acceleration toward a far target is not a jam (17 false jams, 2 of 3 snap-backs) #511
I rewrote the stream's jam watch so it judges time instead of counting readings. A jam now means the load stayed high for 300 ms while the joint made no progress toward a far target. A hard move toward a far target no longer counts as one. When a real jam parks a joint, the page now says why.
stopped parked by the crew deploy bd07f2f
I rewrote the stream's jam watch so it judges time instead of counting readings. A jam now means the load stayed high for 300 ms while the joint made no progress toward a far target. A hard move toward a far target no longer counts as one. When a real jam parks a joint, the page now says why.
Cause: the old watch called it a jam when 6 readings in a row showed load ≥ 45% and less than 8 ticks of travel. Those readings wait in the stream's single queue with the governor ticks and frames. When the queue backs up they run back-to-back, a few ms apart. At the start of a hard move (180°/s, 1080°/s²) the joint covers under 2 ticks in the first 15 ms while the load is at 60–70%. So six bunched readings looked like a joint that wasn't moving, and it was parked mid-drag.
The change:
- New rule (
src/jam-watch.ts): each reading is time-stamped when it is read. A joint jams only when all of these hold:
- every reading over 300 ms shows load ≥ 45%, - the joint moved no more than 8 ticks, in any direction, - the target is more than 30 ticks away.
If two readings are 300 ms or more apart (a hold, a stalled queue), the watch starts over. src/server.ts keeps the existing response: park the joint where it is, and let it follow again once the leader backs off.
- The page says why.
/statusgainsjams: [{ id, joint, position, target, load, why }];jammedkeeps its old shape. The hold line now reads, for example, "jam: shoulder_lift pushed at 90%+ load for 300 ms and moved 0 of the 300 ticks to its target — stopped it there; back the leader off". The driver's refusal line carries the same reason. If the arm is still running the old server and sends only ids, the page shows the old wording. - Fake bus (
src/fake-bus.ts): with dynamics on, it now reports load: 70% while a servo speeds up or brakes, 15% at steady speed, 0 at rest. Astuckservo also works with dynamics: it never moves and reports 90%.
Tests (src/jam-stream.test.ts, the real server on the dynamic fake bus at 180/1080, plus unit tests in src/jam-watch.test.ts):
- A 90° flick there and back four times, read every 5 ms, raises no jam, and the arm arrives each time.
- A joint held against a stop jams in under 500 ms,
/statusgives the reason, and the free joint keeps going. - Both tests fail on the old watch: the flick raises a false jam, and the stuck joint takes 616 ms.
- The relevant suites pass: 192 tests (server, precision-history, stop, release, motion, bench, terminal, fake-serve) plus the hold-line and page-feedback tests.
Report:
- Before: production
/metricsat 00:15Z counts 19 jams since 19:49:59Z, about 4.3 per hour. The two newest look like the same false jam:shoulder_lift 1076 → 1845 load -70andelbow_flex 2956 → 3037 load -70. - After: not measured. I can't reach the Pi or the steward's
arm-metrics.jsonl, so the after figure has to come from the hourly snapshot once this runs on the arm.
Not done:
- The 45% load threshold is unchanged; only the "no progress" side and the timing changed.
- The supervised-move stall check in
src/motion.tsis untouched: it already reads at its own polling pace and was not among the reported jams.
To try it: bun --cwd Software/so101 test src/jam. On a preview, push a joint against something on the stream and read the hold line.
🤖 Generated with Claude Code
Parked by the crew: the preview's deploy failed and the decider saw no point in another try
I rewrote the stream's jam watch so it judges time instead of counting readings. A jam now means the load stayed high for 300 ms while the joint made no progress toward a far target. A hard move toward a far target no longer counts as one. When a real jam parks a joint, the page now says why.
Cause: the old watch called it a jam when 6 readings in a row showed load ≥ 45% and less than 8 ticks of travel. Those readings wait in the stream's single queue with the governor ticks and frames. When the queue backs up they run back-to-back, a few ms apart. At the start of a hard move (180°/s, 1080°/s²) the joint covers under 2 ticks in the first 15 ms while the load is at 60–70%. So six bunched readings looked like a joint that wasn't moving, and it was parked mid-drag.
The change:
src/jam-watch.ts): each reading is time-stamped when it is read. A joint jams only when all of these hold:- every reading over 300 ms shows load ≥ 45%, - the joint moved no more than 8 ticks, in any direction, - the target is more than 30 ticks away.
If two readings are 300 ms or more apart (a hold, a stalled queue), the watch starts over.
src/server.tskeeps the existing response: park the joint where it is, and let it follow again once the leader backs off./statusgainsjams: [{ id, joint, position, target, load, why }];jammedkeeps its old shape. The hold line now reads, for example, "jam: shoulder_lift pushed at 90%+ load for 300 ms and moved 0 of the 300 ticks to its target — stopped it there; back the leader off". The driver's refusal line carries the same reason. If the arm is still running the old server and sends only ids, the page shows the old wording.src/fake-bus.ts): with dynamics on, it now reports load: 70% while a servo speeds up or brakes, 15% at steady speed, 0 at rest. Astuckservo also works with dynamics: it never moves and reports 90%.Tests (
src/jam-stream.test.ts, the real server on the dynamic fake bus at 180/1080, plus unit tests insrc/jam-watch.test.ts):/statusgives the reason, and the free joint keeps going.Report:
/metricsat 00:15Z counts 19 jams since 19:49:59Z, about 4.3 per hour. The two newest look like the same false jam:shoulder_lift 1076 → 1845 load -70andelbow_flex 2956 → 3037 load -70.arm-metrics.jsonl, so the after figure has to come from the hourly snapshot once this runs on the arm.Not done:
src/motion.tsis untouched: it already reads at its own polling pace and was not among the reported jams.To try it:
bun --cwd Software/so101 test src/jam. On a preview, push a joint against something on the stream and read the hold line.🤖 Generated with Claude Code
Closes #509