Software

The robot description already exists: the same script that draws the 3D model writes a URDF with masses and inertias. Training, simulation and the on-robot loop follow the two parent projects' published code, both of which are permissively licensed.

Robot description

foh_v0_2.urdf is generated by tools/build_data.py from the same part list as the viewer: 24 links, 23 revolute joints, every part as a box or cylinder for both visual and collision, link masses from the catalogue and inertias from the primitives. Frame: X forward, Y left, Z up; the pelvis is the base link. Joint limits are the software limits on the design page; effort limits are the actuators' peak torques. A <mujoco> block in the file tells MuJoCo to keep the coloured visual geoms, so it loads with its materials; other parsers ignore the block.

# MuJoCo reads URDF directly
pip install mujoco
python -c "import mujoco, mujoco.viewer as v; m = mujoco.MjModel.from_xml_path('foh_v0_2.urdf'); v.launch(m)"

# or any URDF viewer
pip install yourdfpy && yourdfpy foh_v0_2.urdf
What the URDF hasWhat it does not have yet
Kinematics, joint axes and limits, link masses and inertias, primitive collision shapes, actuator effort limits, a floating pelvis baseDetailed meshes, contact-tuned foot geometry, joint friction and damping from measurement, actuator armature and reflected inertia, the gripper's prismatic joints

Regenerate after any change to the model with python3 tools/build_data.py; the viewer, the BOM tables and the URDF update together.

Does it stand? A physics check

Before any policy exists, the URDF can be dropped into MuJoCo with a floor, a floating base and one position servo per joint, each limited to its actuator's peak torque. tools/sim_stand.py does exactly that and runs the tests below. They answer what a design should answer before it is printed: the mass sits over the feet, the leg torques while standing and crouching stay far inside the limits, and a joint-stiff robot with no balance controller survives a moderate push. These are not the numbers a trained policy will get; they are the floor it starts from.

MuJoCo frame: the robot standing at the zero pose
Stand: 5 s after a 10 mm drop.
MuJoCo frame: the robot in the crouch pose
Crouch: hips −35°, knees 70°, ankles −35°.
MuJoCo frame: the robot leaning after an 8 N·s push
Push: 8 N·s forward on the torso, recovered.
TestWhat happensMax tiltSettled tiltBusiest jointResult

Reading it. Standing costs the legs under 1 N·m per joint, because the centre of mass lands 10 mm behind the ankle axis and the feet extend 93 mm in front of it and 142 mm behind. The crouch peaks at 16 N·m at the knee, a quarter of the RS03's 60 N·m, with the robot settling 72 mm lower. The pushes are impulses on the torso: the robot rides out 8 N·s (40 N for 0.2 s) on stiff joints alone and topples at 10 N·s. That threshold is the geometry, not the actuators: 10 N·s gives the 27 kg robot 0.37 m/s, whose capture point at a 0.53 m centre-of-mass height is 85 mm forward, right at the toe. A controller that shifts the hips or takes a step moves that limit far out; the bring-up test A8 asks for a 20 N push under the standing policy for that reason.

# reproduce (MuJoCo 3.x, numpy, pillow for the frames)
pip install mujoco numpy pillow
python3 tools/sim_stand.py --render     # prints the table, writes assets/data/sim_results.json and assets/img/sim/*.png

# watch it interactively
python3 -c "import mujoco, mujoco.viewer as v, sys; sys.path.insert(0, 'tools'); import sim_stand; v.launch(sim_stand.build())"

The servo gains in the script (RS03 220 N·m/rad, RS06 150, RS02 80, RS05 25, with damping at 4 % of that) are simulation gains for a stiff hold; the real MIT-mode gains in the control contract are lower and get tuned once the actuators are on the bench.

And walking?

Nothing in the primitive model says whether the robot walks; that needs a controller, and the standard route today is a reinforcement-learning policy. The check for this design is the same path the two parent projects took, in this order:

  1. Static margins (done above): mass over the feet, crouch within torque limits, push recovery on stiff joints.
  2. Torque and speed margins for a gait: the design page compares the RS03's 60 N·m and the RS06's 36 N·m against Duke V2's measured walking loads on the same joints; the knee and hip carry more than a 2× margin at 27 kg.
  3. Train a velocity-tracking policy in Isaac Lab (Berkeley Humanoid Lite's task and rsl_rl config, with this URDF converted to USD) or in MuJoCo Playground with the MJCF that sim_stand.build() compiles. A policy that tracks 0.5 m/s in simulation with masses and friction randomised is the pass mark.
  4. Sim-to-sim: run the same policy in plain MuJoCo with the actuator torque limits and a 2 ms loop, as play_mujoco.py does for Berkeley Humanoid Lite. If it walks there too, the robot's model is not the weak link.
  5. The real robot: acceptance tests A7 to A9, posture then standing then a first step, on the hoist.

The v0.5 milestone on the roadmap is the first of these on hardware; steps 3 and 4 need no hardware and are the next software work.

Stack

LayerChoiceStart from
TrainingIsaac Lab, PPO via rsl_rl, a velocity-tracking locomotion task with domain randomisation on masses, friction and actuator gainsBerkeley Humanoid Lite: same repo layout (source/<robot>, source/<robot>_assets, scripts/rsl_rl), MIT
Sim-to-simMuJoCo, same observation and action contract as the real robotBerkeley Humanoid Lite's scripts/sim2sim/play_mujoco.py
On-robot loopPython on the mini PC: 500 Hz PD in the actuators (MIT mode), 50 Hz policy with ONNX Runtime, SocketCAN via python-canDuke Humanoid V2 deploy (Apache-2.0) for the RobStride protocol, ID/limit/zero scripts and the safety layers; Berkeley Humanoid Lite lowlevel for the policy runner
Teleop and grippersGamepad velocity commands; Feetech serial servo SDK for the grippersDuke V2 hardware_bindings/ft_servo (MIT)
Perception (optional)librealsense2 → the policy's height-map or a separate plannerDuke V2's head-camera pipeline

Control contract

Fixed by the URDF and this page, so that a policy trained by one person runs on a robot built by another.

#JointActuatorDefault (rad)KpKd

Repositories

RepositoryContentsState
fullyopenhml.github.ioThis site, tools/build_data.py, the generated JSON and URDFlive
foh_assetsURDF, MJCF, USD, meshes once they existplanned
foh_lowlevelRobStride driver, bring-up scripts, policy runnerplanned
foh_rlIsaac Lab task, configs, checkpointsplanned