Skip to content

BusyBox

BusyBox is a 3D-printable panel of everyday physical controls — buttons, switches, sliders, a knob, and pluggable wires. It was designed at Microsoft Research as a benchmark for affordance generalization: whether a robot foundation model that has learned to press one button can press a button it has never seen, on a panel it has never seen.

Armnet runs an instrumented BusyBox: the panel is wired to a Raspberry Pi that reports the live position of every control. That is what makes it an automated environment — the box knows whether the switch actually flipped, so it scores your rollout itself rather than asking a human what they saw, and the arm stages the next episode rather than waiting for someone to tidy up.

Why it is a good benchmark

The controls are deliberately generic. A toggle switch on a BusyBox looks and behaves like a toggle switch on a washing machine or a light fitting, so a policy that can work the panel is demonstrating a skill that transfers, not a scene it has memorised. The modules are also reconfigurable, so the same set of affordances can be presented in arrangements a policy has not been trained on.

For Armnet specifically the practical advantage is consistency: every episode is scored the same way by the same sensor, rather than by whichever person happened to be watching. Pair that with a reset the arm performs itself and a benchmark run needs no operator at all — it can go all night.

How scoring works

Install armnet-busybox in your image

Scoring and resets happen through the robot your container is driving, so the environment has to be installed alongside your job:

RUN pip install armnet-busybox

There is nothing to import — ctx.cell.instrument(robot) finds it through the armnet.environments entry point, and everything below then happens on its own. The managed CLIs (armnet-policy-eval and friends) already include it. Leave it out and the job still runs, but a person has to judge and reset every episode from the FMS.

Each task carries a goal — "the left toggle is up", "the knob reads 3", "the green button was pressed, then the yellow one". After every control tick the cell reads the panel state and compares it against that goal. When the goal is met the episode ends immediately and is recorded as a success.

Two details are worth knowing:

  • Buttons are momentary. By the time an episode ends the button has sprung back up, so the box counts presses rather than sampling a held-down state.
  • A rollout can become unwinnable. If an ordered task presses the buttons in the wrong order, the box can tell the goal is no longer reachable and ends the episode early as a failure instead of running down the clock.

How resets work

Resets are performed by the arm, not by a person. Between episodes the cell works out what the panel needs to look like for the task about to run and replays a stored motion plan to put it there. Controls already in a valid starting position are left alone — a slider task accepts any detent except the goal, so most episodes start without the arm staging anything, and the ones that do start from a different distance each time.

A motion plan may temporarily move a rail to the pose where its keypoints were taught. That pose is reset staging only: after the arm returns to rest, the runtime moves every rail directly back to the job's requested position before the next episode starts. It does not home again unless the edge detects that its recorded position is no longer safe to trust.

Each motion is taught once, by a person driving the arm through it, and the plans are being recorded task by task. Until a task has one, the cell asks an on-site operator to stage the panel, so that task runs at operator pace rather than unattended. Nothing about your job changes when a plan lands — the same submission simply stops waiting.

Buttons never wait either way: they are momentary and spring back on their own, so a button run is unattended today.

Tasks

The BusyBox cell is single-armed, so submit these with --armnet.embodiment=lerobot/so-101. Naming the task is what routes your job here; you never address the cell directly.

Every task listed below is scored by the panel itself. Where they differ today is how much staging they need between episodes, which is what the reset section above describes.

Start with the buttons

A button run needs no staging at all, so it is fully unattended and can go all night. push_green_button is the one used throughout these docs, and it has both a dataset and a trained policy to go with it.

Buttons

Momentary — the button springs back, so the panel counts the press.

Task slug Goal
push_green_button Push the green button
push_red_button Push the red button
push_white_button Push the white button
push_yellow_button Push the yellow button

Switches

Two toggle switches, each with an up and a down target. busybox_left_toggle_up is staged by the arm today, because the motion that flips that switch back down is the first one to have been taught.

Task slug Goal
busybox_left_toggle_up Turn the left toggle switch up to "On"
busybox_left_toggle_down Turn the left toggle switch down to "Off"
busybox_right_toggle_up Turn the right toggle switch up to "On"
busybox_right_toggle_down Turn the right toggle switch down to "Off"

Sliders

Two sliders with five detents each. An episode may start from any detent that is not the goal, so the arm covers a different distance each time.

Task slug Goal
busybox_move_left_slider_to_1_to_5 Move the left slider to position 1–5
busybox_move_right_slider_to_1_to_5 Move the right slider to position 1–5

Knob

A six-position rotary knob.

Task slug Goal
turn_knob_to_1turn_knob_to_6 Turn the knob to position 1–6

Wires

Four coloured wires that can be plugged in or pulled out.

Task slug Goal
insert_blue_wire / pull_blue_wire Insert or pull the blue wire
insert_green_wire / pull_green_wire Insert or pull the green wire
insert_red_wire / pull_red_wire Insert or pull the red wire
insert_yellow_wire / pull_yellow_wire Insert or pull the yellow wire

Mixed

Task slug Goal
busybox_mix Draw a different single-control task each episode

Not yet available

busybox_mix is intended to spread one run across the whole panel — a different button, toggle, slider or knob position each episode — so a result says something about the box rather than about one spot on it. The slug is reserved and accepted by the API, but the per-episode task selection behind it is not deployed, so do not use it for a benchmark yet.

Bimanual

Not currently available

The BusyBox panel is attached to a single-arm cell today, so these two-arm tasks cannot be scheduled. The slugs are listed for reference and will become available again when a bimanual BusyBox cell comes online. Submitting one now will not find a matching cell.

Two-step tasks that use both arms in order.

Task slug Goal
busybox_buttons_bimanual Press the green button with the left arm, then the yellow button with the right
busybox_switches_bimanual Flip the top switch on with the left gripper, then the bottom switch on with the right
busybox_button_slider_bimanual Push the green button with the left arm, then move the top slider to position 2 with the right
busybox_slider_knob_bimanual Move the top slider to position 4 with the left arm, then turn the knob to position 3 with the right
busybox_slider_switch_bimanual Move the bottom slider to position 3 with the left arm, then flip the top switch on with the right gripper
busybox_switch_knob_bimanual Flip the bottom switch on with the left gripper, then turn the knob to position 5 with the right

Reference datasets

Three teleoperated datasets are published on the Hugging Face Hub. All three are recorded on the Armnet BusyBox cell, so a policy fine-tuned on them sees the same camera angles and the same panel it will be evaluated against.

Dataset Use it to
villekuosmanen/busybox_push_green_button Fine-tune a single-task policy for push_green_button. The smallest useful starting point, and the one to use if you just want to confirm your training recipe works end to end.
villekuosmanen/busybox_flip_left_switch_off Fine-tune a single-task policy for busybox_left_toggle_down. A good second task for checking whether your recipe transfers between affordance types.
villekuosmanen/busybox_multitask Train a language-conditioned multi-task policy. Covers every task above, so the instruction string is what selects the behaviour.

Start from one of the single-task datasets to get a policy working end to end, then move to busybox_multitask once you want one checkpoint to handle the whole panel.

Two diffusion policies trained on the single-task datasets are published as well, so you can run a real evaluation before training anything of your own: busybox_push_green_button_diffusion and busybox_flip_left_switch_off_diffusion.

Run an evaluation

armnet-policy-eval \
  --policy.path=villekuosmanen/busybox_push_green_button_diffusion \
  --armnet.embodiment=lerobot/so-101 \
  --armnet.task=push_green_button \
  --eval.n_episodes=10 \
  --armnet.episode_time_s=30

Because the panel scores itself, pc_success in the result comes straight from the sensors — you do not need to watch the run or review the video afterwards.

See Also