Exclude body contacts#799
Conversation
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
📝 WalkthroughWalkthroughCollision participation is now gated per-shape via ShapeConfig.has_shape_collision; builder shape/joint registration and collision-pair generation were simplified and tightened. Graph plotting and MuJoCo solver visualization APIs gained optional labeling parameters and body-level collision exclude logic was added. An example enables self-collisions and removes manual filters. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant Builder
participant Model
Note over Builder,Model: Shape registration & joint pairing
User->>Builder: add_shape(shape_cfg)
Builder->>Model: append shape to body_shapes
alt shape_cfg.has_shape_collision
Note over Builder: shape participates in filter pairs
else
Note over Builder: shape excluded from filter pairs
end
User->>Builder: add_joint(parent, child)
Builder->>Model: enumerate parent & child shapes
loop each parent-child shape pair
alt both shapes have has_shape_collision
Builder->>Model: add joint-pair candidate to collision filter
else
Note over Builder: skip pair
end
end
sequenceDiagram
autonumber
participant Solver
participant Model
participant Graph
participant MJSpec as MuJoCoSpec
Note over Solver,Graph: Collision graph coloring
Solver->>Model: selected_shapes (± shape_keys)
Solver->>Graph: build edges using np.triu_indices
Graph-->>Solver: edges
Solver->>Solver: remove edges blocked by shape collision-filter or groups
Solver->>Graph: run coloring -> colors
Graph-->>Solver: colors (optionally labeled by shape_keys)
Note over Solver,MJSpec: MuJoCo assembly with excludes
Solver->>Model: compute body-level exclude pairs
Solver->>MJSpec: add_exclude(body_i, body_j) for pairs
Solver->>MJSpec: add geoms (names from shape_key, friction from shape_mu)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (10)
newton/_src/sim/graph_coloring.py (1)
295-297: Guard against None for node_colors in draw call.Passing
node_color=Noneto NetworkX/Matplotlib can error depending on versions. Draw without the arg when colors aren’t provided.Apply this diff:
- nx.draw_networkx_nodes(G, pos, node_color=node_colors, **default_draw_args) + if node_colors is None: + nx.draw_networkx_nodes(G, pos, **default_draw_args) + else: + nx.draw_networkx_nodes(G, pos, node_color=node_colors, **default_draw_args)newton/_src/sim/builder.py (4)
1985-1991: Double newline in shape labels.You append
"\n(...)"and then"\n".join(...), yielding a blank line. Drop the leading newline in the type.Apply this diff:
- if show_shape_types: - shape_label.append(f"\n({shape_type_str(self.shape_type[i])})") + if show_shape_types: + shape_label.append(f"({shape_type_str(self.shape_type[i])})")
2413-2418: Canonicalize same-body filter pairs for robustness.While current indices likely make
(same_body_shape, shape)ordered, enforce canonical ordering to avoid future edge cases.Apply this diff:
- if cfg.has_shape_collision: + if cfg.has_shape_collision: # no contacts between shapes of the same body for same_body_shape in self.body_shapes[body]: - self.shape_collision_filter_pairs.append((same_body_shape, shape)) + a, b = same_body_shape, shape + if a > b: + a, b = b, a + self.shape_collision_filter_pairs.append((a, b))
2435-2440: Canonicalize parent–child filter pairs too.Match the ordering used in
find_shape_contact_pairsto ensure lookups succeed.Apply this diff:
- for parent_shape in self.body_shapes[parent_body]: - self.shape_collision_filter_pairs.append((parent_shape, shape)) + for parent_shape in self.body_shapes[parent_body]: + a, b = parent_shape, shape + if a > b: + a, b = b, a + self.shape_collision_filter_pairs.append((a, b))
4463-4465: Handle empty contact_pairs without constructing a NumPy array.Warp arrays from
np.array([])can be brittle for vector dtypes; allocate an empty vec2i directly.Apply this diff:
- model.shape_contact_pairs = wp.array(np.array(contact_pairs), dtype=wp.vec2i, device=model.device) + if contact_pairs: + model.shape_contact_pairs = wp.array(np.array(contact_pairs), dtype=wp.vec2i, device=model.device) + else: + model.shape_contact_pairs = wp.empty(shape=(0,), dtype=wp.vec2i, device=model.device)newton/_src/solvers/mujoco/solver_mujoco.py (5)
1670-1680: Avoid false positives by checking both (a,b) and (b,a) in filter setIf model.shape_collision_filter_pairs isn’t guaranteed to store both orders, current membership test can misclassify edges.
Apply this diff:
@@ - num_shapes = len(selected_shapes) - shape_a, shape_b = np.triu_indices(num_shapes, k=1) - cgroup = [model.shape_collision_group[i] for i in selected_shapes] + num_shapes = len(selected_shapes) + shape_a, shape_b = np.triu_indices(num_shapes, k=1) + cgroup = [model.shape_collision_group[i] for i in selected_shapes] + pair_filters = model.shape_collision_filter_pairs @@ - graph_edges = [ - (i, j) - for i, j in zip(shape_a, shape_b, strict=True) - if ( - (selected_shapes[i], selected_shapes[j]) not in model.shape_collision_filter_pairs - and (cgroup[i] == cgroup[j] or cgroup[i] == -1 or cgroup[j] == -1) - ) - ] + graph_edges = [ + (i, j) + for i, j in zip(shape_a, shape_b, strict=True) + if ( + (selected_shapes[i], selected_shapes[j]) not in pair_filters + and (selected_shapes[j], selected_shapes[i]) not in pair_filters + and (cgroup[i] == cgroup[j] or cgroup[i] == -1 or cgroup[j] == -1) + ) + ]
1664-1669: Remove unused mappingto_color_shape_index is created but never used.
2063-2063: Sanitize geom names derived from shape_keyMuJoCo names must be valid identifiers; if shape_key can contain spaces or special chars, sanitize (e.g., replace non-alnum with '', ensure first char is letter/).
2107-2111: Color bit budget: guard for >=32 colorsIf graph coloring yields >=32, contype won’t be set and MuJoCo defaults may cause unintended collisions. Consider modulo bucketing or fallback to affinity-based excludes when color >= 32.
2360-2365: Add excludes safely; consider dedup and self-checks
- If body_filters can contain duplicates across envs/runs, dedup to avoid bloating XML.
- Optionally skip when b1 or b2 not in body_mapping (defensive).
Minor, but helps robustness.
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
newton/_src/sim/builder.py(6 hunks)newton/_src/sim/graph_coloring.py(2 hunks)newton/_src/solvers/mujoco/solver_mujoco.py(9 hunks)newton/examples/robot/example_robot_allegro_hand.py(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
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.
📚 Learning: 2025-08-20T18:02:36.099Z
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.
Applied to files:
newton/_src/sim/builder.py
🧬 Code graph analysis (2)
newton/_src/sim/builder.py (1)
newton/_src/geometry/flags.py (1)
ShapeFlags(30-42)
newton/_src/solvers/mujoco/solver_mujoco.py (2)
newton/_src/sim/builder.py (3)
shape_count(508-512)body_count(515-519)joint_count(522-526)newton/_src/sim/graph_coloring.py (2)
color_graph(189-259)plot_graph(262-314)
⏰ 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 (11)
newton/examples/robot/example_robot_allegro_hand.py (1)
98-102: Enabling self-collisions may increase contact counts; verify contact budgets.With
enable_self_collisions=True, ensurencon_per_env=150andnjmax=200are sufficient for peak frames; otherwise you may silently drop contacts. Consider bumping these or adding a debug assert onlen(self.contacts)vs budget during stress poses.newton/_src/sim/graph_coloring.py (2)
262-272: API extension looks good.New
node_labels/node_colorsparams and updated docstring/readback behavior are consistent.
262-272: Verified — no downstream changes requiredOnly usage found: newton/_src/solvers/mujoco/solver_mujoco.py (lines ~1694–1696). It calls plot_graph(vertices=..., edges=...) with keyword args only; the new optional node_labels/node_colors params default safely.
newton/_src/sim/builder.py (3)
1353-1363: Joint parent/child collision filtering gated on COLLIDE_SHAPES — good.Skips generating filter pairs for non-colliding shapes and enforces canonical order. Looks correct.
4433-4465: find_shape_contact_pairs: efficient and gated to colliding shapes — nice.Sorting by env group and early-break improve performance; group and filter checks read cleanly.
1920-1937: No internal positional callers found — confirm external API impactRipgrep found only the function definition at newton/_src/sim/builder.py:1914; no other calls in the repo. Reordering parameters (inserting show_shape_keys before show_shape_types) is still a breaking change for external callers — restore the original order or add a backward-compatible shim that preserves positional semantics.
newton/_src/solvers/mujoco/solver_mujoco.py (5)
1646-1650: API extension looks goodAdding shape_keys to color_collision_shapes is a useful ergonomics improvement for debugging/visualization.
2013-2016: Good: explicit int32 for indicesUsing np.int32 for selected_* arrays prevents dtype surprises when passing to Warp.
2021-2021: Joint order check tweak is fineany(...) keeps it simple without allocating an array.
2034-2044: Body-filter precompute is a nice win; confirm no -1 world body involvementLooks good. Please double-check selected_bodies cannot include -1; excludes involving the world aren’t needed (collision system already skips world-attached shape collisions across builders per prior discussion).
2113-2119: Per-shape friction mapping: LGTMComputes slide/torsional/rolling as (μ, τ·μ, ρ·μ). Matches update_geom_properties_kernel semantics.
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
newton/_src/sim/builder.py (1)
2435-2440: Also canonicalize parent–child filter pairs and skip non‑colliding parentsSame ordering issue here; additionally, skip parents without
COLLIDE_SHAPESto align with “colliding shapes only”.Apply:
- if cfg.has_shape_collision and cfg.collision_filter_parent and body > -1 and body in self.joint_parents: - for parent_body in self.joint_parents[body]: - if parent_body > -1: - for parent_shape in self.body_shapes[parent_body]: - self.shape_collision_filter_pairs.append((parent_shape, shape)) + if cfg.has_shape_collision and cfg.collision_filter_parent and body > -1 and body in self.joint_parents: + for parent_body in self.joint_parents[body]: + if parent_body > -1: + for parent_shape in self.body_shapes[parent_body]: + if not (self.shape_flags[parent_shape] & ShapeFlags.COLLIDE_SHAPES): + continue + a, b = parent_shape, shape + if a > b: + a, b = b, a + self.shape_collision_filter_pairs.append((a, b))newton/_src/solvers/mujoco/solver_mujoco.py (1)
1646-1700: Fix asymmetric filter checks, remove dead code, correct return documentation
- In graph_edges (and the earlier exclusion loop), check both (si, sj) and (sj, si) against model.shape_collision_filter_pairs to avoid unfiltered pairs when storage order differs.
- Remove the unused to_color_shape_index mapping at the top of color_collision_shapes.
- Update the docstring’s Returns section to reflect that the function returns an array of shape (model.shape_count,) rather than (num_shapes,).
🧹 Nitpick comments (7)
newton/_src/sim/graph_coloring.py (2)
262-272: plot_graph: accept optional labels/colors more defensivelySmall guardrails will prevent confusing errors when
node_labels/node_colorslengths don’t matchvertices, and avoid passingNonetonode_color.Apply:
def plot_graph(vertices, edges, edge_labels=None, node_labels=None, node_colors=None): @@ - if edge_labels is None: - edge_labels = [] + if edge_labels is None: + edge_labels = [] + if node_labels is not None and len(node_labels) != len(vertices): + raise ValueError("node_labels length must match vertices length") + if node_colors is not None and len(node_colors) != len(vertices): + raise ValueError("node_colors length must match vertices length")
296-301: Avoid passing node_color=None; ensure clean label fallback
- Only pass
node_colorwhen provided.- Current type-line adds a leading newline when only shape type is shown; drop the prefix newline to avoid leading blank labels.
Apply:
- nx.draw_networkx_nodes(G, pos, node_color=node_colors, **default_draw_args) + draw_args = dict(default_draw_args) + if node_colors is not None: + draw_args["node_color"] = node_colors + nx.draw_networkx_nodes(G, pos, **draw_args) @@ - labels=dict(enumerate(node_labels if node_labels is not None else vertices)), + labels=dict(enumerate(node_labels if node_labels is not None else vertices)),newton/_src/sim/builder.py (3)
1985-1991: Shape labels may start with a leading newline when only type is shownWhen
show_shape_keys=Falseandshow_shape_types=True,shape_labelbecomes["\n(type)"], producing a leading blank line. Drop the prefix newline and rely on join.Apply:
- shape_label = [] + shape_label = [] if show_shape_keys: shape_label.append(self.shape_key[i]) if show_shape_types: - shape_label.append(f"\n({shape_type_str(self.shape_type[i])})") - vertices.append("\n".join(shape_label)) + shape_label.append(f"({shape_type_str(self.shape_type[i])})") + vertices.append("\n".join(shape_label))
1920-1937: plot_articulation: new show_shape_keys option — nice usabilityThe new toggle is sensible and defaults are reasonable. Please reflect the new argument in the public API docs/migration guide.
Add a brief note in docs/migration.rst describing:
- ShapeConfig.has_shape_collision (new)
- plot_articulation(show_shape_keys=True) (new default)
2413-2417: Optional: centralize pair canonicalization to avoid future driftConsider a tiny helper (local function or private method)
append_filter_pair(i, j)that sorts and appends once. Reduces repetition and prevents regressions.Also applies to: 2435-2440
newton/_src/solvers/mujoco/solver_mujoco.py (2)
1071-1132: Clamp mu to non-negative and preempt invalid frictionIf a material supplies a negative μ (bad input), MuJoCo will receive negative friction, which is non-physical. Clamp μ ≥ 0 in-kernel.
Apply this diff:
- mu = shape_mu[shape_idx] - geom_friction[worldid, geom_idx] = wp.vec3f(mu, torsional_friction * mu, rolling_friction * mu) + mu = wp.max(shape_mu[shape_idx], 0.0) + geom_friction[worldid, geom_idx] = wp.vec3f(mu, torsional_friction * mu, rolling_friction * mu)
2034-2044: Body-exclude + color: order of operations is good; guard colliding_shapes emptySequence (compute excludes → colors) makes sense. If
colliding_shapesis empty, both operations are no-ops; consider an early return to skip work.
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
newton/_src/sim/builder.py(6 hunks)newton/_src/sim/graph_coloring.py(2 hunks)newton/_src/solvers/mujoco/solver_mujoco.py(9 hunks)newton/examples/robot/example_robot_allegro_hand.py(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
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.
📚 Learning: 2025-08-20T18:02:36.099Z
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.
Applied to files:
newton/_src/sim/builder.py
📚 Learning: 2025-08-12T17:51:37.474Z
Learnt from: nvlukasz
PR: newton-physics/newton#519
File: newton/geometry.py:16-22
Timestamp: 2025-08-12T17:51:37.474Z
Learning: In the Newton public API refactor, common geometry symbols like ParticleFlags and ShapeFlags are now exposed at the top level in newton/__init__.py rather than through newton.geometry. The newton/geometry.py module is intentionally focused only on broad-phase collision detection classes (BroadPhaseAllPairs, BroadPhaseExplicit, BroadPhaseSAP).
Applied to files:
newton/_src/sim/builder.py
🧬 Code graph analysis (2)
newton/_src/solvers/mujoco/solver_mujoco.py (2)
newton/_src/sim/builder.py (3)
shape_count(508-512)body_count(515-519)joint_count(522-526)newton/_src/sim/graph_coloring.py (2)
color_graph(189-259)plot_graph(262-314)
newton/_src/sim/builder.py (1)
newton/_src/geometry/flags.py (1)
ShapeFlags(30-42)
⏰ 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 GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (10)
newton/_src/sim/builder.py (2)
168-171: ShapeConfig: explicit collision toggles LGTMIntroducing
has_shape_collision/has_particle_collisionand wiring them intoflagsis clear and backward‑compatible via the setter. No issues spotted.Also applies to: 179-191
1353-1357: add_joint: filter only colliding shapes — good tighteningSkipping non‑colliding shapes on both sides before adding filter pairs is correct and prevents unnecessary entries.
newton/examples/robot/example_robot_allegro_hand.py (1)
98-98: Enabling self-collisions: verify contact budgetWith self-collisions on, contact count per env may rise. Ensure
ncon_per_env=150is sufficient for the worst case to avoid contact truncation; bump if you observe overflows.newton/_src/solvers/mujoco/solver_mujoco.py (7)
1947-1948: LGTM: source of μ now comes from materialsUsing
model.shape_material_muhere keeps CPU-path friction consistent with the runtime kernel’s per-geom friction update.
2013-2016: LGTM: explicit int32 typing for selectionsExplicit dtypes avoid accidental upcasts on device transfers and align with Warp’s int32 expectations.
2021-2025: LGTM: clearer joint-order validationIndex-based comparison is simple and avoids list allocation; warning is appropriate.
2063-2064: Geom naming via shape_key improves debuggabilityNames are now stable and readable; index suffix preserves uniqueness.
2113-2119: LGTM: per-geom friction set consistently for CPU pathMatches kernel formula; ensures parity across backends.
2360-2365: Spec excludes applied at body level — confirm no unintended suppressionspec.add_exclude is applied per-body (newton/_src/solvers/mujoco/solver_mujoco.py:2362–2364) and will suppress collisions for any shapes subsequently attached to those bodies. Repo scan shows body/shape state is set/mutated during build/import (sim/builder.py — m.shape_flags / m.body_shapes set ~4125; utils/import_usd.py clears shape collision flags ~1246). Confirm shape attachments and COLLIDE_SHAPES flags are finalized before spec.compile (solver_mujoco.py:2366); otherwise use shape-pair excludes or gate the body-level excludes behind a feature flag.
1646-1659: Small: parameter doc type and Python version — resolvedpyproject.toml declares requires-python = ">=3.10" and classifiers include Python 3.10–3.12, so zip(..., strict=True) and PEP 604 annotations are supported; no change required.
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
newton/_src/sim/builder.py (1)
2435-2440: Parent–child filter pairs should also be canonicalized and gatedSame issue here: append uses raw (parent_shape, shape) and doesn’t check parent_shape’s COLLIDE_SHAPES. Normalize and gate to avoid missed filters and noise.
Apply:
- if cfg.has_shape_collision and cfg.collision_filter_parent and body > -1 and body in self.joint_parents: - for parent_body in self.joint_parents[body]: - if parent_body > -1: - for parent_shape in self.body_shapes[parent_body]: - self.shape_collision_filter_pairs.append((parent_shape, shape)) + if cfg.has_shape_collision and cfg.collision_filter_parent and body > -1 and body in self.joint_parents: + for parent_body in self.joint_parents[body]: + if parent_body > -1: + for parent_shape in self.body_shapes[parent_body]: + if not (self.shape_flags[parent_shape] & ShapeFlags.COLLIDE_SHAPES): + continue + a, b = parent_shape, shape + if a > b: + a, b = b, a + self.shape_collision_filter_pairs.append((a, b))
♻️ Duplicate comments (3)
newton/_src/sim/builder.py (1)
2413-2418: Canonicalize and gate same‑body filter pairs; current order can miss filteringPairs are appended as (same_body_shape, shape) without normalization and without checking the existing shape’s COLLIDE_SHAPES flag. find_shape_contact_pairs checks membership using canonical (min, max), so reversed tuples won’t match and same‑body pairs may slip through.
Apply:
- if cfg.has_shape_collision: - # no contacts between shapes of the same body - for same_body_shape in self.body_shapes[body]: - self.shape_collision_filter_pairs.append((same_body_shape, shape)) + if cfg.has_shape_collision: + # no contacts between shapes of the same body + for same_body_shape in self.body_shapes[body]: + # skip non-colliding shapes + if not (self.shape_flags[same_body_shape] & ShapeFlags.COLLIDE_SHAPES): + continue + # store in canonical order for consistent lookup + a, b = same_body_shape, shape + if a > b: + a, b = b, a + self.shape_collision_filter_pairs.append((a, b))Optional: also skip when body == -1 if global–global contacts are already excluded by the collision system.
Run to find any other non-canonical appends:
#!/bin/bash rg -nP -C2 'shape_collision_filter_pairs\.append\('newton/_src/solvers/mujoco/solver_mujoco.py (2)
1618-1651: Fix potential KeyError and canonicalize pair membership checksThe method has two issues:
- Direct indexing of
model.body_shapes[b1]will raise KeyError if a body has no shapes- Shape collision filter pairs may not be stored in canonical order, causing missed exclusions
Apply this diff to fix both issues:
@staticmethod def find_body_collision_filter_pairs( model: Model, selected_bodies: nparray, colliding_shapes: nparray, ): """For shape collision filter pairs, find body collision filter pairs that are contained within.""" body_exclude_pairs = [] shape_set = set(colliding_shapes) - body_shapes = {} - for body in selected_bodies: - shapes = model.body_shapes[body] - shapes = [s for s in shapes if s in shape_set] - body_shapes[body] = shapes - bodies_a, bodies_b = np.triu_indices(len(selected_bodies), k=1) for body_a, body_b in zip(bodies_a, bodies_b, strict=True): b1, b2 = selected_bodies[body_a], selected_bodies[body_b] - shapes_1 = body_shapes[b1] - shapes_2 = body_shapes[b2] + shapes_1 = model.body_shapes.get(b1, []) + shapes_2 = model.body_shapes.get(b2, []) + shapes_1 = [s for s in shapes_1 if s in shape_set] + shapes_2 = [s for s in shapes_2 if s in shape_set] + if not shapes_1 or not shapes_2: + continue excluded = True for shape_1 in shapes_1: for shape_2 in shapes_2: - if shape_1 > shape_2: - s1, s2 = shape_2, shape_1 - else: - s1, s2 = shape_1, shape_2 - if (s1, s2) not in model.shape_collision_filter_pairs: + # Check both orderings since pairs may not be canonicalized + if ((shape_1, shape_2) not in model.shape_collision_filter_pairs and + (shape_2, shape_1) not in model.shape_collision_filter_pairs): excluded = False break + if not excluded: + break if excluded: body_exclude_pairs.append((b1, b2)) return body_exclude_pairs
1681-1688: Normalize shape pair before membership check in solver_mujoco.pyVerified shape_collision_filter_pairs are appended from multiple importers and builder code and ultimately turned into a set; some append sites do not guarantee a consistent ordering. Make the membership test order-independent.
Location: newton/_src/solvers/mujoco/solver_mujoco.py (around lines 1681–1688)
Suggested change (use min/max to normalize the pair):
graph_edges = [ (i, j) for i, j in zip(shape_a, shape_b, strict=True) if ( - (selected_shapes[i], selected_shapes[j]) not in model.shape_collision_filter_pairs + (min(selected_shapes[i], selected_shapes[j]), max(selected_shapes[i], selected_shapes[j])) + not in model.shape_collision_filter_pairs and (cgroup[i] == cgroup[j] or cgroup[i] == -1 or cgroup[j] == -1) ) ]
🧹 Nitpick comments (3)
newton/_src/sim/builder.py (1)
1920-1920: New plot_articulation flag works; avoid empty labels edge-caseWhen both show_shape_keys and show_shape_types are False, shape nodes get an empty label. Harmless, but consider defaulting to the shape index to keep labels non-empty.
Would you like a small patch to show the index when both toggles are False?
Also applies to: 1934-1935, 1985-1990
newton/_src/solvers/mujoco/solver_mujoco.py (2)
2121-2127: Friction setup uses undefinedshape_muvariableAfter fixing line 1955, ensure the geom friction setup uses the correct variable name consistently.
Verify the variable name is consistent after the fix at line 1955:
# set friction from Newton shape materials using model's friction parameters mu = shape_mu[shape] geom_params["friction"] = [ mu, model.rigid_contact_torsional_friction * mu, model.rigid_contact_rolling_friction * mu, ]
2029-2029: Simplify joint order validationThe current validation using
any()with a generator is correct but could be more readable.Consider this more explicit approach:
- if any(joint_order[i] != i for i in range(len(joints_simple))): + if not np.array_equal(joint_order, np.arange(len(joints_simple))):
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
newton/_src/sim/builder.py(6 hunks)newton/_src/solvers/mujoco/solver_mujoco.py(9 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
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.
📚 Learning: 2025-08-20T18:02:36.099Z
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.
Applied to files:
newton/_src/sim/builder.pynewton/_src/solvers/mujoco/solver_mujoco.py
🧬 Code graph analysis (2)
newton/_src/sim/builder.py (1)
newton/_src/geometry/flags.py (1)
ShapeFlags(30-42)
newton/_src/solvers/mujoco/solver_mujoco.py (2)
newton/_src/sim/builder.py (3)
shape_count(508-512)body_count(515-519)joint_count(522-526)newton/_src/sim/graph_coloring.py (2)
color_graph(189-259)plot_graph(262-314)
⏰ 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 (5)
newton/_src/sim/builder.py (1)
1353-1357: Good gating on collision participation before adding joint-based filtersSkipping parent/child shapes without COLLIDE_SHAPES avoids unnecessary filter pairs. Looks correct.
newton/_src/solvers/mujoco/solver_mujoco.py (4)
1654-1709: Excellent addition of shape labeling for graph visualization!The new
shape_keysparameter makes debugging collision graphs much more intuitive by allowing meaningful labels on nodes. The implementation cleanly integrates with the existing visualization without disrupting the core graph coloring logic.
2368-2373: Good addition of body-level collision exclusions!The implementation correctly adds MuJoCo contact exclusions for body pairs that should not collide, ensuring parent-child collisions are properly ignored even when one body is static.
2018-2023: Explicit typing improves maintainabilityGood practice using explicit
np.int32dtype for the shape/body/joint arrays when not separating environments. This ensures type consistency across different code paths.
2071-2071: Improved geom naming using model.shape_keyGood change from type-based naming to using the actual shape keys, making debugging and identification easier.
|
The benchmark performance has decreased slightly, however I found this not to be severe since the overhead for computing those body pairs to exclude from collisions is rather small when comparing 8192 envs (3 runs each):
|
Signed-off-by: Eric Heiden <eric-heiden@outlook.com> Co-authored-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com> Co-authored-by: camevor <camevor@nvidia.com>
Signed-off-by: Eric Heiden <eric-heiden@outlook.com> Co-authored-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com> Co-authored-by: camevor <camevor@nvidia.com>
Description
Continuation of @camevor's work on #793:
Newton Migration Guide
Please ensure the migration guide for warp.sim users is up-to-date with the changes made in this PR.
docs/migration.rstis up-to dateBefore your PR is "Ready for review"
newton/tests/test_examples.py)pre-commit run -aSummary by CodeRabbit
New Features
Improvements
Bug Fixes
Examples