Skip to content

Proxy-coupled MJWarp (Articulation) + VBD (Particles) Solver#5751

Open
mmichelis wants to merge 19 commits into
isaac-sim:developfrom
mmichelis:feat/proxycoupledsolver
Open

Proxy-coupled MJWarp (Articulation) + VBD (Particles) Solver#5751
mmichelis wants to merge 19 commits into
isaac-sim:developfrom
mmichelis:feat/proxycoupledsolver

Conversation

@mmichelis
Copy link
Copy Markdown
Collaborator

Description

Summary

Adds a new NewtonProxyCoupledMJWarpVBDManager that wraps newton.solvers.SolverCoupledProxy, splitting simulation between MuJoCo Warp (rigids/articulations) and VBD (particles/deformables) with selected MJWarp bodies exposed as proxies in the VBD view. Migrates the Franka soft lift environments to use this manager by default.

Builds on top of Newton Coupling in gdaviet/coupled-solver-framework.

Changes

  • New manager: NewtonProxyCoupledMJWarpVBDManager + matching ProxyCoupledMJWarpVBDSolverCfg, partitioning bodies/joints/shapes between the two sub-solvers and collecting proxy body IDs for the coupling.
  • Flexible body selectors: mjwarp_bodies, vbd_bodies, and proxy_bodies accept both SceneEntityCfg entries and raw USD prim-path regex strings (e.g. "/World/envs/env_.*/MyCube"), making it possible to claim assets that aren't registered as named scene entities.
  • Soft envs migrated: lift_franka_soft (rigid + cloth variants) now default to CoupledNewtonCfg with proxy coupling; cleanup pass on the env configs, observations, and rewards (deformable → object rename).
  • Tests: new test_proxy_coupled_mjwarp_vbd.py covering body partitioning, proxy selection, and the prim-path-string selector path.
  • Docs: updated VBD solver page and contrib reference with proxy-coupling usage; changelog fragment added.

Type of change

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

Test plan

  • ./isaaclab.sh -p -m pytest source/isaaclab_contrib/test/deformable/test_proxy_coupled_mjwarp_vbd.py
  • Run lift_franka_soft state-machine demo and confirm rigid/soft coupling behaves as expected.

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
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the changelog and the corresponding version in the extension's config/extension.toml file
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

@github-actions github-actions Bot added documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team labels May 22, 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 — Automated PR Review

Summary

This PR adds NewtonProxyCoupledMJWarpVBDManager wrapping Newton's SolverCoupledProxy, enabling partitioned simulation between MuJoCo Warp (rigids/articulations) and VBD (particles/deformables) with selected MJWarp bodies exposed as proxies in the VBD view via lagged impulses. The implementation includes flexible body selectors supporting both SceneEntityCfg entries and raw USD prim-path regex strings, comprehensive tests, and documentation updates.


🏗️ Architecture & Design (Isaac Lab Expert)

Strengths:

  • Clean separation of concerns: The NewtonProxyCoupledMJWarpVBDManager extends NewtonVBDManager appropriately, reusing builder hooks and deformable infrastructure
  • Well-designed selector system: Supporting both SceneEntityCfg and raw prim-path regex strings provides flexibility for assets that may not be registered as named scene entities
  • Good use of the existing _filter_solver_kwargs helper (centralized in NewtonManager) reduces code duplication across all solver managers
  • The CoupledNewtonCfg with scene_cfg field elegantly solves the resolver-at-build-time problem
  • Proxy configuration parameters (proxy_mode, proxy_iterations, proxy_collide_interval, proxy_mass_scale) are well-documented and expose the underlying Newton solver capabilities appropriately

Suggestions:

  1. Consider documenting the performance trade-offs — The docs mention proxy coupling "typically scales better" but a brief note about when to prefer alternating coupling vs proxy coupling would help users choose

  2. Line 46-48 in proxy_coupled_mjwarp_vbd_manager.py — The scene_cfg lookup:

    outer_cfg = PhysicsManager._cfg
    scene_cfg = outer_cfg.scene_cfg if isinstance(outer_cfg, CoupledNewtonCfg) else None

    This relies on accessing the physics manager's internal _cfg. Consider adding a comment explaining this coupling, as it's a non-obvious dependency


🔍 Silent Failure Analysis

Potential Issues Found:

  1. Empty partition warning could be clearer (Line 57-62 in proxy_coupled_mjwarp_vbd_manager.py):

    if solver_cfg.proxy_bodies and not proxy_body_ids:
        logger.warning(...)

    ✅ Good: Warning is logged when proxy_bodies are specified but none match
    ⚠️ Consideration: When proxy_bodies=[] (empty list), there's no warning. This is intentional but might surprise users who expect some default proxies

  2. Body partition validation (Lines 189-199 in proxy_coupled_mjwarp_vbd_manager.py):

    if overlap := sorted(mjc_owned & vbd_owned):
        raise ValueError(...)
    unclaimed = [b for b in range(int(model.body_count)) if b not in mjc_owned and b not in vbd_owned]
    if unclaimed:
        raise ValueError(...)

    ✅ Excellent: Both overlapping and unclaimed bodies raise clear errors with body labels for debugging

  3. SceneEntityCfg proxy_bodies validation (Lines 257-260):

    if isinstance(spec, SceneEntityCfg) and spec.body_names is None:
        raise ValueError(...)

    ✅ Good: Requires explicit body_names for SceneEntityCfg entries in proxy_bodies

  4. Regex pattern matching edge case — Line 141 uses asset_re.match(lbl) which matches from string start. This is correct but note that patterns in body_names use fullmatch on short names. The different matching semantics are intentional but could be documented

  5. vbd_bodies receiving particles automatically (Lines 70-71):

    vbd_particles = list(range(model.particle_count))

    All particles go to VBD entry — this is the correct design but users might not realize particles cannot be partitioned. A docstring note would help


🧪 Test Coverage Analysis

What's Tested (362 lines in test_proxy_coupled_mjwarp_vbd.py):

  • _resolve_entity_to_body_ids: no body_names returns all, body_names filtering, missing asset error, unmatched patterns error
  • _partition_model_by_entities: correct split of bodies/joints/shapes, overlap detection, unclaimed detection
  • _select_proxy_bodies: COLLIDE_SHAPES filtering, body_names requirement, empty input, deduplication
  • ✅ Raw prim-path string selectors: prefix matching, single body, no matches error, mixed with SceneEntityCfg

What Could Be Added:

  1. Static shape handling — Test that shapes with body == -1 always go to VBD (currently covered implicitly by extra_static_shape=True test but could be more explicit)

  2. Joint inheritance — Test that joints correctly inherit from their child body's partition (partially covered but edge cases like joints spanning partitions aren't tested)

  3. Integration smoke test — The file notes that end-to-end tests rely on test_environments.py. Consider adding a minimal integration test that verifies _build_solver completes without error with valid inputs

  4. Multi-environment body labels — Test that env_0, env_1, etc. patterns work correctly with multiple environment clones


📝 Documentation Review

Strengths:

  • Comprehensive RST documentation explaining proxy-coupling concept and when to use it
  • Good parameter table in using-vbd-solver.rst
  • Changelog fragment added
  • Clear distinction between alternating and proxy coupling approaches

Minor Suggestions:

  • The docs reference newton_mjwarp_vbd_proxy preset but the default is now proxy-coupled. Users might be confused about which is which

🔧 Minor Code Quality Observations

  1. Consistent rename from deformable to object — The rename in rewards/observations/events is thorough and improves generality. Good migration approach

  2. Viewer config migration — Moving from self.viewer.* to visualizer_cfgs list is a clean pattern

  3. State machine demo simplification — The generic parse_env_cfg approach in lift_franka_soft.py removes task-specific branching — cleaner


✅ Overall Assessment

This is a well-designed, thoroughly tested PR that adds significant new functionality for proxy-coupled simulation. The architecture follows Isaac Lab patterns, error handling is robust with informative messages, and the test coverage is comprehensive for the static partitioning logic.

Recommendations:

  1. Address the CI "Check changelog fragments" failure (appears to be running)
  2. Consider the minor documentation clarifications noted above
  3. The code is ready for human review focusing on the Newton solver integration specifics

This review was generated by the Isaac Lab Review Bot using ensemble multi-model analysis.


Update (e05cf4f):

Silent failure concern addressed — Empty proxy_bodies matching now raises ValueError instead of logging a warning. This directly addresses my earlier observation about potential silent failure.

Additional improvements in this commit:

  • Added explicit ValueError for unknown coupling_mode values in both coupled managers (removes potential silent fall-through)
  • Fixed docstring references to use correct Newton API path (newton.solvers.experimental.coupled.SolverCoupledProxy)
  • Removed unused logging import

These changes improve robustness and code quality. 👍


Update (a372856):

Changelog fragments added — This commit adds the missing changelog entries that were flagged by CI:

  • isaaclab_contrib: Documents the new ProxyCoupledMJWarpVBDSolverCfg and manager
  • isaaclab_newton: Documents the _filter_solver_kwargs helper extraction
  • isaaclab_tasks: Documents the breaking rename from deformableobject in lift_franka_soft

CI checks should now pass. LGTM 🚀


Update (ca5ef5d):

No new issues — This commit focuses on documentation updates and unrelated improvements. Changes analyzed:

Documentation:

  • Moved visualization docs from /source/features/ to /source/overview/core-concepts/ (path reorganization)
  • Fixed relative image paths in visualization.rst
  • Added newton_kamino solver support to preset tables
  • Reformatted environment preset tables for clarity with explicit physics=, renderer=, presets= labels
  • Added new RLinf (GR00T VLA) integration documentation

Bug fixes (unrelated to proxy coupling):

  • Fixed OVRTX log file path to use cross-platform tempfile.gettempdir() instead of hardcoded /tmp
  • Fixed flaky noise tests using torch.ones for scale operations to avoid 0/0=NaN

Testing:

  • Re-enabled rendering correctness tests in normal CI
  • Removed redundant render correctness tests from shadow hand vision tests

All proxy-coupled solver changes from previous commits remain intact. LGTM 🚀

@mmichelis mmichelis marked this pull request as ready for review May 22, 2026 15:55
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 22, 2026

Greptile Summary

This PR introduces NewtonProxyCoupledMJWarpVBDManager, a new Newton physics manager that splits simulation between MuJoCo Warp (rigid bodies/articulations) and VBD (particles/deformables) using lagged-impulse proxy coupling via SolverCoupledProxy. It also migrates the Franka soft-lift environments to use this manager by default and adds flexible body selectors that accept both SceneEntityCfg and raw prim-path regex strings.

  • New manager & config: NewtonProxyCoupledMJWarpVBDManager + ProxyCoupledMJWarpVBDSolverCfg partition bodies, joints, and shapes between the two sub-solvers, with selected MJWarp bodies exposed as proxies in the VBD view; CoupledNewtonCfg extends NewtonCfg to carry a scene_cfg for selector resolution.
  • Env migration: FrankaSoftEnvCfg (volume deformables) defaults to the proxy-coupled solver with string prim-path selectors; FrankaClothEnvCfg retains the existing coupled MJWarp+VBD manager unchanged.
  • Tests & docs: Unit tests cover partitioning, proxy selection, overlap/unclaimed-body errors, and mixed selector types; documentation and changelog updated accordingly.

Confidence Score: 3/5

The proxy-body cross-validation gap means a misconfigured proxy_bodies list silently passes incorrect body IDs to SolverCoupledProxy, producing wrong simulation physics with no error.

The new manager introduces a missing cross-check: bodies resolved by _select_proxy_bodies are never validated to be a subset of mjc_bodies. If a user configures proxy_bodies to select VBD-partition bodies, the coupled solver receives body IDs from the wrong entry and generates physically incorrect proxy forces with no diagnostic. Additionally, vbd_bodies=[] in the shipped franka_soft_env_cfg.py relies on an undocumented assumption about which assets produce Newton rigid-body entries, so a scene change could cause a runtime crash.

proxy_coupled_mjwarp_vbd_manager.py (proxy body cross-validation) and franka_soft_env_cfg.py (implicit vbd_bodies assumption) deserve a second look before merge.

Important Files Changed

Filename Overview
source/isaaclab_contrib/isaaclab_contrib/deformable/proxy_coupled_mjwarp_vbd_manager.py New manager wrapping SolverCoupledProxy with MJWarp+VBD split; body/joint/shape partitioning logic is clean but proxy_body_ids are not validated against the MJWarp partition, and SceneEntityCfg zero-match is silently ignored.
source/isaaclab_contrib/isaaclab_contrib/deformable/newton_manager_cfg.py Adds ProxyCoupledMJWarpVBDSolverCfg and CoupledNewtonCfg; well-structured configclass definitions with clear docstrings and sensible defaults.
source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_soft_env_cfg.py Migrates default physics to proxy-coupled solver; vbd_bodies left empty with an undocumented assumption that no static rigid bodies exist beyond the robot in the Newton model.
source/isaaclab_contrib/test/deformable/test_proxy_coupled_mjwarp_vbd.py Comprehensive unit tests for partitioning and proxy-selection logic using fake models; covers overlap, unclaimed, empty, dedup, and mixed string/SceneEntityCfg cases.
source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/observations.py Adds ObjectSampledPointsInRobotRootFrame for stable per-episode point sampling; object_com_in_robot_root_frame is still exported but no longer used by any env config.
source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/mdp/rewards.py Renamed asset references from deformable to object; reward/termination functions are correct and well-documented.
source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/lift_franka_soft/franka_cloth_env_cfg.py Cloth env cleanup; uses the existing CoupledMJWarpVBD solver (not proxy), so unaffected by the new partitioning logic.
source/isaaclab_contrib/isaaclab_contrib/deformable/vbd_manager.py Minor diff; no logic changes identified.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[_build_solver called] --> B[_partition_model_by_entities]
    B --> C{any overlap?}
    C -- yes --> ERR1[ValueError: overlap]
    C -- no --> D{any unclaimed?}
    D -- yes --> ERR2[ValueError: unclaimed]
    D -- no --> E[mjc_bodies / vbd_bodies / joints / shapes split]
    E --> F[_select_proxy_bodies]
    F --> G{proxy_bodies empty?}
    G -- yes --> H[proxy_ids = empty list]
    G -- no --> I[resolve each spec to body IDs]
    I --> J[filter: keep only COLLIDE_SHAPES bodies]
    J --> K{resolved to zero?}
    K -- yes --> ERR3[ValueError: zero collidable proxies]
    K -- no --> L[proxy_ids = filtered list]
    H --> M[Build SolverCoupledProxy entries mjc + vbd]
    L --> M
    M --> N[Build SolverCoupledProxy.Proxy source=mjc dest=vbd]
    N --> O[NewtonManager._solver = SolverCoupledProxy]
    O --> P[_use_single_state = False, _needs_collision_pipeline = False]
Loading

Reviews (1): Last reviewed commit: "Docs: Add changelog fragments" | Re-trigger Greptile

Comment on lines +56 to +62
proxy_body_ids = cls._select_proxy_bodies(model, solver_cfg.proxy_bodies, scene_cfg)
if solver_cfg.proxy_bodies and not proxy_body_ids:
raise ValueError(
f"ProxyCoupledMJWarpVBDSolverCfg.proxy_bodies={solver_cfg.proxy_bodies!r} resolved to "
"zero bodies after filtering for `ShapeFlags.COLLIDE_SHAPES`. Rigid bodies would not be "
"visible to VBD; check that the selected bodies own at least one collidable shape."
)
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.

P1 Proxy bodies not validated against MJWarp partition

_select_proxy_bodies returns body IDs without checking whether they belong to mjc_bodies. The docstring specifies proxies must be MuJoCo bodies exposed to VBD, but nothing prevents a user from listing bodies that are actually in the VBD partition. When those IDs are passed to SolverCoupledProxy as the proxy source in the "mjc" entry, the coupled solver operates on body IDs that are owned by the "vbd" entry — producing physically incorrect proxy forces with no diagnostic. A guard like invalid = [b for b in proxy_body_ids if b not in set(mjc_bodies)] before constructing the SolverCoupledProxy.Proxy object would catch this at configuration time.

Comment on lines +157 to +168
if patterns is not None:
unmatched = [p for p, ok in zip(patterns, matched) if not ok]
if unmatched:
raise ValueError(
f"ProxyCoupledMJWarpVBDSolverCfg.{field}: {spec_repr} has no bodies matching {unmatched}."
)
elif isinstance(spec, str) and not body_ids:
# Strings have no asset-cfg safety net — zero matches is almost always a typo.
raise ValueError(
f"ProxyCoupledMJWarpVBDSolverCfg.{field}: {spec_repr} matched no bodies in "
f"`model.body_label` (labels are full post-clone prim paths)."
)
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.

P2 Silent empty return for SceneEntityCfg with no matching bodies

When spec is a SceneEntityCfg with body_names=None and the asset's prim_path matches no bodies in the model (e.g., a typo in the asset name resolves to the wrong prim path on scene_cfg), the function returns an empty body_ids list without raising. The string-spec branch correctly raises for zero matches, but the SceneEntityCfg branch doesn't. The downstream "unclaimed bodies" error from _partition_model_by_entities will eventually fire, but with a misleading count rather than pointing at the offending selector.

Comment on lines +151 to 178
newton_mjwarp_vbd_proxy: CoupledNewtonCfg = CoupledNewtonCfg(
solver_cfg=ProxyCoupledMJWarpVBDSolverCfg(
mjwarp_cfg=MJWarpSolverCfg(
cone="elliptic",
ls_parallel=True,
ls_iterations=20,
integrator="implicitfast",
),
vbd_cfg=VBDSolverCfg(
iterations=10,
),
mjwarp_bodies=["/World/envs/env_.*/Robot"],
proxy_bodies=[
"/World/envs/env_.*/Robot/panda_hand",
"/World/envs/env_.*/Robot/panda_(left|right)finger",
],
proxy_collide_interval=5,
),
model_cfg=NewtonModelCfg(
soft_contact_ke=1e4,
soft_contact_kd=1e-5,
soft_contact_mu=5.0,
shape_material_ke=4e4,
shape_material_kd=1e-5,
shape_material_mu=5.0,
),
num_substeps=10,
)
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.

P2 vbd_bodies left at default [] — relies on undocumented assumption

ProxyCoupledMJWarpVBDSolverCfg defaults vbd_bodies=[], so _partition_model_by_entities requires that mjwarp_bodies=["/World/envs/env_.*/Robot"] captures every rigid body in the Newton model. If the table USD (loaded via UsdFileCfg) or ground plane creates a Newton rigid body entry (body index ≥ 0), it would be unclaimed and trigger a runtime ValueError. This assumption is not documented in the config comment. A brief inline comment explaining why the scene's static assets don't produce Newton body entries would prevent future confusion when the scene changes.

mmichelis added 19 commits May 26, 2026 14:24
Drop the local DeformableNewtonCfg in franka_soft_env_cfg.py and use the
shared CoupledNewtonCfg from isaaclab_contrib.deformable.newton_manager_cfg
instead, so the existing soft/cloth envs match the proxy-coupled manager.
Rename the scene entity, command, event, reward, termination, and mdp
function identifiers from 'deformable' to 'object' in the lift_franka_soft
environments and the lift_franka_soft state machine demo, so the proxy
coupled solver's SceneEntityCfg("object") selectors and the rest of the
lift task family share one asset key. Asset types (DeformableObjectCfg)
and USD prim paths are unchanged.
@mmichelis mmichelis force-pushed the feat/proxycoupledsolver branch from a372856 to ca5ef5d Compare May 26, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant