Microduck Studio docs
From your first policy to a result someone else can reproduce. Guides for exploring Microduck on your phone, and measuring its motions in physics.
Getting started
You can start with the policies and recorded motions included in the app. No robot, account or network connection is needed to explore them.
- Install the beta. Get Apple's free TestFlight app, then join the Microduck Studio beta on your iPhone or iPad. There is no App Store release yet.
- Explore a policy. Open Behaviours and choose a policy from Released by Pollen Robotics. Its detail shows the network's structure and where it came from. Use Probe this network to explore how its inputs affect its actions.
- Play or make a motion. Open Studio to find motions and scenes. Play a recorded motion, or create a draft and edit its keyframes. A preview shows the poses you asked for.
- Measure it in physics. Follow the bench setup guide to run the bench on a computer, then enter the address it prints in the app. Use a measured run to find out what your policy or motion actually does.
Simulation comes first. Nothing here has run on hardware. The policy inspector, motion previews and physics bench answer different questions. Read what the results mean before treating a preview or a score as evidence about a real robot.
Inspect a policy
Start in Behaviours. Policies are grouped by their provenance: Released by Pollen Robotics and From elsewhere. The app checks the network's fingerprint; renaming a file does not make it an official release.
Bring your own network
Open an .onnx file from Files, AirDrop it to your device, or
import a public model using its HTTPS URL. Use a direct download URL for the
model file. A repository's web page is not the model itself.
If the file is refused, it stays in your library. Open its detail to read the reason alongside the operations, layer widths, parameter count and tensor names the app found. That gives you something concrete to check in your export.
Probe what it listens to
Choose Probe this network and start from an observation preset. Change an input and compare the actions, joint targets and travel limits. The input's z-score shows how far it sits from the network's training distribution; the sensitivity ranking shows which inputs affect its output.
This is a forward pass through the network. To see a policy walk in a simulated world, run it on the physics bench.
Write a motion
In Studio, create a motion by posing the robot and editing its keyframes. Or open Draft with words, choose Motion and describe what you want. The model's draft goes through the same joint and travel checks as a motion you write yourself, then opens for you to edit.
Choose a scene and preview the result. The preview interpolates your poses and times; it does not calculate whether the robot could perform the move. The motion's Sim to real row records whether it has been written, previewed or run on a bench.
Export your authored motion as .duckmove to keep a copy or
share it. A .duckintent holds a recorded policy run in physics.
See file formats for the distinction, and
reproduce a published number when you are ready
to check a measured result.
Evaluations
One good run is an anecdote. An evaluation is a number somebody else can check.
Microduck Studio takes its evaluation vocabulary from inspect-robots, an open source framework for evaluating physical AI. Its one big idea is that a robotics evaluation has two swappable inputs where a language model evaluation has one. A policy turns an observation into actions. An embodiment is the body and the world that executes them. A task is defined against neither: it is a set of scenes, a set of scorers, one horizon, and a number of epochs with a rule for reducing them.
In the app that becomes a setup screen with four presets, and a preset is a whole plan rather than a starting point. Picking one fills the scenes, what varies between the epochs of a scene, the rule for reducing them, the horizon and the scorers. Two of the four walk a network under a command schedule from drop heights it was never tuned on. The other two run one episode per published cell of a challenge grid, scored by the challenge harness rather than by the app.
What is left to pick is the thing being evaluated and where it runs. The thing being evaluated is a network from your library, named by digest, or a published challenge entrant. Where it runs is an embodiment: a bench on your network, named and carrying its world's digest, or this phone's own bench. A real Microduck is on that list and cannot be chosen, because nothing can drive one yet.
There is no epoch count, no reducer picker and no seed field on that screen. A control that could disagree with the preset would be a second opinion about what the preset means, and the four presets are the whole argument.
What comes back is an EvalLog: one immutable JSON file that carries the whole specification beside the results, so a number can always be traced back to the run that produced it. It records the task, the policy, the embodiment and its world, the horizon, when it started and finished, the metric for every scorer, and every scene's per epoch scores as well as the reduced one. Trials that errored are recorded and never scored, and a run where every trial errored is a failure rather than a zero.
What it does not record is a seed, and the file says so in its own words:
No seed was recorded, because no route on this bench reads one and a seed here would name a control nothing used. What varies between the epochs of a scene is the height the duck is dropped from, and every height is listed.
One of inspect-robots' reducers is missing here for the same kind of reason, and the app says which and why:
pass at k is not offered. It estimates how often a success would appear in k independent draws, and these epochs are not draws. They are a fixed list of drop heights the bench walks in order, so the estimator's assumption is false here and its number would look like a probability without being one.
- Policy
- Observation in, actions out. Named by digest, not by filename, because
anybody can call a file
alpha_walking.onnx. - Embodiment
- The body and the world. A bench on your network, or this phone's own bench. A real Microduck is named and unavailable: nothing can drive one yet.
- Task
- Scenes, scorers, one horizon, and epochs with a reducer: mean, median, max, min or mode.
- Scorer
- A pure reader of the recorded trajectory. It cannot change what happened, only report on it.
- EvalLog
- Version 1, strict JSON, sorted keys, non finite floats written as null. The filename is the task slug and a short hash.
What the app does not do is run inspect-robots. An iPhone cannot run Python, and it has no physics engine either. The rollouts happen on a bench, the scoring reads the trajectory the bench recorded, and the file is written to inspect-robots' schema rather than by inspect-robots' code. That distinction matters enough that it is tested rather than promised: a check in the app's repository writes a log, reads it back with the real library, and fails if their reader refuses it.
The field that would carry inspect-robots' version instead carries a sentence naming Microduck Studio and saying that no inspect-robots ran. Their own viewer prints that field in its header, so a stranger who renders one of these logs is told, in their own report, what wrote it.
An evaluation you have run can be saved, exported as its JSON, rendered as a
self contained report the app draws itself, sent through the share sheet, or
published to a Hugging Face dataset under your own account. A log written
somewhere else can be opened on the phone. On a computer,
inspect-robots view LOG.json renders the same file as a standalone
HTML report, and read_eval_log will refuse it outright if the schema
is wrong, which is the point of writing the real one.
An EvalLog is a record and not a submission. The two challenge screens are the only path onto either leaderboard, and a log of a challenge run does not put anything on one.
The bench
This is the piece that turns a preview into a measurement. It is a small Node program that runs MuJoCo on a computer you already own, a Mac, a PC or a Raspberry Pi, and the app talks to it over your own network.
It is a desktop download. It runs on a Mac, a PC or a Raspberry Pi, and it does not run on the phone.
Starting it
# Windows: right-click start.ps1, then Run with PowerShell
# macOS and Linux:
./start.sh
The first start installs MuJoCo and onnxruntime and takes a couple of minutes. Every start after that is seconds. It prints the address to type into the app, or to curl.
Use the Tailscale address it prints, not a Wi-Fi one. A 192.168 address works right up until your phone prefers cellular or you leave the room, and then it fails looking exactly like a bench that has gone down.
What travels with it
The bundle carries scene.mjb, the physics world every
measurement is made against, and its digest. A number measured against a
different world is not comparable to one measured here, which is why the world
ships with the code rather than just its filename.
3f8c9ab9b409ba74c73c30179d5f7c12b025f631693f9eec78d80dca242547be
That is the plant every number on this page was produced against. It is printed in full rather than shortened, because a shortened digest is a digest nobody can compare.
The bench inside the phone
The app also carries the bench, served to itself over HTTP on 127.0.0.1, so that it has one client for one protocol rather than two. Its speed on a phone has never been measured. The figure that gets quoted for the core came off a desktop browser on a Raspberry Pi 5, which is not an iPhone, so the app reports the tick its own bench measures wherever it actually runs rather than quoting somebody else's machine.
You can drive the same core in Safari without the app. The phone bench probe tells you what your own phone can and cannot do with it. It is a probe and not a bench: it is there so you find out before the app asks you to trust a number.
Challenges
Two published challenges. Both open, both unmet, both scored against the same compiled world by the harness in duckbench.
Stairs
Get the duck from the floor onto a step in simulation and leave it standing there, upright on the tread with both feet resting on it, fifty ticks after the move ends.
The criterion, as sim/climb_score.mjs exports it:
honest: at the scored instant the trunk is upright, past the riser at
x > 120 mm, more than 95 mm above the tread, with at least two feet resting
on a tread (past the riser, within 5 mm below to 45 mm above it, and within
3 mm of a step), and the duck never left the 340 mm-wide flight at any tick
of the episode. stable: honest, and upright for at least 45 of the 50 tail
ticks.
The bar is 7 of the 9 core cells, cleared and still standing. Nothing has met it: the record is 5 of 9 at a 60 mm rise, and round six measured the trunk's peak height as the reason 7 was never reachable at this scale.
A submission is scored over fourteen cells of which nine are the core every published number is quoted against. Nineteen rows are published: sixteen entries, two of them reference oracles, and three reference controls, with the intent files they name, byte for byte as the audit ran them. The negative result is published rather than hidden.
Ball
A ball is put down in front of the duck, between 450 mm and 1.2 m away and up to 40 degrees off its heading, and the duck has to reach it and move it in simulation without falling over.
The criterion, restated from sim/chase_score.mjs: chased means
the duck touched the ball, meaning any duck geometry came within 3 mm of it at
any tick, and the ball finished at least 100 mm further along the duck's initial
heading than it started, and the duck was still upright at the end of the
episode. Stable means chased, and upright for at least 45 of the 50 tail
ticks.
Four control rows and no entries. Standing still and both of Pollen's kick policies chase nothing at all; walking straight ahead takes 4 of the 9 core cells and 1 of the 5 extended, and every cell the controls miss needs steering, which nothing bundled does.
The nine reward terms are the reward these policies were trained on, and they are reported so an edit can be watched moving them. They are not the verdict: a shaped sum of nine weighted terms is not a thing a person can hold in their head, and a leaderboard sorted on it would reward a duck that stands beautifully still.
One caveat this challenge carries. The kick config was trained against a 70 mm, 15 g ball. This plant's ball is 100 mm and 30 g, so a speed in metres per second measured here is not comparable with anything Pollen published.
Both grids, both entrant corpora and every published score live in the datasets below. The app scores an entrant on the published grid, through the harness's own episode function, against the same compiled world, and it says so beside every number, because a score measured somewhere else is not a score on this leaderboard.
Stairs challenge dataset
Ball challenge dataset
Submit a result
A submission is not a number. The file holds the move, all fourteen per-cell answers unrounded, the bench's plant digest and the date. It is written to this device and nothing is sent until you pick where it goes.
Where it goes is a GitHub issue on duckbench with the file attached. That is the submission a maintainer sees. Publishing the same file to a Hugging Face dataset under your own account, under CC BY 4.0, is your archive and submits nothing to anyone.
Reproduce a published number
Five steps, and no app required.
- Get the bench and check it. Download the bundle above, run
shasum -a 256 duckbench-bundle.zip, and compare the answer with the digest linked beside the download. - Start it.
./start.shon macOS or Linux. The first start installs MuJoCo and onnxruntime. - Check the plant.
curl -s http://ADDRESS/healthand read the plant name and digest out of the answer. If the digest is not the one printed above, stop. Whatever you measure will not be comparable with anything on this page, and the honest thing to do with an incomparable number is not to publish it. - Get an entrant. The published intents live in the two
datasets, and the same files with the same digests are in
duckbench/challenge/intents. - Score it, and compare the right column. Post the entrant
to
/climbor/chaseone cell at a time, taking the cell list from/climb/gridor/chase/grid, or run the harness directly. The stairs table ranks on cleared core cells and prints the stable count beside it; the ball table ranks on chased and prints stable beside it. Quoting one without the other is quoting a number the table does not rank by.
The app calls sixteen bench routes and no others, and the bench inside the phone answers exactly that set and returns 404 for anything else, so a route that exists on one bench and not the other cannot hide.
File formats
The app reads back every format it writes. That was not always true and fixing it is why these are documented.
| Extension | What it holds |
|---|---|
.duckmove |
A motion: keyframe times and one angle per joint. Poses and times interpolated, with no physics run. |
.duckplan |
A fetch plan: the measured object and what was asked for. The steps are derived when it is read, so a plan cannot go stale and disagree with the app that opens it. |
.duckintent |
A recording of a policy running in physics, with the world it was recorded in named inside it. |
.onnx |
A trained policy. The app checks its input and output widths against the robot the app knows about, and tells you exactly why it refuses one that does not match. |
.json |
A formal evaluation: the task, the policy, the embodiment and its world
digest, the horizon, every scorer's metric, and every scene's per epoch
scores. inspect-robots' own schema, so read_eval_log and
inspect-robots view open it unchanged. |
What is honest about this
Two claims are easy to make about a robot app and neither one is made here, and there are four more things worth saying out loud.
A preview is not a result. A preview on a phone is what you asked for, not what the robot would do. The bench was asked, and the four authored stair motions in this app get up their flight 0 times in 16. Nothing on a phone screen would ever have told you that.
A success rate is not an answer. The bench's criterion is "ends standing", which a duck standing perfectly still passes every time. So the distance travelled is shown beside the rate, always, and a leaderboard is never sorted on a shaped sum of reward terms.
Nothing here has run on hardware. Every number on this page and in the app came out of simulation, in a named world, with that world's digest attached to it. When a real Microduck arrives, none of these numbers transfers for free.
The bench inside the phone has never been speed measured on a phone. The figure that gets quoted for the core was measured in a desktop browser on a Raspberry Pi 5. That is not an iPhone, and the app does not pretend otherwise: it reports the tick its own bench measures where it actually runs.
The ball challenge's numbers are about this ball. The kick config was trained against a 70 mm, 15 g ball and this plant's ball is 100 mm and 30 g, so a speed measured here is not comparable with anything Pollen published.
This site borrows a framework's vocabulary, one way. Microduck Studio adopts inspect-robots' evaluation vocabulary and log format. It is not made by, endorsed by, or affiliated with inspect-robots, and no inspect-robots code runs in the app.
Downloads and links
Everything is public. Our four repositories, the two challenge datasets, the bench bundle, the phone bench probe, and the two projects this one stands on.
Get the app
- TestFlight beta Free. iPhone and iPad, iOS 17 or later. Needs Apple's TestFlight app.
- App Store: not yet, and there is no date.
The code
- duck-studio The app itself, and StudioKit, the package that holds everything it computes. Apache-2.0.
- duckkit The Microduck as pure Swift: its joints, their travel, its kinematics. The app pins a released tag of it.
- duckbench The bench and the challenge harness. The physics, and the scoring functions the challenge numbers came out of.
- microduck-com This site. Static HTML and CSS with no build step, so you can read the whole thing.
The data
- Stairs challenge The grid, the entrants, the published scores, and the round that measured why the bar is out of reach.
- Ball challenge The grid, the entrants and the published scores.
- Submit a result An issue on duckbench with the submission file attached. That is the path a maintainer sees.
The tools
- The bench bundle 11.7 MB, with its sha256. MuJoCo, onnxruntime, and the compiled world every number is measured against. A desktop download; it does not run on the phone.
- Phone bench probe The same core in Safari, so you can see what your own phone manages before you trust a number from it.
- inspect-robots and its documentation The evaluation framework whose vocabulary and log format the app adopts. MIT licensed, and not ours.
Elsewhere
- Pollen Robotics' Microduck The robot itself, from the people who make it. This app is not theirs.
- Privacy No accounts, no analytics, no tracking, and the reason this page loads nothing from anywhere else.