Skip to content

Implements passive fixed tendons with mjwarp#5522

Open
nv-rgresia wants to merge 20 commits into
isaac-sim:developfrom
nv-rgresia:passive-tendons
Open

Implements passive fixed tendons with mjwarp#5522
nv-rgresia wants to merge 20 commits into
isaac-sim:developfrom
nv-rgresia:passive-tendons

Conversation

@nv-rgresia
Copy link
Copy Markdown
Contributor

@nv-rgresia nv-rgresia commented May 6, 2026

Description

I have implemented the ability to use and modify passive tendons properties. Despite the shortcomings, this is on feature parity with the existing shadow hand physx environment. While mjc supports spatial tendons, this PR assumes that only fixed tendons are present in the scene, if any. Only 1:1 properties are currently supported (stiffness and damping; there is no equivalent for limit stiffness and rest length/position limits don't have the same data type as in physx (vec2f instead of float), so they can't be ported without restructuring the buffers/apis.

Type of change

  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings

@github-actions github-actions Bot added documentation Improvements or additions to documentation asset New asset feature or request isaac-sim Related to Isaac Sim team isaac-mimic Related to Isaac Mimic team infrastructure labels May 6, 2026
@nv-rgresia nv-rgresia changed the base branch from main to develop May 6, 2026 20:01
@kellyguo11 kellyguo11 moved this to In progress in Isaac Lab May 7, 2026
# newton requires implicitactuators be specified in usd and there's a bug with physx tendons
usd_path=f"{ISAAC_NUCLEUS_DIR}/Robots/ShadowRobot/ShadowHand/shadow_hand_instanceable_newton.usd",
#usd_path=f"{ISAAC_NUCLEUS_DIR}/Robots/ShadowRobot/ShadowHand/shadow_hand_instanceable_newton.usd",
usd_path=f"/home/rgresia/Repositories/mujoco_menagerie/shadow_hand/right_hand.usd/right_shadow_hand.usda",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this does not look right

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

num_envs=8192, env_spacing=0.75, replicate_physics=True, clone_in_fabric=False
)
default: InteractiveSceneCfg = physx
default: InteractiveSceneCfg = newton_mjwarp
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

non-breaking, but might prefer to not change default?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

reverted

if "PhysxTendonAxisRootAPI" not in schema_name:
continue
# set into PhysX API by attribute prefix schema_name: (e.g. PhysxTendonAxisRootAPI:default:stiffness)
if prim_type != "MjcTendon":
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think MjcTendon is newton specific so it may need to be added to new newton schema. #5049 has example how physx setup is done. @ooctipus could you also help suggest here?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think there are other mujoco schema also got leaked into core schema. and also seems like we have not prepared a home for mujoco schema under isaaclab_newton properly. This check is no ideal but probably makes sense for this PR, and would require follow up work to split it.

if self.num_fixed_tendons > 0 or self.num_spatial_tendons > 0:
raise NotImplementedError("Fixed and spatial tendons are not supported yet.")
if self._root_view.tendon_count > 0:
tendon_types = wp.to_torch(
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems wasteful to convert to torch just to check if there is a non-zero element. Any way we can do this in a warp way

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm open to suggestions, but the only alternative I can think is writing a kernel since warp array don't have a lot of the same utility

"""
raise NotImplementedError()
wp.launch(
shared_kernels.write_2d_data_to_buffer_with_indices,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

These two kernels can be combined into one. This might be out of the scope of this PR, if so can we leave a TODO so someone know to refactor it later

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

env_ids: Sequence[int] | torch.Tensor | wp.array | None = None,
self,
*,
limit: float | torch.Tensor | wp.array,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

What's going on with these argument types? It seems below that they're meant to be warp arrays since we're going to use a warp kernels, maybe we should make the typing strict again. Also if its proxy array we can just keep the type as ProxyArray

(num_instances, num_fixed_tendons) if full_data.
fixed_tendon_ids: The tendon indices to set the damping for. Defaults to None (all fixed tendons).
env_ids: Environment indices. If None, then all indices are used.
full_data: Whether to expect full data. Defaults to False.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This argument does not seem to change the logic at all, just used to check the shape. It seems either way we're going to use env_ids, can we just drop the full data and full data assert below?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it has been removed

damping: float | torch.Tensor | wp.array,
fixed_tendon_mask: wp.array | None = None,
env_mask: wp.array | None = None,
self,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

whats happening with the indentation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

@ooctipus
Copy link
Copy Markdown
Collaborator

make sure run the linter, everything else looks ok to me, thanks!

self.assert_shape_and_dtype(
stiffness, (env_ids.shape[0], fixed_tendon_ids.shape[0]), wp.float32, "stiffness"
)
# Warp kernels can ingest torch tensors directly, so we don't need to convert to warp arrays here.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can you check its probably not torch tensor but ProxyArray, we don't wnat to directly give torch into warp kernel, as far as I am aware.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

the randomized damping/stiffness buffers are being generated in torch and passed to the kernel. we would have to port _randomize_prop_by_op to a warp kernel to avoid the conversion all together. The data it copies from is warp array too.

@github-actions github-actions Bot added the isaac-lab Related to Isaac Lab team label May 14, 2026
Copy link
Copy Markdown

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Choose a reason for hiding this comment

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

🤖 Isaac Lab Review Bot

Summary

This PR implements passive fixed tendon support for Newton/MuJoCo-based articulations (mjwarp), enabling tendon stiffness and damping randomization. The implementation follows the established Isaac Lab patterns for property setters with index/mask variants and correctly handles the subset of tendon properties that map between PhysX and MuJoCo. The core implementation appears sound, though there are a few edge cases and documentation items to address.

Design Assessment

Architecture

  • Clean separation between PhysX and Newton code paths using self.newton flag in event handlers
  • Proper use of Warp kernels (shared_kernels.write_2d_data_to_buffer_with_indices) matching existing patterns
  • Correct staging buffer pattern: internal buffers → write_fixed_tendon_properties_to_sim_index() → simulation
  • Appropriate NotImplementedError exceptions for unsupported properties (limit_stiffness, rest_length, offset, position_limits)

Data Flow

  • Simulation bindings correctly created from mujoco.tendon_stiffness and mujoco.tendon_damping attributes
  • ProxyArray wrappers properly initialized in _pin_proxy_arrays()
  • Staging buffers cloned from sim bindings allowing batch writes

API Surface

  • Index and mask methods implemented consistently
  • full_data parameter added appropriately for handling both partial and full data paths

Findings

🔴 Critical

None identified.

🟡 Medium

  1. Missing full_data parameter forwarding (articulation.py:2699, 2795)

    • set_fixed_tendon_stiffness_index() and set_fixed_tendon_damping_index() accept full_data parameter
    • The mask methods call these with full_data=True, but the index methods signature shows full_data is not used in the implementation
    • Consider actually implementing the full_data branching logic or document that it is currently ignored
  2. write_fixed_tendon_properties_to_sim_index missing parameter usage (articulation.py:3049-3077)

    • Method signature includes fixed_tendon_ids parameter but implementation always uses self._ALL_FIXED_TENDON_INDICES
    • This ignores partial tendon updates, writing all tendons regardless of the parameter
    dim=(env_ids.shape[0], self._ALL_FIXED_TENDON_INDICES.shape[0]),  # Always uses ALL
  3. Assumption comment needs verification (articulation_data.py:1410)

    • Comment says "assumes all tendons are fixed and only one arti in scene"
    • This assumption may not hold in complex scenes - should either validate or relax the constraint

🟢 Low

  1. Typo in schema check comment (schemas.py:876)

    • "exiss" → "exists"
  2. Shadow Hand config hardcoded position change (shadow_hand_env_cfg.py:161)

    • Position changed from (0.0, 0.0, 0.5) to (-0.33, -0.36, 0.3) - is this intentional or debug artifact?

Test Coverage

⚠️ Test infrastructure updated but functional tests still missing

The mock articulation view now supports tendons (num_tendons parameter, tendon_count property), which enables unit testing. However, actual test cases are still needed:

  • No unit tests for set_fixed_tendon_stiffness_index/mask
  • No unit tests for set_fixed_tendon_damping_index/mask
  • No unit tests for write_fixed_tendon_properties_to_sim_index
  • No integration tests for the Newton tendon randomization path

Recommendation: Add tests covering:

  1. Tendon property getters return correct shapes and initial values
  2. Stiffness/damping setters modify buffers correctly
  3. write_fixed_tendon_properties_to_sim_index propagates to simulation
  4. Randomization event with Newton backend
  5. Edge case: articulation with zero tendons

CI Status

  • ⚠️ Check changelog fragments: Failed - changelog entry required
  • 🔄 Most CI checks still pending (recently pushed commits)
  • ✅ Pre-commit, license check, base builds passing

Verdict

Minor fixes needed 🟡

The implementation is architecturally sound and follows established patterns. Key items before merge:

  1. Add changelog fragment (blocking)
  2. Address the write_fixed_tendon_properties_to_sim_index not using fixed_tendon_ids parameter
  3. Consider adding basic test coverage for the new tendon APIs
  4. Clean up commented code and typos

Update (2744b3e): ✅ Issue #5 (commented-out code) has been addressed - the commented num_fixed_tendons check has been removed. Other findings remain open.


Update (e054aa2): ✅ Changelog fragments have been added (blocking CI issue resolved). The incremental diff also includes unrelated Jacobian/mass-matrix task-space controller support in articulation_data.py — these appear to be additional features merged into this PR branch. Original tendon-related findings (items #1–4, #6–7) remain open; item #5 (commented code) was previously addressed.


Update (c7dd84d): Release housekeeping only — version bumps (5.2.1, 0.9.1, 0.7.1) and changelog consolidation. No tendon-related code changes. Previous findings (items #1–4, #6–7) remain open.


Update (99b1a61): Merge commit bringing in unrelated changes from develop branch:

  • AppLauncher CUDA fix: Deferred torch.cuda.set_device() call to after SimulationApp starts, fixing startup crashes when OpenBLAS at-fork handlers were unsafe
  • LEAPP export refactor: Major restructure of export.py for better modularity (functions separated: create_arg_parser(), parse_export_args(), export_rsl_rl_agent(), main_cli())
  • Test improvements: Export tests now batch multiple tasks per Kit process, reducing startup overhead; removed unnecessary AppLauncher from test_noise.py and test_wrench_composer.py
  • CI/build fixes: Import paths corrected to use isaaclab.utils.configclass directly; lazy loading updates to isaaclab.utils
  • Various changelog skip files for unrelated packages

No changes to the core tendon implementation. Previous findings (items #1–4, #5–6) remain open.


Update (614dbb6): Merge commit bringing in develop branch changes. Key updates include:

  • Unified RL train/play entrypoints (#5623): Added ./isaaclab.sh train and ./isaaclab.sh play commands dispatching to library-specific implementations (rsl_rl, rl_games, skrl, sb3, rlinf). Deprecated old per-library scripts with warnings.
  • URDF/MJCF importer refactor (#5394): Simplified converters to delegate to Isaac Sim's importer pipeline; removed duplicate IsaacLab implementations of _apply_fix_base, _apply_link_density, _apply_joint_drives, etc.
  • Newton replication label fix (#5433): Fixed tendon labels (e.g. mujoco:tendon_label) keeping source proto path after replication; now properly rewrites per-env paths
  • Teleop replay agent (#5507): Added non-interactive entry point for CI replay of teleop sessions
  • skrl drone-ARL config fix (#5613): Changed input: STATES to input: OBSERVATIONS fixing AttributeError

No changes to the core passive tendon implementation. Previous findings (items #1–4, #5–6) remain open.


Update (5b2f873): Continuation of merge from develop. Incremental diff shows only the merge-in of unrelated PRs (same items as 614dbb6 update above). The tendon randomization event fix in source/isaaclab/isaaclab/envs/mdp/events.py is notable — it removes the self.newton instance variable set during __init__ and instead queries env.sim.physics_manager.__name__.lower() at call-time, making the backend check more robust for multi-backend scenarios.

No new findings. Previous findings (items #1–4, #5–6) remain open.


Update (4b50d5f): ✅ Test infrastructure improvement — the mock articulation view (MockArticulationView) now supports tendon mocking:

  • Added num_tendons: int = 0 constructor parameter
  • Added tendon_count property returning the configured value

This addresses the test infrastructure gap I noted earlier. However, actual test cases exercising the tendon APIs have not been added yet. The mock is ready; tests using it are still missing. Previous code findings (items #1–4, #5) remain open.


Update (997200d): Merge commit bringing in unrelated changes from develop branch. Key updates include:

  • Camera warp migration: Camera data outputs now use ProxyArray wrappers backed by Warp arrays; renderers (NewtonWarpRenderer, OVRTXRenderer, IsaacRtxRenderer) updated to accept ProxyArray in set_outputs and update_camera
  • Newton v1.2.0 bump: All newton[sim] pins updated from v1.2.0rc2 to stable v1.2.0
  • Preset CLI improvements: Added typed preset selectors (physics=NAME, renderer=NAME) and bucketed help-time variant enumeration
  • OVRTX renderer fixes: Fixed multi-GPU device mismatch; fixed cloned environments disappearing; renamed use_cloning to use_ovrtx_cloning
  • Cubric ABI verification: Added runtime version check for omni::cubric::IAdapter interface
  • Frame stacking: Added CartpoleCameraPresetsEnv with CircularBuffer-backed frame stacking
  • Test improvements: Added determinism training tests, preset CLI tests, stacked image tests

No changes to the core passive tendon implementation. Previous findings (items #1–4, #5) remain open.


Update (ad103b4): Release housekeeping commit — version bumps (isaaclab 5.3.0, isaaclab_newton 0.10.0, isaaclab_ov 0.2.0, isaaclab_physx 0.8.0, isaaclab_ovphysx 0.2.0, isaaclab_tasks 1.7.0, isaaclab_teleop 0.4.0) and changelog consolidation from fragment files into main CHANGELOG.rst.

Key changes in this commit:

  • OVPhysX Articulation implementation: Major addition of Articulation and ArticulationData classes for the OVPhysX backend, with full API surface (index/mask writers/setters, actuator pipeline, tendon support, wrench composers). This is new backend work, not changes to the existing Newton/PhysX tendon code.
  • OVPhysX tendon tensor types: Moved tendon tensor types out of _CPU_ONLY_TYPES since OVPhysX wheel exposes tendons on the simulation device (GPU-resident).
  • OVPhysX step update: Added update_articulations_kinematic() call in physics step.
  • Allegro hand task preset: Added ovphysx preset to ObjectCfg and PhysicsCfg so Isaac-Repose-Cube-Allegro-Direct-v0 can run against OVPhysX backend.
  • Test infrastructure: Added test_articulation_helpers.py for OVPhysX-specific scaffolding tests (USD tendon-scope resolution, mock binding-set shape contracts).

No changes to the original passive fixed tendon implementation for Newton/MuJoCo. Previous findings (items #1–4, #5) remain open.


Update (40135b6): Merge commit bringing in code-quality improvements from develop:

  • Import path migration: 200+ files updated from from isaaclab.utils import configclass to from isaaclab.utils.configclass import configclass — this fixes lazy-import issues and improves module loading performance
  • CI workflow updates: New test-rendering-correctness and test-rendering-correctness-kitless jobs added to build.yaml; ovrtx pip package removed from the general isaaclab_tasks test shards
  • Python version pin: pyproject.toml now requires >=3.12,<3.13 (was >=3.12)
  • OVRTX renderer optimization: Avoided disk I/O by exporting USD to string in memory (export_stage_to_string, open_usd_from_string) instead of always writing intermediate files; temp_usd_dir now defaults to None
  • Documentation fixes: Updated configclass import paths in RST files (hydra.rst, cloning.rst, record_video.rst, contributing.rst, etc.)
  • Changelog skip files: Added for lazy-import fix across isaaclab, isaaclab_contrib, isaaclab_experimental, isaaclab_mimic, isaaclab_newton, isaaclab_ov, isaaclab_ovphysx, isaaclab_physx, isaaclab_rl, isaaclab_tasks

No changes to the core passive tendon implementation. Previous findings (items #1–4, #5) remain open.

logger.info(f"Simulation parameters for joints in {self.cfg.prim_path}:\n" + joint_table.get_string())

# read out all fixed tendon parameters from simulation
if self.num_fixed_tendons > 0:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

removal instead of commenting?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done


manager_name = env.sim.physics_manager.__name__.lower()
if "newton" in manager_name:
self.newton = True
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In other places in this file _backend is used, let's use the same convention

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

self.asset.set_fixed_tendon_limit_stiffness(
limit_stiffness[env_ids[:, None], tendon_ids], tendon_ids, env_ids
)
if not self.newton:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this would become _backend == "physx" if we use the above comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

@kellyguo11
Copy link
Copy Markdown
Contributor

@nv-rgresia many of the tasks tests are failing with this error 2026-05-15T21:58:04.5870969Z 2026-05-15T21:58:04Z [104,639ms] [Warning] [omni.physx.plugin] PhysX warning: PxShape::getMaterialFromInternalFaceIndex received 0xFFFFffff as input - returning NULL., FILE /builds/omniverse/physics/physx/source/physx/src/NpShape.cpp, LINE 598

Could you check if it's related to the change?

@nv-rgresia
Copy link
Copy Markdown
Contributor Author

@kellyguo11 i'm looking into it, I found some errors related to the mock arti view and the lack of usd asset, but didn't see what your referring to. Considering I didn't touch physx-related code or the asset, i'm not sure how my changes are related.

@kellyguo11
Copy link
Copy Markdown
Contributor

https://github.com/isaac-sim/IsaacLab/actions/runs/25945216312/job/76271830895?pr=5522 - this job for example seems to be failing on a lot of physx related errors

# newton requires implicitactuators be specified in usd and there's a bug with physx tendons
usd_path=f"{ISAAC_NUCLEUS_DIR}/Robots/ShadowRobot/ShadowHand/shadow_hand_instanceable_newton.usd",
# newton/mujoco have separate usd schema
usd_path=f"{ISAAC_NUCLEUS_DIR}/Robots/ShadowRobot/ShadowHand/shadow_hand_newton.usd/right_shadow_hand.usda",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this seems to be the source of all the remaining test failure. which file is the correct USD asset? and has it been uploaded to S3?

@kellyguo11 kellyguo11 moved this from In progress to In review in Isaac Lab May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

asset New asset feature or request documentation Improvements or additions to documentation infrastructure isaac-lab Related to Isaac Lab team isaac-mimic Related to Isaac Mimic team isaac-sim Related to Isaac Sim team

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

5 participants