Skip to content

Conversation

@alanray-tech
Copy link

@alanray-tech alanray-tech commented Dec 3, 2025

Description

This PR adds comprehensive USD (Universal Scene Description) import functionality to Genesis, enabling users to directly import USD stages containing articulated structures and rigid bodies into Genesis simulation scenes.

The implementation includes:

  • USD Parser System: A modular parser architecture with separate parsers for materials, articulations, and rigid bodies
  • Articulation Support: Full parsing of USD articulations with support for multiple joint types (revolute, prismatic, spherical, fixed)
  • Rigid Body Support: Parsing of standalone rigid bodies with collision and visual geometry
  • Material Parsing (Placeholder, need further development): Automatic extraction and conversion of USD rendering materials
  • Scene Integration: New Scene.add_stage() method for easy USD file import
  • New Morph Types: USDArticulation and USDRigidBody morph classes for USD-based entities

Key components:

  • UsdParser: Main parser entrance with import_from_stage() method
  • UsdArticulationParser: Extracts and parses articulation structures from USD stages
  • UsdRigidBodyParser: Extracts and parses rigid body prims from USD stages
  • UsdRenderingMaterialParser: Parses USD materials and shaders
  • UsdParserContext: Context manager for tracking parsed entities and materials
  • UsdParserUtils: Utility functions for transform computation, mesh conversion, and coordinate system transformations

Motivation and Context

USD is a widely-used format in the robotics and simulation community, particularly in tools like NVIDIA Isaac Sim, Omniverse, and other industry-standard platforms. Adding USD import capability allows Genesis users to:

  1. Import assets directly from USD files without manual conversion
  2. Leverage existing USD asset libraries and pipelines
  3. Work seamlessly with USD-based workflows and tools
  4. Import complex articulated structures (robots, mechanisms) with proper joint definitions
  5. Import rigid bodies with correct collision and visual geometry

This feature significantly improves Genesis's interoperability with the broader robotics and simulation ecosystem.

How Has This Been / Can This Be Tested?

The feature can be tested using the provided example script:

import genesis as gs
gs.init(backend=gs.cpu)

scene = gs.Scene(
    viewer_options=gs.options.ViewerOptions(
        camera_pos=(3.5, 0.0, 2.5),
        camera_lookat=(0.0, 0.0, 0.5),
        camera_fov=40,
        enable_interaction=True,
    ),
    rigid_options=gs.options.RigidOptions(
        gravity=(0, 0, -9.8),
        enable_collision=True,
        enable_joint_limit=True,
        max_collision_pairs=10000,
    ),
    show_viewer=True,
)

# Load a USD stage
entities = scene.add_stage("path/to/your/file.usd")

# Build and run simulation
scene.build()
while scene.viewer.is_alive():
    scene.step()

Testing Checklist:

  1. Test with USD files containing both articulations and rigid bodies
  2. Verify joint types are correctly parsed (revolute, prismatic, fixed)
  3. Verify visual and collision meshes are correctly extracted
  4. Verify materials are correctly applied (Placeholder, need further development)
  5. Verify transforms and coordinate systems are correctly converted

Test Files:

  • Example script: examples/usd/import_stage.py
  • Test with USD files that have:
    • ArticulationRootAPI prims
    • RigidBodyAPI prims
    • CollisionAPI prims
    • Visual and collision geometry
    • Various joint types

Screenshots (if appropriate):

20251202-115133.mp4

Checklist:

  • I read the CONTRIBUTING document.

  • I followed the Submitting Code Changes section of CONTRIBUTING document.

  • I tagged the title correctly (including BUG FIX/FEATURE/MISC/BREAKING)

  • I updated the documentation accordingly or no change is needed.

  • I tested my changes and added instructions on how to test it for reviewers.

  • I have added tests to cover my changes.

  • All new and existing tests passed.

@alanray-tech alanray-tech changed the title Usd Articulation/RigidBody Support Feature [Feature] Usd Stage Import Support: Articulation & RigidBody Dec 3, 2025
@YilingQiao
Copy link
Collaborator

can you add a unit test? The assets can be put into the huggingface repo. An example is like this

asset_path = get_hf_dataset(pattern="linear_deformable.urdf")

@github-actions
Copy link

🔴 Benchmark Regression Detected ➡️ Report

@duburcqa
Copy link
Collaborator

You are getting some timeout error in example CI for your newly introduced example.

@duburcqa
Copy link
Collaborator

duburcqa commented Jan 12, 2026

The scene is too complex. You are dying processing more than 511 geometries.

EDIT: Several options:

  • simplifying the USD in example
  • use another, much simpler, USD if and only if 'PYTEST_VERSION' is specified
  • Disable this example on the CI, but it means that it is not basically not maintained and may break

@alanray-tech
Copy link
Author

The scene is too complex. You are dying processing more than 511 geometries.

EDIT: Several options:

  • simplifying the USD in example
  • use another, much simpler, USD if and only if 'PYTEST_VERSION' is specified
  • Disable this example on the CI, but it means that it is not basically not maintained and may break

I think I will remove it from CI, because we have unit tests

@duburcqa
Copy link
Collaborator

duburcqa commented Jan 13, 2026

I think I will remove it from CI, because we have unit tests

There is no relation between both. Example CI is not there to serve as unit test, because they do not check anything in the first place... But to make sure that the example scripts that we provide are running! Completely different objective.

It is very important to run all our example scripts on the CI? otherwise they will break sooner than later.

@alanray-tech
Copy link
Author

alanray-tech commented Jan 13, 2026

I think I will remove it from CI, because we have unit tests

There is no relation between both. Example CI is not there to serve as unit test, because they do not check anything in the first place... But to make sure that the example scripts that we provide are running! Completely different objective.

It is very important to run all our example scripts on the CI? otherwise they will break sooner than later.

I see, then I would like to upload a relative simple asset to Huggingface and use it.

@duburcqa
Copy link
Collaborator

Do not forget to update the official Genesis documentation accordingly (not the documenting the API) but real doc with tutorial and explanation of the basics and what is supported for now.

@alanray-tech
Copy link
Author

Do not forget to update the official Genesis documentation accordingly (not the documenting the API) but real doc with tutorial and explanation of the basics and what is supported for now.

Yep, I'll add it when we are almost done.

@duburcqa
Copy link
Collaborator

I see, then I would like to upload a relative simple asset to Huggingface and use it.

Yes, go ahead. For example scripts it is completely fine.

return transform_by_R(pos - trans, R_inv)


def _polar_torch(A, pure_rotation: bool, side: Literal["right", "left"]):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use _tc_ prefix for torch implementation.

return transform_by_R(pos - trans, R_inv)


def _polar_torch(A, pure_rotation: bool, side: Literal["right", "left"]):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could add typing for A: torch.Tensor

return U, P


def _polar_numpy(A, pure_rotation: bool, side: Literal["right", "left"]):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_np_ prefix + A typing : def _np_polar(A: np.ndarray,

Comment on lines +1323 to +1328
if isinstance(A, torch.Tensor):
return _polar_torch(A, pure_rotation, side)
elif isinstance(A, np.ndarray):
return _polar_numpy(A, pure_rotation, side)
else:
gs.raise_exception(f"the input must be either torch.Tensor or np.ndarray. got: {type(A)=}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you return, no need for elif-else clauses. It will raise an exception in the future when Ruff will be fully enforced.

    if isinstance(A, torch.Tensor):
        return _polar_torch(A, pure_rotation, side)
    if isinstance(A, np.ndarray):
        return _polar_numpy(A, pure_rotation, side)
    gs.raise_exception(f"the input must be either torch.Tensor or np.ndarray. got: {type(A)=}")

@duburcqa
Copy link
Collaborator

I don't have any more comments to add. Just need to resolve what is still open and we are good to go!

Comment on lines +31 to +39
@pytest.fixture
def tol():
"""
Custom tolerance for USD tests.
USD Joint Limits use float32 (C++ float) precision, so we need a tolerance
that's appropriate for float32 rather than the default float64 tolerance.
"""
return 1e-6
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the right approach. You should rather use @pytest.mark.parametrize("precision", ["32"]) "decorator". This will map tol to the appropriate tolerance. It is np.finfo(np.float32).eps, i.e. ~1.19e-7.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it does not pass, hardcode 1e-6 where needed with a comment stating that large tolerance is needed for unknown reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet