Automated Onshape→MJCF Modeling
This chapter is the book's core innovation: replacing the original replica's manual inertia-entry approach with a full reproduction of Pollen's CAD→MJCF automated pipeline, which computes each link's mass, center of mass, and inertia tensor automatically. Companion scripts:
scripts/cad2mjcf/export_mjcf.py+onshape_config.json.
4.1 The Pain Point: Why Manual Inertia Estimation Fails
The original microduck-replica MJCF model estimates link mass, inertia, and center of mass by hand. The simulated gait looks perfect, but deploying to hardware causes immediate tipping — the root cause is exactly that the dynamics model does not match the real robot (the Sim2Real gap).
Pollen's original MicroDuck solution: SolidWorks assembly → import into Onshape, assign part densities → onshape-to-robot automatically exports MJCF, computing inertia tensors from geometry. This book reproduces that pipeline in full, adapted for the domestic HD1910 servo.
4.1.1 The Full Workflow Chain
STEP assembly → Onshape import → assign part densities → servo-mass override config → onshape-to-robot export of STL meshes + MJCF → automatic MJCF validation → replace the simulation robot XML
4.2 Onshape Prerequisites
- Import the full-assembly STEP into an Onshape document;
- Assign each 3D-printed part a PLA-equivalent density:
1240 kg/m³; - For HD1910 servo parts, do not compute mass from geometry; force-override the mass to 21 g in the config;
A servo's internal coreless motor, magnetic encoder, and gear train are structurally complex, so geometry-derived mass is inaccurate; the manufacturer's nominal weight must override it.
- In the Onshape assembly, define the joints (revolute), and record the joint IDs, rotation directions, and physical limits.
4.2.1 PLA Equivalent-Density Reference
| Infill ratio | Equivalent density (g/cm³) |
|---|---|
| 20% | 0.35–0.45 |
| 40% | 0.6–0.7 |
| Solid PLA | ~1.24 |
4.2.2 Preparing Onshape API Keys
Apply for Onshape API keys so the script can remotely read the assembly geometry:
- Onshape personal account → developer console → create an API key;
- Export two environment variables:
export ONSHAPE_ACCESS_KEY="your_access_key"
export ONSHAPE_SECRET_KEY="your_secret_key"
Do not commit the keys to Git; add them to
.gitignore.
4.3 Configuration File Walkthrough: onshape_config.json
Path: scripts/cad2mjcf/onshape_config.json
{
"url": "https://cad.onshape.com/documents/[document-id]/w/[workspace-id]/e/[assembly-id]",
"output_format": "mjcf",
"output_dir": "./output",
"mesh_type": "stl",
"units": "meters",
"joints": {
"thigh_right": {"type":"revolute","limits":[-1.57,1.57]},
"thigh_left": {"type":"revolute","limits":[-1.57,1.57]},
"knee_right": {"type":"revolute","limits":[-1.57,1.57]},
"knee_left": {"type":"revolute","limits":[-1.57,1.57]},
"hip_pitch_right": {"type":"revolute","limits":[-1.57,1.57]},
"hip_pitch_left": {"type":"revolute","limits":[-1.57,1.57]},
"hip_roll_right": {"type":"revolute","limits":[-1.0,1.0]},
"hip_roll_left": {"type":"revolute","limits":[-1.0,1.0]},
"hip_yaw_right": {"type":"revolute","limits":[-1.0,1.0]},
"hip_yaw_left": {"type":"revolute","limits":[-1.0,1.0]},
"ankle_pitch_right": {"type":"revolute","limits":[-1.57,1.57]},
"ankle_pitch_left": {"type":"revolute","limits":[-1.57,1.57]},
"ankle_roll_right": {"type":"revolute","limits":[-0.8,0.8]},
"ankle_roll_left": {"type":"revolute","limits":[-0.8,0.8]}
},
"override_mass": {
"hd1910_servo": 0.021
}
}
units: meters: MuJoCo enforces SI units (meters, kilograms, seconds);limitsare in radians and must match the HD1910 mechanical hard stops exactly, preventing joint overtravel in simulation;override_mass: force-overrides the servo mass, taking precedence over geometry-derived values.
4.4 One-Command MJCF Export: export_mjcf.py
Path: scripts/cad2mjcf/export_mjcf.py
Script logic:
- Read the JSON config;
- Call the
onshape-to-robotlibrary to access Onshape remotely and export STL meshes and MJCF; - Auto-invoke the MJCF validation script after export;
- On validation success, prompt to copy
robot.xmlto the simulation assets directory.
4.4.1 Execution Command
python scripts/cad2mjcf/export_mjcf.py --config scripts/cad2mjcf/onshape_config.json
Normal output:
=== Starting onshape-to-robot MJCF export ===
✅ Export complete, validating MJCF model
✅ MJCF loaded successfully, link count: 16
✅ Total mass: 0.726 kg
✅ Basic MJCF validation passed
✅ All done! MJCF path: scripts/cad2mjcf/output/robot.xml
Tip: copy this robot.xml to software/training/assets/robots/microduck_feetech.xml
4.5 What the Validation Script Does (validate_mjcf.py)
- Load the MJCF model and read the link count;
- Compute the total machine mass;
- Iterate over every link and check that the inertia tensor is positive-definite (ixx, iyy, izz must all be > 0);
A non-positive-definite inertia tensor causes numerical instability and simulation explosion in MuJoCo.
4.6 The Closed-Loop Iteration (new in this book; absent from the original replica)
Exporting MJCF from CAD is only the first step. The complete loop:
- Export the initial MJCF from Onshape;
- Run the standing demo in simulation and observe center-of-mass stability;
- 3D-print the parts, weigh them individually, and record in
data/cad_weigh_records/;- Compare the CAD-computed mass against the real weight;
- Fine-tune part densities in Onshape and re-export MJCF;
- Iterate until the CAD-computed vs. measured mass error is < 5%.
4.7 Engineering Pitfall Log
- Unit error: Onshape uses millimeters; exporting without converting to meters scales the model by 1000× and crashes the simulation immediately;
- Joint limits entered in reverse, driving the simulated joint in a physically forbidden direction;
- Servo without
override_mass: geometry-derived mass is too small, shifting the simulated center of mass upward; - Broken STL mesh faces, causing MuJoCo collision-detection errors.
4.8 Chapter Summary
This chapter automates dynamics modeling, no longer relying on empirically estimated inertia.
But robot simulation needs more than rigid-body dynamics — it also needs the servo model. The next chapter introduces the BAM servo model and fits its parameters using HD1910 bench measurements.