Skip to content

Fix cylinder vs plane collisions#831

Merged
nvtw merged 8 commits into
newton-physics:mainfrom
nvtw:dev/tw/fix_cylinder_plane_collisions
Sep 29, 2025
Merged

Fix cylinder vs plane collisions#831
nvtw merged 8 commits into
newton-physics:mainfrom
nvtw:dev/tw/fix_cylinder_plane_collisions

Conversation

@nvtw

@nvtw nvtw commented Sep 25, 2025

Copy link
Copy Markdown
Member

Description

Fixes #826

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

  • Bug Fixes

    • Improved cylinder–plane collision handling with a validity check to prevent spurious contacts and unnecessary processing.
    • Standardized collision flow to respect validity signals, enabling consistent early exits and more reliable physics.
    • Adjusted cylinder radius handling to avoid overestimated contact sizes, improving simulation accuracy.
  • Tests

    • Updated rigid-contact tests to include a cylinder scenario, revised insertion sequence and expected end positions, and increased solver iteration limits for stability.

@nvtw nvtw self-assigned this Sep 25, 2025
@coderabbitai

coderabbitai Bot commented Sep 25, 2025

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Cylinder-plane contact now returns an explicit validity flag and callers gate processing on it; cylinder is excluded from effective-radius calculation in geo data. A test inserts a cylinder (free joint) and updates expected positions; solver njmax increased.

Changes

Cohort / File(s) Summary of Changes
Collision kernel & pipeline gating
newton/_src/geometry/kernels.py
cylinder_plane_collision now returns (p_a_world, p_b_world, normal, distance, valid). Call sites in the collision flow unpack valid and skip/early-return when invalid; broadphase/narrowphase/handle_contact_pairs updated to respect validity.
GeoData radius handling
newton/_src/geometry/kernels.py
Cylinder contribution to radius_eff calculation in create_geo_data is commented out (cylinder no longer treated as having an effective radius).
Test updates
newton/tests/test_rigid_contact.py
test_shapes_on_plane now inserts a cylinder body at x=5.0 (free joint, radius/height scaled) and moves the mesh body to x=7.0; expected intermediate/final end positions adjusted. Solver mujoco_warp njmax increased from 100 to 150.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Broadphase
  participant Narrowphase as cylinder_plane_collision
  participant Pipeline as handle_contact_pairs

  Broadphase->>Narrowphase: Request contact info (cylinder, plane)
  Note right of Narrowphase #DDEBF7: compute (pA, pB, normal, distance, valid)
  Narrowphase-->>Broadphase: Return contact tuple

  Broadphase->>Pipeline: Dispatch contact tuple
  alt valid == true
    Pipeline->>Pipeline: Continue full contact processing
  else valid == false
    Note right of Pipeline #FDE9E9: Skip / early return for this contact
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • mmacklin
  • eric-heiden
  • camevor

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The change to increase the njmax solver parameter in the mujoco_warp test configuration does not relate to correcting cylinder-vs-plane contact positions and appears to be an unrelated adjustment. Revert or isolate the njmax solver configuration change into a separate pull request or provide justification for why this test parameter adjustment is necessary for the cylinder-plane collision fix.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Fix cylinder vs plane collisions” succinctly and accurately describes the primary purpose of the changeset by indicating that the update addresses collision handling between cylinder and plane shapes, matching the core modifications made in the geometry collision logic.
Linked Issues Check ✅ Passed The pull request implements the key fix from issue #826 by removing the cylinder from the effective radius calculation in create_geo_data and adding a validity check in handle_contact_pairs to prevent spurious cylinder‐plane contact points, and updates the corresponding test to verify correct contact positions.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@nvtw nvtw requested a review from eric-heiden September 25, 2025 07:39

@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: 0

🧹 Nitpick comments (2)
newton/_src/geometry/kernels.py (2)

1442-1455: Avoid magic threshold; use finiteness check for invalid contacts.

Comparing to 1.0e5 is brittle and unit-dependent. The primitive returns +inf for invalid contacts; prefer a finiteness check.

Apply this diff:

-    # Check if this contact point is valid (primitive function returns wp.inf for invalid contacts)
-    valid = distance < 1.0e5  # Use a reasonable threshold instead of exact wp.inf comparison
+    # Check if this contact point is valid (primitive returns +inf for invalid contacts)
+    valid = wp.isfinite(distance)

If wp.isfinite is unavailable here, fall back to an explicit comparison:
valid = distance < wp.inf


2163-2170: Early-return before doing work when contact is invalid.

Micro-optimization and clearer flow: check valid before flipping the normal.

Apply this diff:

-        # Flip the normal since we flipped the arguments
-        normal = -neg_normal
-        if not valid:
-            return
+        if not valid:
+            return
+        # Flip the normal since we flipped the arguments
+        normal = -neg_normal
📜 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 1bad84b and ca8efef.

📒 Files selected for processing (2)
  • newton/_src/geometry/kernels.py (4 hunks)
  • newton/tests/test_rigid_contact.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
newton/tests/test_rigid_contact.py (1)
newton/_src/sim/builder.py (4)
  • add_body (1179-1241)
  • add_joint_free (1609-1660)
  • add_shape_cylinder (2636-2676)
  • add_shape_mesh (2721-2754)
⏰ 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). (2)
  • GitHub Check: Run GPU Benchmarks (Pull Request)
  • GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
🔇 Additional comments (4)
newton/_src/geometry/kernels.py (2)

1340-1340: Good call excluding cylinder from radius_eff.

Leaving CYLINDER out of radius_eff avoids double-counting its radius since cylinder-plane distance is computed surface-to-surface in the primitive.


1413-1415: All callers unpack the 5-tuple
The sole call at newton/_src/geometry/kernels.py:2163 now unpacks (p_b_world, p_a_world, neg_normal, distance, valid).

newton/tests/test_rigid_contact.py (2)

140-148: Adds cylinder case; expectations look correct.

Axis aligned with world Z, so center settles at half_height. Good coverage addition.


149-157: Mesh body shift and expectations updated appropriately.

Sequence and z-targets consistent with shape half-extents and scale.

@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 (1)
newton/tests/test_rigid_contact.py (1)

140-147: Add a rotated cylinder case to also validate radius-based offset

To catch regressions when the cylinder axis is horizontal (rest height should equal radius), add a second cylinder rotated from Z to Y (similar to the capsule). Example:

         expected_end_positions.append(wp.vec3(5.0, y_pos, 0.3 * scale))
 
+        # Rotated cylinder (axis along Y): rest height equals radius
+        b = builder.add_body(xform=wp.transform(wp.vec3(6.0, y_pos, 1.0), wp.quat_identity()))
+        builder.add_joint_free(b)
+        xform_cyl = wp.transform(wp.vec3(), quat_between_axes(newton.Axis.Z, newton.Axis.Y))
+        builder.add_shape_cylinder(
+            body=b,
+            xform=xform_cyl,
+            radius=0.1 * scale,
+            half_height=0.3 * scale,
+        )
+        expected_end_positions.append(wp.vec3(6.0, y_pos, 0.1 * scale))
📜 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 1bad84b and ca8efef.

📒 Files selected for processing (2)
  • newton/_src/geometry/kernels.py (4 hunks)
  • newton/tests/test_rigid_contact.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
newton/tests/test_rigid_contact.py (1)
newton/_src/sim/builder.py (4)
  • add_body (1179-1241)
  • add_joint_free (1609-1660)
  • add_shape_cylinder (2636-2676)
  • add_shape_mesh (2721-2754)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
🔇 Additional comments (2)
newton/tests/test_rigid_contact.py (2)

140-147: Good addition: cylinder-on-plane case verifies correct half-height contact offset

Adding the vertical cylinder and expecting z = half_height exercises the previously incorrect offset. Looks correct and aligned with the fix for #826.


149-156: Mesh reposition and expectations updated — looks consistent

Moving the mesh to x=7.0 and keeping the expected z at the mesh half-extent (0.3*scale) is consistent with the vertex setup and scaling.

Comment thread newton/_src/geometry/kernels.py Outdated
Comment thread newton/_src/geometry/kernels.py Outdated
@eric-heiden

eric-heiden commented Sep 29, 2025

Copy link
Copy Markdown
Member

@nvtw Can you fix test_rigid_contact? There seem to be some issues potentially introduced here.

@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: 0

🧹 Nitpick comments (4)
newton/tests/test_rigid_contact.py (4)

140-147: Cylinder addition looks correct; consider also testing rotated orientation.

Upright cylinder’s expected z=half_height is correct. To fully cover the cylinder–plane bug, add a rotated cylinder (axis along X or Y) and expect z=radius for that case. This catches orientation-related regressions.


149-156: Mesh shift and expectations updated — add a quick length check.

The move to x=7.0 and expected z=0.3·scale are consistent. Add a guard to ensure the number of expected positions matches the number of dynamic bodies to catch future divergences early.

Apply this minimal check near Line 183:

   body_q = state_0.body_q.numpy()
-  expected_end_positions = np.array(expected_end_positions)
+  expected_end_positions = np.array(expected_end_positions)
+  assert expected_end_positions.shape[0] == model.body_count

44-47: Remove redundant builder setup.

builder is created and configured at Lines 44–47 but immediately replaced at Lines 99–104; the first instance (and add_ground_plane() there) is unused.

Apply:

-    builder = newton.ModelBuilder()
-    builder.default_shape_cfg.ke = 1e4
-    builder.default_shape_cfg.kd = 500.0
-    builder.add_ground_plane()

Also applies to: 99-104


194-194: Drop explicit njmax=150 or document rationale. MuJoCo defaults njmax to nefc when unspecified (and uses the larger of the default or provided value); remove the override for model-agnostic stability or add a brief note explaining why 150 is required here.

📜 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 0bb9041 and ba22011.

📒 Files selected for processing (1)
  • newton/tests/test_rigid_contact.py (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-19T12:27:30.629Z
Learnt from: preist-nvidia
PR: newton-physics/newton#579
File: newton/examples/example_mujoco.py:350-354
Timestamp: 2025-08-19T12:27:30.629Z
Learning: In Newton examples, Example.use_mujoco is a high-level attribute that controls whether to use MuJoCo solver vs other solvers (like XPBD), while SolverMuJoCo.use_mujoco_cpu controls the backend within MuJoCo (CPU vs Warp). These are separate concepts serving different purposes - the PR rename only applies to the solver parameter, not the Example class attributes.

Applied to files:

  • newton/tests/test_rigid_contact.py
🧬 Code graph analysis (1)
newton/tests/test_rigid_contact.py (2)
newton/_src/sim/builder.py (4)
  • add_body (1179-1241)
  • add_joint_free (1609-1660)
  • add_shape_cylinder (2636-2676)
  • add_shape_mesh (2721-2754)
newton/_src/solvers/mujoco/solver_mujoco.py (1)
  • SolverMuJoCo (1136-2844)
⏰ 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)

@nvtw

nvtw commented Sep 29, 2025

Copy link
Copy Markdown
Member Author

Had to increase njmax for mujoco because the test scene now has more objects and generated more contacts. This first looked like a CI hiccup but it's fixed now.

@nvtw nvtw enabled auto-merge (squash) September 29, 2025 08:30
@nvtw nvtw merged commit 09e3910 into newton-physics:main Sep 29, 2025
12 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Oct 16, 2025
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Oct 31, 2025
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Nov 24, 2025
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Jan 12, 2026
4 tasks
eric-heiden added a commit to eric-heiden/newton that referenced this pull request Jan 28, 2026
Co-authored-by: Eric Heiden <eheiden@nvidia.com>
@coderabbitai coderabbitai Bot mentioned this pull request Feb 27, 2026
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Mar 30, 2026
mmacklin pushed a commit to mmacklin/newton that referenced this pull request Apr 7, 2026
Co-authored-by: Eric Heiden <eheiden@nvidia.com>
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.

Cylinder <> Plane collisions generate wrong contact positions

2 participants