flamingo-cycle
Stand on one foot, either side, on command, and come back to a two-foot stand: twist = [flag, side, 0].
Command — twist slots of the 13-D command block (head / body slots: unused (zeros), unused (zeros)); idle = [0, 0, 0]
| slot | meaning |
|---|---|
| twist[0] | flag: 0 = stand on two feet (HOME), 1 = stand on one foot |
| twist[1] | side: +1 = right foot down / left leg lifted, -1 = left foot down / right leg lifted; 0 allowed while flag = 0 |
| twist[2] | unused (0) |
What it does — flag = 1: shifts its weight over the chosen foot, lifts the other ~9 cm and holds (10 s+); flag = 0: lowers it and stands. Either side, same network, ~1.5 s each way, interruptible at any time. Choose side before raising the flag.
Limits (sim only, never run on hardware) — holds pushes ≤ 0.15 m/s from any direction; a harder push toward the lifted side is absorbed by a brief touch-down; toward the standing side it steps down; backward ≥ 0.18 m/s it falls (2 of 24 random trials). No "gave up" signal: while flag stays 1 it re-lifts after stepping down — drop the flag if you want it to stay on two feet.
Try it in simulation (laptop, no robot) — needs the training repo on its flamingo branch (the keyboard driver's flamingo mode lives there):
git clone -b flamingo https://github.com/pollen-robotics/microduck_rl.git && cd microduck_rl && uv sync # once
hf download RemiFabre/microduck-flamingo-cycle policy.onnx --local-dir policies/flamingo-cycle # this repo's policy → ./policies/flamingo-cycle/policy.onnx
On Linux:
uv run scripts/infer_policy.py --flamingo policies/flamingo-cycle/policy.onnx --new-cmd-obs
On macOS (the MuJoCo window needs the mjpython launcher, which needs libpython next to the venv — once):
ln -s "$(.venv/bin/python -c 'import sys; print(sys.base_prefix)')/lib/libpython3.12.dylib" .venv/libpython3.12.dylib
uv run mjpython scripts/infer_policy.py --flamingo policies/flamingo-cycle/policy.onnx --new-cmd-obs
Then, in the terminal that launched it (not the MuJoCo window): press F to lift the foot, F again to put it down; C picks the side (while standing); P gives a random push; Q quits.
Try it on the robot — no daemon code change; the robot only runs this policy while configured this way:
scp policy.onnx control.py radxa@<robot>:/home/radxa/policies/flamingo/ # 1. files on the robot
# 2. on the robot: edit /etc/robot/robotd.toml as in control.py's header (walk = this policy, stand = "none", ...) then
sudo systemctl restart robotd
# 3. from the laptop, forward the daemon socket and drive it
ssh -L /tmp/robotd.sock:/run/robotd.sock radxa@<robot> # keep this terminal open
python3 control.py --socket /tmp/robotd.sock # f = lift / lower, c = side, q = quit (lowers first)
python3 control.py --socket /tmp/robotd.sock --timeline "0:0,1;3.6:1,1;11.6:0,1;18.3:1,-1;26:0,-1" # scripted, same as the video
Contract obs[1,61] f32 → actions[1,14] f32, normalizer baked in, 50 Hz, targets around HOME × 1.0. Kind perpetual, entry pose standing.
Provenance Mjlab-FlamingoCycleHard-Flat-MicroDuck — pollen-robotics/microduck_rl @ 0bf9897 on branch flamingo (https://github.com/pollen-robotics/microduck_rl/commit/0bf9897), not merged yet, run pollen-robotics/flamingo-cycle-r2-hard-20260829-0245; exported with scripts/export.py.
Files policy.onnx · manifest.json (all of the above, machine-readable) · media/preview.mp4 (sim rollout) · control.py
Format: Microduck policy sharing.