Open-source project
AI-FanGe/Microduck-build-tutorial avatar
AI-FanGe/Microduck-build-tutorial

Microduck: a 14-servo biped that walks from an ONNX policy on a Pi Zero 2 W

A practical hardware and software setup for a compact RL-powered biped robot.

922 stars236 forksPythonMIT

At a glance

What is it?
Microduck splits into deployment code for a Raspberry Pi Zero 2 W and a MuJoCo/MjLab training environment that exports walk.onnx. The prebuilt image is the fastest path in, and the servo ID map is the first thing that can go wrong.
Who is it for?
Adopt Microduck if you already own Dynamixel hardware and want a documented path from a flashed image to a walking policy, and if you accept that the README covers assembly, wiring and first boot rather than tuning. Skip it if you want a finished product, a simulation-only workflow, or a robot that does not depend on soldering and a 6V battery pack with adequate instantaneous current.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 16, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What Microduck is, and the two halves it ships as

Microduck is a small biped robot project with a clean split down the middle. The microduck/ directory holds the deployment code that runs on a Raspberry Pi Zero 2 W: it reads an IMU, reads gamepad or keyboard input, drives Dynamixel servos, and loads a walking policy in ONNX format. The mjlab_microduck/ directory is a reinforcement learning training environment built on MuJoCo and MjLab, and its job is to produce the walk.onnx file that the deployment side consumes. The README frames itself as a build and first-run guide, covering hardware, wiring, flashing microduck.img.xz, Wi-Fi setup, SSH login, running the model, and updating code later. 3D printed parts are included in the repository, and the project credits microban and pollen-robotics/microduck as prior open source work that informed the mechanics, deployment approach and training flow.

The audience is narrow and specific. This is for someone who wants a physical biped on a desk and is willing to set Dynamixel IDs by hand, solder power wiring, and print a frame. It is not a simulation tutorial, and it is not a plug-and-play kit. The hardware list is what sets that expectation: one Raspberry Pi Zero 2 W, one ROBOTIS OpenRB-150 servo controller board, fourteen Dynamixel XL330-M288-T servos, a finished 6V rechargeable battery, a BNO080/BNO085/BNO086 IMU over I2C, a 16GB or larger microSD card, and a set of PLA prints from microduck/cad/.

How the Pi, the OpenRB-150 and the servo bus fit together

The data path is short and worth understanding before you plug anything in. The Raspberry Pi Zero 2 W talks to the OpenRB-150 over USB, and the OpenRB-150 sits on the XL330 servo bus. That means the Pi never drives servos directly; it sends commands through the controller board. On the input side, the IMU talks to the Pi over I2C on bus 1, with SDA on GPIO2 and SCL on GPIO3, and the code probes for common BNO08x addresses. Mount orientation is handled by the IMU_MOUNT_QUAT configuration value, which the README says is already matched to the current walking model.

Servo discovery is automatic but ordered. The code prefers a device matching /dev/serial/by-id/usb-ROBOTIS_OpenRB-150_*, and if that is not present it falls back to /dev/ttyACM0, /dev/serial0, /dev/ttyAMA0 or /dev/ttyS0. That fallback chain is convenient and also a trap: if the OpenRB-150 fails to enumerate, the code may still open a serial port and fail later with a less obvious error. The README's own troubleshooting line points at the right order of checks when the bus misbehaves: confirm the OpenRB-150 enumerated, then battery voltage, then GND, then wiring order, then servo IDs.

The servo layout is split across three chains off a single splitter. The right leg runs ID 5 down to ID 1, the left leg runs ID 10 down to ID 6, and the head and neck chain runs ID 12, 11, 13, 14. Physical wiring order can follow whatever is convenient for the frame, but the IDs must match the table exactly. Fourteen servos are used by the walking code, ten in the legs and four in the head and neck. A fifteenth ID is reserved for an optional mouth servo, and the README states plainly that the current walking policy does not depend on it.

Flashing the image and getting to a first walking run

The project publishes a prebuilt image as the image.v1 release, named microduck.img.xz. The README recommends verifying it before writing, since a truncated download produces a boot failure that looks like a hardware fault.

bash
sha256sum microduck.img.xz

The expected digest should match the release page. Writing is done with Raspberry Pi Imager: choose Raspberry Pi Zero 2 W as the device, pick Use custom for the OS and select microduck.img.xz, choose the microSD card, and answer No when asked whether to apply OS customization, because the image already carries its own configuration. A command line route exists for Linux, with the README's warning to be certain about the device name.

bash
lsblk
xzcat microduck.img.xz | sudo dd of=/dev/sdX bs=4M status=progress conv=fsync

Wi-Fi can be set before first boot by editing network-config on the bootfs partition. The README gives the full Netplan-style block and notes that the Pi Zero 2 W only supports 2.4GHz, so a 5GHz-only network will not work. Three placeholders need replacing: the two-letter country code, the network name and the password. Multiple access points can be listed in the same access-points mapping.

yaml
network:
  version: 2
  wifis:
    wlan0:
      dhcp4: true
      regulatory-domain: "<YOUR_COUNTRY_CODE>"
      access-points:
        "<YOUR_WIFI_NAME>":
          password: "<YOUR_WIFI_PASSWORD>"
      optional: true

First boot expands the filesystem, regenerates SSH host keys, reads network-config if you used that route, and starts a headless gamepad service. The README says to wait one to three minutes and not to cut power. The default login is user with password password, and the README suggests changing it with passwd immediately. Reaching the robot over mDNS is the expected path, with a router lookup as the fallback when .local resolution fails.

bash
ping microduck.local
ssh user@microduck.local

Once logged in, the first model run is two lines. Place the robot on a stable surface or hold it, because the program enables servo torque and moves smoothly to a neutral pose before it starts reading input.

bash
cd ~/microduck
PYTHONPATH=src .venv/bin/python src/main.py

The startup sequence is documented: torque on, return to neutral, read keyboard or gamepad, then load src/agents/walk.onnx. Keyboard control uses v to toggle walking, the up and down arrows for forward and back, left and right to turn, x to zero speed, i to toggle IMU display, and q to stop the control loop. If SSH complains that the remote host identification changed, the README's fix is to drop the stale key and reconnect.

bash
ssh-keygen -R microduck.local

Servo IDs, shutdown settings and the failure modes the README admits

The single most likely reason a correctly assembled Microduck will not walk is a servo ID mismatch. The mapping is fixed: 1 right_ankle, 2 right_knee, 3 right_hip_pitch, 4 right_hip_roll, 5 right_hip_yaw, 6 left_ankle, 7 left_knee, 8 left_hip_pitch, 9 left_hip_roll, 10 left_hip_yaw, 11 head_pitch, 12 neck_pitch, 13 head_yaw, 14 head_roll, 15 mouth optional. The README says to set these with Dynamixel Wizard before flashing the image or before the first run, and lists the recommended parameters: protocol 2.0, baud rate 1Mbps, return delay time 0, PWM slope 255, and shutdown with the input voltage error trigger removed so that battery voltage sag does not stop the servos mid-motion.

That last setting is the interesting one. Removing the voltage error from shutdown makes the robot tolerant of a battery that dips under load, and it also removes a protection that would otherwise stop a servo before damage. The README compensates by telling you to confirm the battery can supply enough instantaneous current. If you pair the robot with a pack that cannot, the failure will not be a clean shutdown; it will be erratic behaviour under load.

Power wiring is the second area where the documentation is explicit about what not to do. The current hardware uses a finished 6V rechargeable battery pack, not a self-built 2S pack with BMS and a 2S charge board. The Pi needs a stable 5V supply from a regulator or USB source while the servos and OpenRB-150 run from the 6V battery side, and the two sides must share a ground. Solder joints need heat shrink or hot glue insulation. Before powering up, the README says to check polarity with a multimeter.

A third limitation is scope. The README covers assembly, flashing, networking and first run. It does not document a tuning workflow for the walking policy, and it does not describe how to modify the training environment beyond noting that mjlab_microduck/ exists to train and export walk.onnx. If your goal is to experiment with reinforcement learning rather than to build the robot, the deployment half of this repository is not where the interesting work is.

Where Microduck sits next to microban and pollen-robotics/microduck

The README credits two upstream projects, and the difference in approach matters more than the shared lineage. pollen-robotics/microduck is the original small biped whose mechanics and deployment ideas this project builds on. microban is the other credited reference. Microduck's contribution is the packaging: a prebuilt Raspberry Pi image with the hostname, user, deployment directory, virtual environment, walk.onnx policy and a headless gamepad service already in place, plus a documented BOM and a wiring diagram set.

That packaging is the real difference in approach. Where an upstream project may leave you to assemble a Python environment and figure out which serial port your controller board appears on, Microduck pins the deployment directory at ~/microduck, the virtual environment at ~/microduck/.venv, and the policy at ~/microduck/src/agents/walk.onnx, and it ships a systemd service that starts the gamepad listener at boot. The trade-off is that you inherit someone else's image decisions. If you want a different Python version, a different user, or a different boot behaviour, you are modifying a prebuilt image rather than starting from a clean Raspberry Pi OS Lite install.

For someone who wants to train their own policy rather than run the shipped one, the comparison flips. The training environment here is tied to MuJoCo and MjLab and emits ONNX, which the deployment code loads. A project that trains and deploys through a different simulator or a different export format will not be a drop-in swap, because the deployment side expects src/agents/walk.onnx at that path.

Editorial conclusion

Adopt Microduck if you already own Dynamixel hardware and want a documented path from a flashed image to a walking policy, and if you accept that the README covers assembly, wiring and first boot rather than tuning. Skip it if you want a finished product, a simulation-only workflow, or a robot that does not depend on soldering and a 6V battery pack with adequate instantaneous current. Before flashing, verify three things: that every servo is set to the ID in the table with protocol 2.0 and 1 Mbps, that your Wi-Fi is 2.4GHz because the Pi Zero 2 W does not join 5GHz networks, and that the SHA256 of microduck.img.xz matches the release page. If the servo bus does not enumerate as an OpenRB-150 device, the code falls back to /dev/ttyACM0 and you will be debugging the wrong layer.

Frequently asked questions

What is Microduck and what hardware does it need?

It is a compact biped robot project with deployment code for a Raspberry Pi Zero 2 W and a MuJoCo/MjLab training environment that exports walk.onnx. The BOM lists a Raspberry Pi Zero 2 W, a ROBOTIS OpenRB-150, fourteen Dynamixel XL330-M288-T servos, a finished 6V rechargeable battery, a BNO08x IMU, a 16GB or larger microSD card, PLA prints from microduck/cad/, M2/M2.5 screws and Dynamixel 3-pin cables.

How do I install Microduck and run the walking model for the first time?

Flash microduck.img.xz with Raspberry Pi Imager (choose Use custom and answer No to OS customization), set Wi-Fi in network-config on the bootfs partition, and wait one to three minutes on first boot. Then SSH to user@microduck.local and run cd ~/microduck followed by PYTHONPATH=src .venv/bin/python src/main.py, holding the robot or placing it on a stable surface first.

What servo IDs does Microduck expect, and what happens if they are wrong?

The mapping runs 1 right_ankle through 10 left_hip_yaw for the legs, 11 to 14 for head and neck, and 15 for an optional mouth servo the walking policy does not use. The README says to set them with Dynamixel Wizard before flashing or before the first run, and a mismatch is the most likely cause of a robot that will not walk.

Why does Microduck not connect to my Wi-Fi network?

The Raspberry Pi Zero 2 W only supports 2.4GHz Wi-Fi, so a 5GHz-only network will not work. If you configured Wi-Fi by editing network-config, check that the two-letter country code, network name and password placeholders were all replaced.

Does Microduck use the optional mouth servo?

No. The README states that ID 15 is reserved for a mouth servo and that the current walking policy does not depend on it, so the fourteen servos used are the ten in the legs and four in the head and neck.

Official sources

  1. AI-FanGe/Microduck-build-tutorial on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Community notes

Community notes