Add Unitree robot URDFs and physics simulation CLI#180
Merged
Conversation
Phase 1 of an official-URDF effort: ship simplified primitive-geometry URDFs that exercise the full kinematic and physics pipeline today, plus a `vcad simulate` CLI command so the demo is one command from the terminal. The simplified URDFs use only boxes / cylinders / spheres because the URDF reader's <mesh> handling is currently a no-op (it routes mesh references to CsgOp::StepImport, which only loads STEP). Topology, joint origins, axes, and limits track the real Unitree G1 (23 actuated DOF) and Go2 (12 actuated DOF). Follow-up phases will pass URDF <inertial> through the IR, add STL import, and vendor the official URDFs. Includes integration tests that load both robots and step the physics world to confirm finite states, plus a changelog entry. https://claude.ai/code/session_01HtymVXbgcAvFXrPkPv33MA
Phase 2 of the official-URDF effort. Add InertialProperties to vcad_ir::PartDef so authored mass / inertia tensor / centre-of-mass can travel from a URDF <inertial> tag all the way to phyz, without being re-derived from a tessellated mesh + density. PhysicsWorld::from_document now prefers PartDef.inertial when present, falling back to the existing bounding-box estimate when it isn't. The URDF reader populates the field for every link that ships an <inertial> block; the unit conversion mirrors the rest of the importer (kg + kg·m² stay SI, COM offset converts metres → millimetres for IR). Extends the Unitree integration test to assert the G1's authored mass sums into a sensible whole-robot ballpark, so a future regression that silently drops <inertial> is loud. The new field is `Option<InertialProperties>` with `serde(default)`, so existing on-disk .vcad files round-trip unchanged. Every Rust construction site now passes `inertial: None` explicitly — no behavioural change in the editor / loon / mecheval paths. https://claude.ai/code/session_01HtymVXbgcAvFXrPkPv33MA
Phase 3 of the official-URDF effort. Add CsgOp::MeshImport for triangle-
mesh files and a stl_io-backed loader so URDF <mesh filename="..."> tags
resolve to actual geometry instead of broken StepImport stubs. Includes
package:// URI resolution against caller-supplied package roots.
Pieces:
- vcad-ir: new CsgOp::MeshImport { path, scale }. Stub handlers added
in every match site that already handled CsgOp::StepImport (loon,
vcode, wasm, evaluate, validate, repl).
- vcad-kernel-physics::stl: load_stl() reads binary or ASCII STL via
stl_io and converts to a TriangleMesh in vcad's millimetre frame,
applying URDF <mesh scale="..."> factors.
- PhysicsWorld::evaluate_part: bypass the BRep-solid path for
MeshImport — load straight into TriangleMesh and feed to the
convex-hull collider builder.
- vcad-kernel-urdf: new UrdfReadOptions { package_roots, urdf_dir }
plus read_urdf_with_options / read_urdf_from_str_with_options. The
reader resolves package://NAME/path against each root, falling back
to a 1cm placeholder cube when a mesh can't be found (so the rest of
the kinematic + inertial tree still loads).
- vcad-cli: `vcad simulate --package-root DIR` (repeatable) for ROS
workspaces.
Tests: a synthetic 1-metre triangle round-trips through the loader at
1000 mm with correct scale handling, and an end-to-end test confirms
package:// URIs resolve to absolute paths via the URDF reader.
https://claude.ai/code/session_01HtymVXbgcAvFXrPkPv33MA
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Phase 4 of the official-URDF effort, and the first end where you can
point `vcad simulate` at an upstream URDF and get sensible behaviour.
Vendored from `unitreerobotics/unitree_ros`:
- examples/unitree-g1-official.urdf — 23-DOF humanoid, references STL
meshes (load by running against the URDF in its native dir or by
vendoring meshes alongside).
- examples/unitree-go2-official.urdf — quadruped, references DAE
meshes; vcad doesn't load DAE yet so links resolve to 1cm
placeholder cubes, but joint topology and authored <inertial> still
flow through correctly.
- examples/UNITREE.md — explains the four URDF fixtures and how to
enable mesh resolution.
Importer fixes that made loading real URDFs possible:
- Reorder <robot> children before serde-deserialise so interleaved
<link>/<joint>/<material> siblings don't trip quick-xml's
"duplicate field" error. The pre-pass walks the XML with quick-xml
events, captures byte ranges per top-level element, and rebuilds
a normalised string with all materials → links → joints → other.
- Link.visual: Option<Visual> → visuals: Vec<Visual> (and same for
collisions / Visual.material). Real-world URDFs (Unitree's Go2 in
particular) attach multiple visuals or multiple <material> children
to a single <visual>. The first entry is used as the primary.
- Skip MeshImport for non-STL extensions (.dae, .obj, …) so DAE
references don't reach the STL parser as garbage. They fall back to
the placeholder cube path.
Tests: load both vendored official URDFs, assert sensible link / joint
counts (33 / 32 for G1 23-DOF, 42 / 41 for Go2), check authored mass
sums into the right whole-robot ballpark, and step the physics world
without diverging.
https://claude.ai/code/session_01HtymVXbgcAvFXrPkPv33MA
Replace the 0.004166666 magic literal with `1.0 / 240.0` (via `default_value_t`), and explain in the help text that 240 Hz is the standard rigid-body timestep used by phyz / Rapier / Bullet. https://claude.ai/code/session_01HtymVXbgcAvFXrPkPv33MA
Phase 5 of the official-URDF effort: you can now drop a `.urdf` file
onto the viewport at vcad.io and the robot loads as a regular vcad
Document with assembly + joints + authored inertials. The Simulate tab
+ Play button just work from there.
Pieces:
- vcad-kernel-wasm: new `importUrdfBuffer(data)` export wrapping
`vcad_kernel_urdf::read_urdf_from_str`. Returns the imported
Document as JSON (which the existing `Document.fromJson` path
already knows how to load).
- @vcad/engine: thin TS wrapper `Engine.importUrdf(buffer)` matching
the existing `importStep` shape, with a runtime guard for kernels
built without urdf support.
- @vcad/core: export `computeNextIds` so callers building a VcadFile
from a non-`.vcad` Document (here: URDF) get correct
`nextNodeId` / `nextPartNum`.
- @vcad/app: handle `.urdf` extension in the drop / file-picker
pipeline. Toast reports link + joint counts on success. Mesh
references inside the URDF can't reach the user's filesystem from
the browser, so they fall back to 1cm placeholder cubes per link
— joint topology and authored mass / inertia still flow through,
so simulation behaves like the real robot to first order.
- Rebuild the checked-in kernel WASM artifact (~13 MB now, was 10 MB
optimised — the new mirror skips wasm-opt for the local sandbox
build; CI re-optimises). Disable wasm-opt in the release profile
metadata so offline rebuilds don't have to download binaryen.
Drag examples/unitree-g1.urdf or examples/unitree-g1-official.urdf onto
the viewport at the Vercel preview from PR #180 to try it out.
https://claude.ai/code/session_01HtymVXbgcAvFXrPkPv33MA
Phase 6: drop-down access to all four Unitree fixtures from the
File → Examples menu (and the command palette / inline onboarding).
No drag-drop required — pick "Unitree G1 (humanoid)" or any sibling,
hit the Simulate ▶ button, watch the robot fall over.
Pieces:
- examples/index.ts: extend the `Example` type with an optional
`urdf: { urdfText, name }` source bundle, mutually exclusive with
the existing `file: ExampleFile`.
- Four new entries that import the URDFs via Vite `?raw` so the text
lands in the bundle as a string. Lazy IR materialisation happens
at click time through `engine.importUrdf` — saves us from baking
several hundred kB of pre-converted Document JSON into the app.
- Header / CommandPalette / InlineOnboarding: detect URDF examples
and dispatch through the `vcad:load-example` event with a `urdf`
detail variant. App.tsx's onLoadExample handler now branches on
detail shape and routes URDFs through the same import path the
drag-drop uses.
The two "official" entries are the Unitree-shipped descriptors: visuals
appear as 1cm placeholder cubes (browser can't reach STL/DAE meshes)
but joint topology and authored inertials are exact, so simulation
behaves like the real robot. The two hand-authored entries ship full
primitive geometry inline.
https://claude.ai/code/session_01HtymVXbgcAvFXrPkPv33MA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds complete support for simulating Unitree robots (G1 humanoid and Go2 quadruped) by introducing URDF example files, enhancing the URDF importer to preserve authored inertial properties, and adding a new
vcad simulateCLI command for running physics simulations.Key Changes
New Robot Models
URDF Importer Enhancements
UrdfReadOptionsstruct to handlepackage://NAME/...URI resolution with configurable package roots, enabling proper mesh loading from ROS-style package layouts<inertial>blocks (mass, inertia tensor, center-of-mass) now flow through the importer intoPartDef::inertialinstead of being discardedread_urdf_with_options()API: Allows callers to specify package roots and URDF directory for mesh resolutionPhysics Simulation
vcad-kernel-physics/src/stl.rs): Loads binary/ASCII STL files with automatic metre→millimetre conversion and non-uniform scale supportPhysicsWorldnow prefers authored inertial properties over mesh-derived estimates, enabling accurate simulation of real robot dynamicsvcad simulateCLI command: New subcommand that:.urdfor.vcadinput files--package-rootfor resolvingpackage://URIsIR and Data Model
InertialPropertiesstruct: New type invcad-ircapturing mass (kg), inertia tensor (kg·m²), and center-of-mass (mm)CsgOp::MeshImport: New CSG operation for mesh references with optional scale, replacing ad-hoc mesh handlingTesting
unitree.rs): End-to-end validation that both robots load, build physics worlds, and step without panickingstl_loader.rs): Verify unit conversion and scale application; testpackage://URI resolutionImplementation Details
package://URIs cannot be located, allowing fallback to placeholder geometry<link>elements must precede<joint>elements in URDF files (required by vcad's quick-xml-serde reader)https://claude.ai/code/session_01HtymVXbgcAvFXrPkPv33MA