Go further: read runs, change one thing, use every port

After your first duck, the questions change: how did a policy become what it is, and what else can the duck do? Here you read a real training run from its own records, run one change of your own on a rented card, and reach the duck's other channels, from its camera to its spare Qwiic port. New to this? Start with your first duck.

Whose tools these are. The robot, its software and its board are Pollen Robotics'. The runs read here are duckbatch's, an open project by this site's author that fine-tunes, in simulation, Pollen's kick and smaller networks distilled from Pollen's walk. Book 4, How a Policy Became, tells the story behind each run. If a step goes wrong for you, tell us which one.

Read a real run

How do you find out what a run actually did? Every fine-tuning run in Book 4 left its records in a public Hugging Face dataset, craigm26/duckbatch-records, one folder per job, and b003's, the run in the book's first lesson, is a good first read. With uv, from the first-duck guides:

uvx --from huggingface_hub hf download craigm26/duckbatch-records \
  --repo-type dataset --include "jobs/6ab6d30b6b030d633f69210a/*" \
  --local-dir records
uvx tensorboard --logdir records/jobs

Open the address TensorBoard prints, and read the log in this order:

  1. What it practised. The Curriculum/ charts are the settings that actually ran, and Curriculum/standing_envs reads 0.25 at every iteration, though the run's menu.yaml asked for 0.05. The menu is a request; the log is what ran.
  2. Each term's pay. The Episode_Reward/ charts, one per reward term, show which term moved: here the pay for tracking the command rises over the run while the duck learns to stand. The steep start is episodes growing to their full length, not learning.
  3. The total, last. Train/mean_reward adds every term together, which hides which one moved.

Beside the log are the menu that was asked for, record.json with the final evaluation, the speed reached at each command in speed/speed_curve.json, the trained network, and a checkpoint every 250 iterations.

Then read the two notes the run was judged by, both in duckbatch's notes folder: the plan, written before it ran, and the verdict after.

Change one thing

A result is something you can learn from only when you wrote down, before it ran, what you expected and the lines it must pass. duckbatch runs a menu, one YAML file, on Hugging Face Jobs, so copy one of its menus, change one setting, and write that note first.

git clone https://github.com/craigm26/duckbatch && cd duckbatch
uv sync
cp menus/b003b-progress.yaml menus/mine.yaml

Edit one line of menus/mine.yaml, give it a new batch_id, and leave it uncommitted. The job runs duckbatch's own code at the commit your copy is on, and --inline sends your menu along with it.

The job needs a Hugging Face token that can write, as HF_TOKEN: export it in your shell, or put the line HF_TOKEN=<your token> in a .env file in the folder, which git ignores. Look first, because --dry-run says what it would start and starts nothing.

uv run duckbatch hf-job menus/mine.yaml --run finetune --inline \
  --dataset <you>/duckbatch-records --dry-run

Then run the same line without --dry-run. That run is billed to your Hugging Face account, which needs credit on it, and a fine-grained token also needs permission to start Jobs, neither of which the dry run checks. The job stops at duckbatch's default limit, --timeout 4h.

On an iPhone, iPad or Mac, Microduck Studio's Train a network on Hugging Face sends the same kind of request, and b003b itself was launched from the request Studio builds.

Other cards and services

Every fine-tuning run in Book 4 trained on one NVIDIA L4 on Hugging Face Jobs, l4x1, duckbatch's default; b002, the distillation that made their starting network, ran on a laptop's graphics card. The Windows and Linux guides train on an NVIDIA card in your own PC instead.

We have not tried another Hugging Face Jobs card yet. duckbatch's --flavor takes one by its hardware name, though its menus size the practice for the L4's memory. Nor have we tried a Linux machine with an NVIDIA card rented from another cloud, which should follow the Linux guide.

If you try either, tell us how it went, and this page will say so. Training in another simulator altogether is lesson 10 of Book 4.

Put a trick on a button

The robot runs two kinds of policy, and Pollen's tools treat them differently. A slot is what it runs by default, the walk or the stand, and robotctl policy load walk replaces it. A skill is what it runs when asked, a bow or a kick, and it sits beside the walk instead of replacing it (Pollen's cheat sheet).

sudo robotctl policy add polite-bow fffiloni/microduck-polite-bow-b1d864
robotctl robot do polite-bow
sudo robotctl pad bind x polite-bow

The first line installs a bow someone published as a skill. The second runs it, once the robot is driving: press Start on the pad first, or the request is refused. The last puts it on the pad's X button, which ships empty, and sudo robotctl pad reset puts the buttons back; nothing restarts either way. A skill that holds until told otherwise, like a one-footed stand, has no length of its own, so policy add takes one with --hold, and the command it reads with --command.

Ports between your devices

The duck and your computers talk over a few ports, and only ssh asks for anything: the duck account's password, or your key.

Who can drive. Pollen's own notes say it plainly: anyone on the duck's network can drive it from the console and see through its camera. Pollen's design note calls that fine on a bench and in an office, not in a home, and a thing to revisit before ducks ship to homes. Anyone who can reach the pad's port, once it is on, can drive it, sit it down and switch it off. Bluetooth is another way in: anyone in range with duckctl and the pairing PIN, the factory 000000 until you change it with sudo robotctl system set-pin, can stand the duck up, run a skill or load another walk, though not steer it.

So keep the duck on a network you would let into the room with it, and never forward these ports to the internet. Pollen's way to reach a duck from outside its network goes through a rendezvous service that checks your account at both ends and keeps the video encrypted. That route stays off until the duck is signed in, and signing in stores a Hugging Face token on the duck that, Pollen's notes say, can push to your repositories, start Jobs and read your billing.

The duck's other channels

The walk reads 61 numbers, but the duck carries more, and Pollen's robotctl, on the duck, shows it:

robotctl monitor

It shows what was asked for beside what was applied, every joint, and the IMU's view of which way is down. Press c for a frame from the head camera, and t for the 8×8 depth grid, which also tells you whether your duck has the depth sensor at all: Pollen's robot docs say most ducks so far have none fitted, while its mapping post describes the duck with one in its head.

To keep the whole state, 50 times a second, for comparing a real walk with the simulator's:

robotctl monitor --json --hz 50 > run.jsonl

A program can also drive. Pollen's UDP pad page describes the 28-byte datagram and has a complete sender in Python, which stands the duck up, turns the walk on and walks it forward for three seconds.

A sensor that steers the duck sends the same datagrams with different stick values, and with a gamepad plugged into a laptop, duckctl udp-pad sends them for you.

Pollen's own mapping post shows these channels working together. Its service, mapd, builds a map of the room from the 8×8 depth sensor, a little at each stop, and finds the duck again after someone carries it, all on the duck's own processor.

Odometry alone, the post measured, drifts about 2.4 cm for every metre walked. The mapper is not in a release yet: a version of it is on the maploc-v2 branch of Pollen's microduck repository, and the recordings it was tested on, with ground truth, are public on Hugging Face as pollen-robotics/SLAM_JJ_Salon_with_GT.

The spare Qwiic port

Can you add a sensor of your own? Pollen has said in its Discord that the duck's board still has an unused Qwiic connector: a small 3.3 V I²C socket, the plug Adafruit calls STEMMA QT.

Pollen's open board, the Robot HAT, has four of them. Its schematic puts J5 on the bus the IMU and the audio codec use, header pins 3 and 5, and the other three on buses that depend on the computer underneath.

Pollen says the final ducks carry an improved version of that board, so look at yours before trusting the open schematic.

  1. See what answers now. On the duck, list the bus and write down every address that answers. On the Zero 3W ducks, Pollen's code expects the audio codec at 0x18, the board's BMI088 IMU at 0x19 and 0x68, connected though unused by default, and the depth sensor, where fitted, at 0x29, or 0x52 on some early ducks. The open board's latest revision has an LSM6DSV16X IMU at 0x6A instead, and Pollen's beta board puts the depth sensor on SPI, where i2cdetect cannot see it:
    sudo i2cdetect -y -r 3
  2. Pick a sensor with an address of its own, one that nothing on the bus already answers to.
  3. Leave the depth sensor's service off while you experiment, which Pollen's cheatsheet calls safe: sudo systemctl stop tofd.
  4. With the duck switched off, plug the sensor in. Switch it on and stop tofd again, since it starts at boot, then run i2cdetect again and look for the new address. If it is not there, i2cdetect -l lists every bus, and the socket may be on another.
  5. Read it from a program, and steer with it, through the UDP pad above, which changes no policy.
  6. Measure the walk again with the sensor on. Anything mounted on the duck changes the body the walk was trained for, so if the walk got worse, that is a case for retraining, with the sensor's weight added to the simulated duck.

Then

Book 4 traces the runs this page reads: why a walk learned to stand, why more practice made it worse, and how a kick learned to go straighter. Lesson 9 is the Qwiic port's, and lesson 10 is about training in another simulator. To race what you train, Race in Pollen's Arena takes the Arena's events one at a time. Found a better way, or a step that failed? Tell us.