Use USD version of CartPole#564
Conversation
Signed-off-by: camevor <camevor@nvidia.com>
📝 WalkthroughWalkthroughRefactors the cart-pole USD scene to CamelCase paths, Xform-wrapped bodies, adds a second pole, renames/updates joints and physics properties, and sets kilogramsPerUnit. Updates examples to load the USD asset via parse_usd instead of the previous URDF parse_urdf. Changes
Sequence Diagram(s)sequenceDiagram
participant Example as Example code
participant Assets as Assets (cartpole.usda)
participant Parser as newton.utils.parse_usd
participant Builder as ArticulationBuilder
participant Scene as Simulation/Stage
Example->>Assets: get_asset("cartpole.usda")
Example->>Parser: parse_usd(asset_path, builder, opts)
Parser->>Builder: emit bodies, joints, physics metadata
Builder->>Scene: instantiate articulation at /cartPole
Scene-->>Example: ready articulation (joints/bodies accessible)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
newton/examples/assets/cartpole.usda (2)
92-113: Nested Cube under pole2 is misnamed "pole1" — rename for consistencyThe nested Gprim under Xform "pole2" is currently named "pole1". While unique by full path, this is confusing and error-prone.
Apply this rename:
- def Cube "pole1" ( + def Cube "pole2" ( prepend apiSchemas = ["PhysicsCollisionAPI"] )
129-138: Verify hinge anchor placement for cart-pole jointlocalPos0 on the cart is set to (0.55, 0.0, 0), which places the hinge well outside the cart’s 0.2 length along X. Typically, the pole should hinge at the top center of the cart, i.e., around (0, 0, +half_cart_height). Consider moving the anchor to the cart’s top face.
Proposed adjustment:
- point3f physics:localPos0 = (0.55, 0.0, 0) + # Hinge at the top center of the cart (cart scale along Z is 0.2 => half-height ~0.1) + point3f physics:localPos0 = (0.0, 0.0, 0.1)Please verify visually/simulation-wise and tweak as needed to avoid initial interpenetration.
newton/examples/example_cartpole.py (3)
19-22: Update comment to reflect USD-based import (not URDF)The example now loads from USD. Adjust the comment to prevent confusion.
-# from a URDF using newton.ModelBuilder(). +# from a USD stage using newton.ModelBuilder().
42-47: Switch to parse_usd is correct; consider applying stage up-axis automaticallyThe call looks good. Optionally pass apply_up_axis_from_stage=True to honor the stage’s upAxis (Z) in environments where the default differs.
- newton.utils.parse_usd( + newton.utils.parse_usd( newton.examples.get_asset("cartpole.usda"), articulation_builder, - enable_self_collisions=False, - collapse_fixed_joints=True, + enable_self_collisions=False, + collapse_fixed_joints=True, + apply_up_axis_from_stage=True, )
63-65: Assumes exactly three joints per env; consider making this resilient to asset changesSetting builder.joint_q[-3:] works with the current asset (prismatic + two revolutes). If the USD changes joint count/order, this will silently misinitialize. If available, prefer targeting joints by name/path via the importer’s mappings or by capturing joint indices at import time.
I can help wire this to importer-returned mappings or generate a small utility to resolve joint indices by path. Want me to draft that?
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
newton/examples/assets/cartpole.usda(2 hunks)newton/examples/example_cartpole.py(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
newton/examples/example_cartpole.py (2)
newton/utils/import_usd.py (1)
parse_usd(34-1269)newton/examples/__init__.py (1)
get_asset(31-32)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run GPU Benchmarks (Pull Request)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (7)
newton/examples/assets/cartpole.usda (7)
7-11: Stage metrics and defaultPrim look correctdefaultPrim set to "cartPole" and kilogramsPerUnit added with metersPerUnit=1, upAxis="Z" — good choices and consistent.
26-31: Articulation root setup is appropriatePhysicsArticulationRootAPI + PhysxArticulationAPI on /cartPole with self-collisions disabled matches the intended articulated rig.
32-45: Rail body/collision split LGTMRigidBodyAPI is on the Xform and CollisionAPI on the nested Cube. This is the right pattern for separating body from shape. Fixed joint to world below completes the static base.
69-91: Pole1 mass properties correctly authored on the rigid bodyPhysicsMassAPI is on the Xform and mass/inertia live there; CollisionAPI is on the nested Cube. Consistent and correct.
114-118: Fixed joint to world is fineReferencing only body1 for PhysicsFixedJoint is valid to attach /cartPole/rail to world.
120-127: Prismatic joint setup LGTMAxis along Y matches the rail scale (0.03, 8, 0.03) and limits [-4, 4] align with the track length.
140-149: Second pole joint looks correctHinge axis, local positions, and rotations are consistent with linking the tips of pole1 and pole2.
Signed-off-by: camevor <camevor@nvidia.com>
Signed-off-by: camevor <camevor@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
newton/examples/assets/cartpole.usda (1)
47-66: Mass/inertia moved to the body prim; color moved to the geom — exactly rightThis addresses prior feedback about placing mass properties on the rigid body prim (Xform with PhysicsMassAPI) and visual color on the mesh prim. Thanks for fixing this.
🧹 Nitpick comments (4)
newton/examples/example_mujoco.py (1)
148-153: Minor: consider clarifying the “root pose” comment to “initial joint state”Given this is a fixed-base articulation, the values immediately after parsing represent the cart prismatic DOF and the two pole angles, not a free-base root pose. Renaming the comment will prevent future confusion.
newton/examples/assets/cartpole.usda (3)
26-45: Articulation root and rail structure are reasonableUsing PhysicsArticulationRootAPI with a fixed joint to world via the rail is valid. The rail being an Xform with a Collision-only child is consistent with a static base.
If you intend the rail to be fully static, you can drop PhysicsRigidBodyAPI from the rail Xform to make that intent explicit and rely on the FixedJoint-to-world. Not critical, but can reduce ambiguity for consumers that infer dynamics from APIs.
55-58: Nit: replace tabs with spaces for consistencyThere are a few lines indented with tabs; the rest uses spaces. While USD is whitespace-agnostic, mixed indentation can trip formatters and reviews.
Apply:
- float3 physics:diagonalInertia = (0.1, 0.1, 0.1) - float physics:mass = 1 - quatf physics:principalAxes = (1, 0, 0, 0) + float3 physics:diagonalInertia = (0.1, 0.1, 0.1) + float physics:mass = 1 + quatf physics:principalAxes = (1, 0, 0, 0) @@ - color3f[] primvars:displayColor = [(0.3, 0.5, 0.7)] + color3f[] primvars:displayColor = [(0.3, 0.5, 0.7)]Also applies to: 64-64
128-137: Cart–pole joint frames: please double-check anchor placementThe pole rotates about X, which is fine, but the cart anchor at (0.55, 0, 0) seems offset along X far beyond the cart’s half-extent (scale X=0.2). If this isn’t by design, consider moving the anchor near the cart’s top center and adjusting localPos1 accordingly.
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
newton/examples/assets/cartpole.usda(2 hunks)newton/examples/example_cartpole.py(2 hunks)newton/examples/example_mujoco.py(1 hunks)newton/examples/example_selection_cartpole.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- newton/examples/example_cartpole.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-12T18:07:17.267Z
Learnt from: nvlukasz
PR: newton-physics/newton#519
File: newton/utils.py:30-33
Timestamp: 2025-08-12T18:07:17.267Z
Learning: In newton/_src/utils/import_usd.py, the PXR/USD modules are lazily loaded using try-except blocks within the functions rather than at module import time, making it safe to import parse_usd at the module level without causing import-time failures for users without USD dependencies.
Applied to files:
newton/examples/example_selection_cartpole.py
📚 Learning: 2025-08-12T18:07:17.267Z
Learnt from: nvlukasz
PR: newton-physics/newton#519
File: newton/utils.py:30-33
Timestamp: 2025-08-12T18:07:17.267Z
Learning: In newton/_src/utils/import_usd.py, the PXR/USD modules are lazily loaded using try-except blocks within the functions (lines 109-111 and 1289-1291) rather than at module import time, making it safe to import parse_usd at the module level without causing import-time failures for users without USD dependencies.
Applied to files:
newton/examples/example_selection_cartpole.py
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run GPU Benchmarks (Pull Request)
🔇 Additional comments (8)
newton/examples/example_mujoco.py (1)
148-153: All CartPole examples now use the USD assetSearches confirmed there are no
parse_urdfcalls loadingcartpole.urdfin any example, and all CartPole examples (example_cartpole.py,example_mujoco.py,example_selection_cartpole.py) correctly invokeparse_usdwith"cartpole.usda".newton/examples/example_selection_cartpole.py (2)
89-89: ArticulationView path update to “/cartPole” is correctMatches defaultPrim and root path in the USD asset; this will select the intended articulation(s).
56-62: All CartPole examples & tests correctly use the USD asset
Verified that:
- example_selection_cartpole.py calls
parse_usd(..., "cartpole.usda", …)with the proper flags and usesArticulationView(self.model, "/cartPole", …).- example_cartpole.py and example_mujoco.py also reference
cartpole.usdain theirparse_usdcalls.- test_examples.py includes both
example_cartpoleandexample_selection_cartpole.No further changes needed here.
newton/examples/assets/cartpole.usda (5)
7-10: Good: units and defaultPrim explicitly setAdding kilogramsPerUnit=1 and renaming defaultPrim to "cartPole" improves clarity and loader consistency.
68-89: Pole1 body/geom and mass properties look correctPhysicsMassAPI on the body and CollisionAPI on the child geom is aligned with USD physics conventions. Center of mass at the body origin matches your joint frames.
91-112: Addition of pole2 and its mass/inertia is consistent with pole1The second pole mirrors pole1 and enables a double-pendulum configuration via the new joint below.
119-126: Verify prismatic axis and limits against intended motion directionAxis is set to "Y" with limits -4..4. The rail geometry also stretches along Y, so this is self-consistent, but it differs from the common X-axis convention for CartPole. Please confirm this is intentional for your coordinate system.
139-148: Pole–pole joint addition looks goodFrames are consistent (top of pole1 to bottom of pole2) and axis matches the first hinge, enabling a double pendulum.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
newton/examples/assets/cartpole.usda (1)
47-66: Mass properties correctly moved to the body Xform; color authored on the meshThis addresses the earlier review about mass/inertia being on the shape. The body now carries PhysicsMassAPI attributes and the Cube carries displayColor — parsers will pick these up as intended.
🧹 Nitpick comments (4)
newton/examples/assets/cartpole.usda (3)
55-58: Nit: mixed tabs/spaces — standardize indentationThe lines authoring physics:diagonalInertia/mass/principalAxes and primvars:displayColor use tabs, while the rest of the file uses spaces. Standardize to spaces to avoid diffs/noise across editors.
Apply this whitespace-only diff:
- float3 physics:diagonalInertia = (0.1, 0.1, 0.1) - float physics:mass = 1 - quatf physics:principalAxes = (1, 0, 0, 0) + float3 physics:diagonalInertia = (0.1, 0.1, 0.1) + float physics:mass = 1 + quatf physics:principalAxes = (1, 0, 0, 0) @@ - color3f[] primvars:displayColor = [(0.3, 0.5, 0.7)] + color3f[] primvars:displayColor = [(0.3, 0.5, 0.7)]Also applies to: 64-64
128-137: Hinge anchor likely misplaced (localPos0 is along X, not Z/top of cart)For a standard cart-pole, the hinge sits on the cart’s top face (Z half-extent), not offset along X. With cart scale z = 0.2, half-height is 0.1. Recommend moving the hinge anchor on the cart to z = +0.1.
Proposed fix:
def PhysicsRevoluteJoint "cartPoleJoint" { rel physics:body0 = </cartPole/cart> rel physics:body1 = </cartPole/pole1> uniform token physics:axis = "X" - point3f physics:localPos0 = (0.55, 0.0, 0) + point3f physics:localPos0 = (0, 0, 0.1) point3f physics:localPos1 = (0, 0, -0.5) quatf physics:localRot0 = (1, 0, 0, 0) quatf physics:localRot1 = (1, 0, 0, 0) }Please verify in the viewer that the pole bottom (localPos1 = (0,0,-0.5)) now meets the cart top without gaps/overlap, and that the swing plane remains about the X axis.
55-58: Consider letting the importer compute inertia from mass and geometryThe diagonalInertia/principalAxes are currently hard-coded and may not match the actual geometry scales and masses (e.g., poles at 0.25 kg with 1 m length). To avoid incorrect dynamics, either compute physically correct box inertia or omit these attributes so the importer computes inertia from shapes.
Option A (simplest): remove explicit inertia/axes so they’re derived:
@@ - float3 physics:diagonalInertia = (0.1, 0.1, 0.1) - float physics:mass = 1 - quatf physics:principalAxes = (1, 0, 0, 0) + float physics:mass = 1 @@ - float3 physics:diagonalInertia = (0.1, 0.1, 0.1) - float physics:mass = 0.25 - quatf physics:principalAxes = (1, 0, 0, 0) + float physics:mass = 0.25 @@ - float3 physics:diagonalInertia = (0.1, 0.1, 0.1) - float physics:mass = 0.25 - quatf physics:principalAxes = (1, 0, 0, 0) + float physics:mass = 0.25Option B: if you want explicit inertia, I can provide the closed-form box inertia values matching your scales and masses.
Also applies to: 76-80, 99-103
newton/examples/example_mujoco.py (1)
148-153: Recommend aligning builder’s up‐axis with the USD stageThe USD file declares upAxis="Z", whereas ModelBuilder’s default is Y-up. To avoid unexpected rotations when loading Z-up stages, enable
apply_up_axis_from_stagein theparse_usdcall:newton.utils.parse_usd( newton.examples.get_asset("cartpole.usda"), articulation_builder, enable_self_collisions=False, collapse_fixed_joints=True, + apply_up_axis_from_stage=True, )(optional — improves robustness when importing Z-up USD assets)
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
newton/examples/assets/cartpole.usda(2 hunks)newton/examples/example_cartpole.py(2 hunks)newton/examples/example_mujoco.py(1 hunks)newton/examples/example_selection_cartpole.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- newton/examples/example_cartpole.py
🧰 Additional context used
🧬 Code Graph Analysis (2)
newton/examples/example_mujoco.py (2)
newton/_src/utils/import_usd.py (1)
parse_usd(34-1274)newton/examples/__init__.py (1)
get_asset(31-32)
newton/examples/example_selection_cartpole.py (2)
newton/_src/utils/import_usd.py (1)
parse_usd(34-1274)newton/examples/__init__.py (1)
get_asset(31-32)
🔇 Additional comments (5)
newton/examples/assets/cartpole.usda (2)
7-11: Units and defaultPrim metadata look correctSwitching defaultPrim to "cartPole" and explicitly authoring kilogramsPerUnit = 1 aligns the stage with importer expectations and avoids unit ambiguity.
32-45: Good separation of body and shape for the railWrapping the visual/collision Cube under an Xform with PhysicsRigidBodyAPI and keeping PhysicsCollisionAPI only on the Cube is the right pattern for rigid bodies in USD physics.
newton/examples/example_mujoco.py (1)
148-153: Switch to USD loader for CartPole — looks goodReplacing parse_urdf with parse_usd and pointing to cartpole.usda matches the asset changes and articulation path updates in this PR.
newton/examples/example_selection_cartpole.py (2)
56-62: USD migration call is consistent and minimalSwitching to parse_usd with the USD asset and keeping collapse_fixed_joints/enable_self_collisions mirrors other examples and the stage settings. With ModelBuilder(up_axis=Z) and stage upAxis=Z, no extra axis handling is needed.
89-89: ArticulationView path updated to “/cartPole” — correctMatches the defaultPrim and articulation root in the USD stage.
Signed-off-by: camevor <camevor@nvidia.com>
Signed-off-by: camevor <camevor@nvidia.com> Co-authored-by: Eric Heiden <eheiden@nvidia.com> Signed-off-by: Miguel Zamora <mazamoramora@nvidia.com>
Signed-off-by: camevor <camevor@nvidia.com> Co-authored-by: Eric Heiden <eheiden@nvidia.com>
Signed-off-by: camevor <camevor@nvidia.com> Co-authored-by: Eric Heiden <eheiden@nvidia.com>
Description
This change uses the USD version of CartPole in the CartPole example.
Resolves #567
Newton Migration Guide
Before your PR is "Ready for review"
newton/tests/test_examples.py)pre-commit run -aSummary by CodeRabbit
New Features
Chores