An Open Source, Low Cost, Servo-Actuated Quadruped
An accessible platform for learning quadruped locomotion.
Legged robots that learn to walk via reinforcement learning are typically demonstrated on hardware that costs tens of thousands of dollars and uses quasi-direct-drive actuators with rich torque feedback. Rumi asks a simpler question: how far can you get with low-cost servo motors, a Raspberry Pi, and a single low-end IMU?
We present a complete, open-source sim-to-real pipeline for a 12-DoF quadruped built on Dynamixel MX-64 servos. The robot is modeled in MuJoCo, its actuators are identified from chirp and step responses using CMA-ES, and two policies — velocity tracking and get-up — are trained with PPO in our rumi-mjlab environment. Trained MLP policies run on a Raspberry Pi 4 at a 50 Hz control loop, with observations assembled from joint encoders and a BNO-080 IMU. We share the URDF, hardware design, system-identification code, training code, and the deployment stack so that the platform can be reproduced and extended by others.
Rumi is intentionally built from parts that anyone can buy. The actuators are 12 ROBOTIS Dynamixel MX-64 servos (three per leg) communicating over a shared 3-pin TTL bus. The on-board computer is a Raspberry Pi 4, the only proprioceptive sensors beyond joint encoders are a single BNO-080 IMU, and the chassis is 3D-printed using PLA material.
The chassis is designed parametrically in CadQuery — a Python-based, code-defined CAD toolkit — so the entire mechanical design is reproducible from a script and diff-friendly in git. From there it is exported to URDF, ported to MuJoCo, and the masses, inertias, and joint limits are taken from the CAD model and refined against the real robot.
Servo actuators are the dominant source of the sim-to-real gap on this platform. We drive each motor with chirp and step signals, log torque and position, and fit a simulated actuator (P/D gains, friction, backlash) to the measured response using CMA-ES. The identified parameters are then plugged back into MuJoCo so the simulator behaves like the real motor.
rumi-mjlabTraining is built on top of mjlab — an Isaac-Lab-style API powered by MuJoCo-Warp. We adapt mjlab to register Rumi as a new robot and add two tasks, training each with PPO over 4096+ parallel environments:
Both policies are small MLPs designed to fit comfortably on the Pi.
The deployment stack runs the policy in pure PyTorch (CPU) at 50 Hz. The observation pipeline pulls joint state via GroupSyncRead over Dynamixel SDK and orientation from the BNO-080. The output is a target joint position vector, sent back to the motors with GroupSyncWrite. A USB latency-timer tweak is required to sustain the loop — the repo includes a one-shot script for it.
Not everything works. Two failure modes dominate on the real robot:
Both failure modes inform the next iteration: a better IMU + small contact sensors, and tighter actuator identification under load.
Rumi is open source. Both the training code and the on-robot deployment stack are MIT-licensed and reproducible from off-the-shelf parts.
mujoco-warp, with PPO configs and W&B logging.
A typical reproduction path is: build the chassis from CAD, flash the motors with the IDs in the README, run the single-motor SysID notebook on one MX-64, transfer the parameters to all twelve, then train and deploy.
If RUMI is useful to you, a star on either repository helps others discover it.
The single biggest source of sim-to-real gap on a servo platform is the actuator. To close it, we run a two-stage identification pipeline before any RL training is taken seriously.
One MX-64 motor is mounted on a fixed bracket with a known pendulum load attached to the output horn (a printed arm with a machined mass at the tip). The motor is driven through a sequence of excitation signals while position, velocity, and commanded target are logged at the control loop rate:
f_start to f_end, which is the most informative
signal for identifying actuator dynamics.The recorded traces are then replayed inside a single-joint MuJoCo model, and CMA-ES searches for the actuator parameters that minimize the joint position + velocity RMSE between sim and real:
armature ≈ 0.0162 kg·m² (range [0.0005, 0.5]) damping ≈ 0.594 N·m·s/rad (range [0.05, 5.0]) frictionloss ≈ 0.001 N·m (range [0.001, 1.0]) loss = RMSE(pos) + w · RMSE(vel), with w = 0
A single shared triplet is fit across all joints — the MX-64s are nominally identical, and shared parameters keep the
model honest. We set the velocity-loss weight w = 0 and optimize against position error only.
Empirically, minimizing position error alone also drove the velocity error down, so the extra loss term added noise to
the CMA-ES objective without improving the fit. Code lives in sim2real/sysid_load/ in the hardware repo
(load_recorder.py, sysid_load.py, rollout_sysid.py).
The identified parameters are written back into rumi.xml and validated on the full 12-DoF robot. The robot is
commanded through an IK-driven motion while MuJoCo replays the same commands, and the simulated and real trajectories are
overlaid in a viser 3D viewer.
Feet are anchored to a shared floor reference so the body can shift to match the centroid, keeping the visual comparison
fair rather than dominated by base drift.
Anyone reproducing RUMI is encouraged to repeat both stages on their own hardware: even with the same servo, the friction and damping vary enough between units and over time that re-identifying is more reliable than copying our numbers.
RUMI's training environment is adapted from mjlab — an Isaac-Lab-style API powered by MuJoCo-Warp for RL and robotics research. Most of the heavy lifting for parallel simulation, PPO configuration, and the env/task abstractions comes from upstream mjlab; the contribution of rumi-mjlab is the robot registration, the Rumi-specific reward and observation design, and the curriculum used for the two tasks shown above. We thank the mjlab authors for releasing it openly.
The mechanical design is authored in CadQuery, an open-source Python-based parametric CAD framework. CadQuery makes the chassis fully scripted, version-controllable, and trivially forkable — if you change a leg length, the URDF and MuJoCo model regenerate from the same source. Thanks to the CadQuery maintainers for that workflow.
If Rumi is useful in your research, please cite the thesis:
@mastersthesis{rumi2026,
title = {Sim-to-Real Policy Transfer on a Low-Cost Servo Quadruped under Sensor and Actuator Constraints},
author = {Vishwanath R},
school = {Indian Institute of Technology Jodhpur},
year = {2026},
note = {M.Tech, Robotics and Mobility Systems}
}