Skip to content

~3 min read

Motions·

A pose is a place. A motion is a sentence — a nod, a shake, a wave — 9 of them built in. The twin below plays each one from its keyframes; tap a chip to loop it. Nothing here moves the metal.

motion what it says frames planned s on the metal s
approve a nod: the head dips and lifts twice 5 3.3 6.5
deny a shake: the head turns left-right twice 5 3.9 7.0
happy head up, a quick left-right wiggle with bobbing, a little bounce of the whole arm 7 5.4 8.7
frustrated head drops, a slow look away, then a short sharp shake 7 6.8 11.1
curious head tilts and lifts toward what it looks at, a small rise of the arm 3 4.6 7.0
wave head raised on the wrist, three fast pans 6 8.6 12.4
look_around a slow survey: far left, far right, up, back 4 7.1 9.8
up · stays head up on the wrist and looking up; stays there 1 2.5 3.2
sleep a slow fold onto the bench 2 1.1 7.5

Planned seconds are travel ÷ speed per frame plus holds, from fomo/motions.py at build; "on the metal" is what the real arm took on 2026-09-08 (5.6 V rail, settle per frame included — LIVE journal).

fomo motion happy --i-know              # from the shell
motion(action="run", name="approve")    # the agent's fifth tool
POST /api/control/motion {"name": "deny", "speed": 60}

"Show me you're happy" in the panel's chat ends up in the same call. A motion starts from wherever the arm is and ends folded unless it says stay (up does). While it plays, state.busy reads motion:happy 3/7, and stop ends it between or inside frames.

A keyframe·

{"rel": {"tilt": 22, "pan": -30}, "speed": 55, "hold_s": 0.1}

rel is degrees from the calibrated home for the joints the frame moves — the others keep their place. speed is degrees per second for the ramp (clamped to the guard's ceiling), hold_s a pause once the frame has settled. Every frame is checked against the hard limits before the first write; a frame the guard would never allow refuses to save.

Sign conventions on this arm: tilt + looks up, pan + looks left, wrist_flex − lifts the head up and over (its + side is blocked at the fold), shoulder_lift + lifts.

Your own·

motion save peek frames='[{"rel":{"tilt":15,"pan":-20},"speed":60,"hold_s":0.2},{"rel":{"tilt":0,"pan":0}}]'

lands in ~/.fomo/motions.json, same shape as poses, relative to home — recalibrating shifts every motion with the fold. A user motion named like a built-in shadows it; built-ins cannot be deleted.

What the metal did·

All nine were played twice back to back on 2026-09-08: 18/18 frames-complete, 155.7 s, servo temperatures unchanged (30–31 °C). Two things the recordings taught:

  • The head servos top out near 70°/s (pan) and 55°/s (tilt) on this rail — tilt's factory torque limit is 500 ‰. Asking for more adds lag, not speed, and the guard then reads the lag as a stall. The built-ins ask for what the servos do.
  • A loaded joint starts slowly. The wrist lifting the head off the forearm moves under half a degree in its first quarter second; the stall check waits 0.6 s before judging.

The built-ins do not use the elbow: on this supply it does not lift the forearm from the fold (rule 8/9 in the guard). On the 7.4 V supply, add it to your own.