bulletphysics / bullet3

Bullet Physics SDK: real-time collision detection and multi-physics simulation for VR, games, visual effects, robotics, machine learning etc.

Home Page:http://bulletphysics.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Invalid collision / contact from performCollisionDetection & getContactPoints after many createMultiBody / removeBody calls

manuel-koch opened this issue · comments

I'm using pybullet to find collisions / contacts of huge scenes with many thousand bodies ( build from convex OBJ meshes ).
I'm not interested in dynamics, I just want to query the "static" scene as-is and find bodies
that are in collision in this static state of the scene.

A scene is build by

  • adding initial static bodies
  • loop
    • adding additional static bodies
    • performing collision detection
    • removing the additional static bodies afterwards

In general this seems to work as expected, but sometimes false collisions / contacts are found between body A & B,
i.e. a collision of actually two distinct bodies A & B that are not even touching.

If I try to double check those false collisions by re-building the scene from scratch incl. bodies A & B
( i.e. only adding those bodies that were in the scene when the collision check was done ),
I can't find those false collisions anymore, i.e. I get the expected results.

My environment is

  • MacOS Ventura 13.4, M1 arm
  • Python 3.10.6
  • pybullet==3.2.5

Pseudo code of my application:

physics_client = bullet.connect(bullet.DIRECT)

# add many "static" bodies
for i in my_static_bodies:
    collision_shape_id = bullet.createCollisionShape(....)
    bullet.createMultiBody(baseCollisionShapeIndex=collision_shape_id,....)
    
while keep_going:
    # add intermediate "static" bodies
    for i in my_additional_static_bodies:
        collision_shape_id = bullet.createCollisionShape(....)
        bullet.createMultiBody(baseCollisionShapeIndex=collision_shape_id,....)

    bullet.performCollisionDetection(physics_client)
    for contact in bullet.getContactPoints(physicsClientId=physics_client):
        # Handle the colliding bodies doing business logic. Not modifying the bodies in any way.
        #
        # Sometimes false collisions are reported, i.e. reporting bodies in collision that are actually distinct, not even touching.
        # Even the contact points are away from any of the two bodies that are supposedly in collision.

    # remove intermediate "static" bodies
    for i in my_additional_static_bodies:
        bullet.removeBody(....)

Any help / idea appreciated - maybe I'm using pybullet in a way that is wrong or not supported ?

Additionally I noticed that contacts are reported for bodies that have been removed.
I even see contacts for the same body getting reported.
Using a simple AABB overlap check shows that the supposedly colliding bodies are distinct.

from typing import Tuple

for contact in bullet.getContactPoints(physicsClientId=physics_client):
    (
        contact_flag,
        body_unique_id_a,
        body_unique_id_b,
        link_index_a,
        link_index_b,
        position_on_a,
        position_on_b,
        contact_normal_on_b,
        contact_distance,
        normal_force,
        lateral_friction_1,
        lateral_friction_dir_1,
        lateral_friction_2,
        lateral_friction_dir_2,
    ) = contact
    if body_unique_id_a == body_unique_id_b:
        continue
    # I keep a list of all body IDs that are in the scene
    if (
        body_unique_id_a not in used_body_ids
        or body_unique_id_b not in used_body_ids
    ):
        # bullet seems to return body IDs we have already removed
        continue

    aabb_a = bullet.getAABB(body_unique_id_a, physicsClientId=physics_client)
    aabb_b = bullet.getAABB(body_unique_id_b, physicsClientId=physics_client)
    if not aabb_overlap(aabb_a, aabb_b, tolerance=0.15):
        continue        

AABB = Tuple[Tuple[float, float, float], Tuple[float, float, float]]

def aabb_overlap(aabb1: AABB, aabb2: AABB, tolerance: float = 0):
    """
    Returns true if aabb1 and aabb2 overlap using given tolerance.
    """
    return (
        aabb1[0][0] - tolerance <= aabb2[1][0] + tolerance
        and aabb1[1][0] + tolerance >= aabb2[0][0] - tolerance
        and aabb1[0][1] - tolerance <= aabb2[1][1] + tolerance
        and aabb1[1][1] + tolerance >= aabb2[0][1] - tolerance
        and aabb1[0][2] - tolerance <= aabb2[1][2] + tolerance
        and aabb1[1][2] - tolerance >= aabb2[0][2] - tolerance
    )