Skip to content

Use body excludes for collision filtering#793

Closed
camevor wants to merge 3 commits into
newton-physics:mainfrom
camevor:body-excludes
Closed

Use body excludes for collision filtering#793
camevor wants to merge 3 commits into
newton-physics:mainfrom
camevor:body-excludes

Conversation

@camevor

@camevor camevor commented Sep 19, 2025

Copy link
Copy Markdown
Member

Description

Newton Migration Guide

Please ensure the migration guide for warp.sim users is up-to-date with the changes made in this PR.

  • The migration guide in docs/migration.rst is up-to date

Before your PR is "Ready for review"

  • Necessary tests have been added and new examples are tested (see newton/tests/test_examples.py)
  • Documentation is up-to-date
  • Code passes formatting and linting checks with pre-commit run -a

Summary by CodeRabbit

  • New Features
    • More granular collision filtering during MuJoCo conversion, auto-creating body-pair exclusions to prevent unwanted collisions and improve simulation performance.
  • Bug Fixes
    • More reliable geometry referencing in MuJoCo export, reducing errors when applying excludes.
    • Correct handling of integer indexing and 0-length slices in selection utilities, preventing slicing edge-case issues.
  • Refactor
    • Streamlined selection logic for simpler, faster array access and improved conversion robustness.

@coderabbitai

coderabbitai Bot commented Sep 19, 2025

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds two collision-filtering helpers to SolverMuJoCo to precompute body-pair excludes from shape-level filters and integrates them into the MuJoCo conversion flow, switches shape-to-geom mapping to geom names, and refines integer and zero-length slice handling in selection._get_attribute_array.

Changes

Cohort / File(s) Summary
MuJoCo solver: collision filtering & mapping
newton/_src/solvers/mujoco/solver_mujoco.py
Adds static methods find_body_collision_filter_pairs(...) and shapes_can_collide(...). Integrates find_body_collision_filter_pairs into fill_arr_from_dict to compute body_filters and remaining_shape_filters, inserts spec.add_exclude calls for body-pair excludes, and changes shape_mapping to map Newton shape IDs → MuJoCo geom names (strings) with name→id resolution later.
Selection utilities: slicing behavior
newton/_src/utils/selection.py
Simplifies _get_attribute_array handling for integer _slice to return attrib[:, _slice] directly. For zero-length strided slices (start == stop), constructs an empty view using attrib.ptr with shape (attrib.shape[0], 0, *attrib.shape[2:]) and copy=False, removing pointer-offset arithmetic. No public API signature changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Converter as Model Converter
  participant Solver as SolverMuJoCo
  participant Spec as MuJoCo Spec
  participant MJ as MuJoCo Model

  Converter->>Solver: select colliding_shapes & shape_filters
  Converter->>Solver: find_body_collision_filter_pairs(colliding_shapes, shape_filters, shape_groups, shape_bodies, body_shapes)
  Solver-->>Converter: body_filters, remaining_shape_filters

  note right of Converter: record geom names in shape_mapping

  Converter->>MJ: create geoms (names preserved)
  Converter->>Spec: add_exclude(body_pair) for each body_filters
  Converter->>MJ: finalize/compile model (excludes applied)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • AntoineRichard
  • adenzler-nvidia

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Use body excludes for collision filtering" is a concise, single-sentence summary that accurately captures the PR's primary change — adding body-level exclusion logic and integrating it into the MuJoCo collision-filtering flow. The raw_summary shows added methods to compute body filter pairs, insertion of spec.add_exclude calls, and mapping changes that directly implement this behavior, and the PR objectives explicitly describe switching to body excludes. It is specific and clear enough for a teammate scanning history to understand the main change.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56a7814 and d0cdd43.

📒 Files selected for processing (1)
  • newton/_src/solvers/mujoco/solver_mujoco.py (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Learnt from: eric-heiden
PR: newton-physics/newton#587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.099Z
Learning: In Newton physics, collisions between shapes with body index -1 (world-attached/global shapes) are automatically skipped by the collision system, so no manual collision filter pairs need to be added between global shapes from different builders.
⏰ 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). (3)
  • GitHub Check: Run GPU Benchmarks (Pull Request)
  • GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
  • GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (5)
newton/_src/solvers/mujoco/solver_mujoco.py (5)

1618-1646: LGTM! Collision filtering optimization with body-pair exclusion.

The find_body_collision_filter_pairs method efficiently consolidates shape-level collision filters into body-level filters when all shape pairs between two bodies are filtered. This reduces the number of individual shape filters needed in MuJoCo and improves collision detection performance.

The logic correctly:

  • Identifies body pairs where all colliding shape combinations are filtered
  • Maintains remaining shape filters that can't be consolidated
  • Handles edge cases with non-filterable body pairs

1647-1667: LGTM! Comprehensive collision detection logic.

The shapes_can_collide method correctly implements Newton's collision group logic:

  • Self-collision prevention (line 1649-1650)
  • Shape filter pair exclusions (lines 1651-1652)
  • Group-based collision rules matching Newton's broad phase behavior (lines 1656-1666)

The group logic properly handles:

  • Group 0: No collisions
  • Positive groups: Only collide with same group or negative groups
  • Negative groups: Collide with different negative groups

2003-2004: Shape mapping now uses geom names instead of indices.

The comment correctly reflects the new mapping semantics where Newton shape IDs are mapped to MuJoCo geom names (strings) rather than numeric indices. This change enables more robust lookup using mj_name2id later in the code.


2045-2054: Body-level collision filtering integration.

The collision filtering logic has been enhanced to precompute body-pair filters from shape-level collision rules. This optimization reduces the number of individual collision exclusions needed in MuJoCo by consolidating them at the body level when possible.

The integration correctly:

  • Uses colliding shapes as input for filter computation
  • Passes Newton model's collision filter data and body-shape mappings
  • Separates body-level filters from remaining shape-level filters

2379-2382: Body exclusion pairs applied to MuJoCo specification.

The body-level collision filters are properly applied to the MuJoCo model by adding exclude constraints between body pairs. This implementation correctly maps Newton body IDs to MuJoCo body IDs and uses the body names from the compiled specification.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
newton/_src/utils/selection.py (1)

167-170: Pipeline failure: KeyError on empty ArticulationView selection.

GPU unit tests hit “No matching articulations”. Either handle empty matches more gracefully (return an empty view with all-zero slices) or include the pattern in the error to aid debugging.

Proposed minimal improvement:

-            raise KeyError("No matching articulations")
+            raise KeyError(f"No matching articulations for pattern '{pattern}'")

Optionally, add a flag allow_empty=False in the ctor that, when True, initializes an empty view (all counts/slices zero) instead of raising.

newton/_src/solvers/mujoco/solver_mujoco.py (2)

1618-1645: Collapse shape filters to body excludes: correct but optimize membership and reuse sets.

Logic is sound; however, using list membership inside a nested product is O(N·S1·S2). Convert filters to a set and cache per‑body colliding shapes to cut cost.

Apply:

 @staticmethod
 def find_body_collision_filter_pairs(colliding_shapes, shape_filters, shape_bodies, body_shapes):
-    """For shape collision filter pairs, find body collision filter pairs that are contained within."""
-    colliding_shapes = set(colliding_shapes)
-    body_filters = set()
-    body_non_filterable = set()
-    remaining_shape_filters = []
-    for sp in shape_filters:
+    """For shape collision filter pairs, find body collision filter pairs that are contained within."""
+    colliding_shapes = set(colliding_shapes)
+    # O(1) membership
+    sf_set = set(tuple(p) for p in shape_filters)
+    body_filters: set[tuple[int, int]] = set()
+    body_non_filterable: set[tuple[int, int]] = set()
+    remaining_shape_filters = []
+    # cache per-body colliding shapes
+    coll_by_body: dict[int, set[int]] = {}
+    for sp in shape_filters:
         if sp[0] not in colliding_shapes or sp[1] not in colliding_shapes:
             continue
         b1, b2 = shape_bodies[sp[0]], shape_bodies[sp[1]]
         bp = min(b1, b2), max(b1, b2)
         if bp in body_filters:
             continue
         if bp in body_non_filterable:
             remaining_shape_filters.append(sp)
             continue
-        b1_shapes = {s for s in body_shapes[bp[0]] if s in colliding_shapes}
-        b2_shapes = {s for s in body_shapes[bp[1]] if s in colliding_shapes}
-        if all(
-            (bs1, bs2) in shape_filters or (bs2, bs1) in shape_filters for bs1, bs2 in product(b1_shapes, b2_shapes)
-        ):
+        b1_shapes = coll_by_body.setdefault(bp[0], {s for s in body_shapes[bp[0]] if s in colliding_shapes})
+        b2_shapes = coll_by_body.setdefault(bp[1], {s for s in body_shapes[bp[1]] if s in colliding_shapes})
+        if all(((a, b) in sf_set) or ((b, a) in sf_set) for a, b in product(b1_shapes, b2_shapes)):
             body_filters.add(bp)
         else:
             body_non_filterable.add(bp)
             remaining_shape_filters.append(sp)
     return list(body_filters), remaining_shape_filters

2356-2359: Skip excludes involving the world body.

MuJoCo excludes between world and a body are unnecessary and may be unsupported. Also, per prior learning, Newton already skips collisions between global shapes across builders.

Guard:

-for b1, b2 in body_filters:
+for b1, b2 in body_filters:
+    if -1 in (b1, b2):
+        continue
     mb1, mb2 = body_mapping[b1], body_mapping[b2]
     spec.add_exclude(bodyname1=spec.bodies[mb1].name, bodyname2=spec.bodies[mb2].name)
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08ff1bf and 56a7814.

📒 Files selected for processing (2)
  • newton/_src/solvers/mujoco/solver_mujoco.py (4 hunks)
  • newton/_src/utils/selection.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Learnt from: eric-heiden
PR: newton-physics/newton#587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.099Z
Learning: In Newton physics, collisions between shapes with body index -1 (world-attached/global shapes) are automatically skipped by the collision system, so no manual collision filter pairs need to be added between global shapes from different builders.
🪛 Ruff (0.12.2)
newton/_src/utils/selection.py

521-521: Avoid specifying long messages outside the exception class

(TRY003)


526-526: Shebang should contain python, pytest, or uv run

(EXE003)


526-526: Shebang is present but file is not executable

(EXE001)


526-526: Shebang should be at the beginning of the file

(EXE005)

🪛 GitHub Actions: GPU Unit Tests on AWS EC2
newton/_src/utils/selection.py

[error] 169-169: KeyError: 'No matching articulations' raised during ArticulationView initialization.

⏰ 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). (3)
  • GitHub Check: Run GPU Benchmarks (Pull Request)
  • GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
  • GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (3)
newton/_src/utils/selection.py (1)

525-535: 0-length slice workaround: safe but keep strides invariant intentionally.

Reusing attrib.ptr with shape (batch, 0, ...) avoids fragile pointer math and is fine as long as no writes occur. Good interim until Warp #958 is fixed.

Once Warp fixes #958, consider reverting to native slicing to reduce custom code paths.

newton/_src/solvers/mujoco/solver_mujoco.py (2)

1981-1982: Store geom names instead of indices: good move.

Names are stable across MuJoCo compile and simplify mapping. You already ensure uniqueness via “{type}_{shape}”.


2022-2031: No action required — self.model.body_shapes is host-side.

newton/_src/sim/model.py initializes self.body_shapes as a Python mapping and newton/_src/sim/builder.py assigns it directly (m.body_shapes = self.body_shapes); utils/selection.py and tests also use Python lists — no host-copy needed before calling find_body_collision_filter_pairs.

Comment thread newton/_src/utils/selection.py
@camevor camevor requested a review from eric-heiden September 19, 2025 16:37
return list(body_filters), remaining_shape_filters

@staticmethod
def shapes_can_collide(a, b, shape_filter_pairs=None, shape_group=None):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I believe this logic should also apply within color_collision_shapes when building the graph, but I'm not 100% sure so I left it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you're right, although in practice we don't yet have environments with different collision groups per env, so let's revisit this later.

@eric-heiden eric-heiden mentioned this pull request Sep 19, 2025
4 tasks
@eric-heiden

Copy link
Copy Markdown
Member

This work has now been merged through this PR #799 that has a slightly faster implementation of the body exclusion pairs (although it skips the collision group handling).

@camevor camevor deleted the body-excludes branch October 17, 2025 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants