IsaacLab icon indicating copy to clipboard operation
IsaacLab copied to clipboard

overlap_mesh ignores updated joint positions in IsaacLab

Open RodsCoimbra opened this issue 9 months ago • 2 comments

I'm using overlap_mesh with an articulation to detect collisions, however, it's behaving incorrectly.

When running the same setup in Isaac Sim’s Script Editor, the collision detection reflects the robot's current joint positions. However, in IsaacLab, using a simple implementation adapted from the rigid object tutorial, the function appears to ignore the updated joint configurations and always uses the default pose defined in the USD (all joints at 0).

For example, if a cube overlaps the arm only in the default configuration, a collision is reported even when the robot is spawned with the arm away. This suggests that IsaacLab does not update the articulation's state for overlap_mesh. Is there a step I’m missing to force the collision check to use the current joint positions, or is this a known limitation?

Default configuration:

Image

Arm in a different configuration:

Image

Output (Equal to both):

Image

Doing the opposite—placing the cube so it overlaps only in the updated configuration—produces no collision detection in either case.

Config:

TIAGO_COLLISION_CFG = ArticulationCfg(
    spawn=sim_utils.UsdFileCfg(
        usd_path=USD_PATH,
        ),
        init_state=ArticulationCfg.InitialStateCfg(
        pos=(0.0, 0.0, 0.0),
        joint_pos={
            "arm_1_joint": 0.5689773361501514, 
            "arm_2_joint": -0.9791297103688189,
            "arm_3_joint": -1.6266468628587152,
            "arm_4_joint": 1.9879300180215413,
            "arm_5_joint": -0.0715584993317675,
            "arm_6_joint": 0.19722220547535926,
            "arm_7_joint": -0.04363323129985824,
        },
    ),
    actuators={...}
)

code:

counter = 0
def report_hit(collision):
    global counter
    counter+=1
    print("Collision with: ", collision.rigid_body)
    return True #False stops search

def check_overlapping(path):
    global counter
    counter = 0
    path_tuple = PhysicsSchemaTools.encodeSdfPath(path)
    _ = get_physx_scene_query_interface().overlap_mesh(path_tuple[0], path_tuple[1],report_hit, False)
    if counter > 0:
        print(path)

def design_scene():
    """Designs the scene."""
    cfg = sim_utils.GroundPlaneCfg()
    cfg.func("/World/defaultGroundPlane", cfg)
    stage = omni.usd.get_context().get_stage()  
    omni.kit.commands.execute('AddPhysicsSceneCommand',stage = stage, path='/World/PhysicsScene')
    prim_utils.create_prim(f"/World/Origin0", "Xform")
    cube_cfg = sim_utils.CuboidCfg(
        size=(0.3043, 0.3941, 0.18363),
        collision_props=sim_utils.CollisionPropertiesCfg(),
        visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2),
    )

    tiago_collision_cfg = TIAGO_COLLISION_CFG.replace(prim_path="/World/Origin0/Tiago")
    tiago = Articulation(cfg=tiago_collision_cfg)
    cube_object = cube_cfg.func("/World/Origin0/cube", cube_cfg, translation=(0.1,-0.5, 0.8))
    scene_entities = {"tiago": tiago}
    return scene_entities

def run_simulator(sim: sim_utils.SimulationContext, entities):
    """Runs the simulation loop."""
    tiago = entities["tiago"]
    sim_dt = sim.get_physics_dt()
    sim_time = 0.0
    count = 0
    stage = omni.usd.get_context().get_stage()
    paths = [res.string for prim in stage_utils.traverse_stage() if (res:=re.search(".*collisions/mesh.*", prim_utils.get_prim_path(prim)))]
    while simulation_app.is_running():
        if count % 250 == 0:
            sim_time = 0.0
            count = 0
            #Position
            root_state_tiago = tiago.data.default_root_state.clone()
            tiago.write_root_pose_to_sim(root_state_tiago[:, :7])
            tiago.write_root_velocity_to_sim(root_state_tiago[:, 7:])
            #joints
            joint_pos, joint_vel = tiago.data.default_joint_pos.clone(), tiago.data.default_joint_vel.clone()
            tiago.write_joint_state_to_sim(joint_pos, joint_vel)
            tiago.set_joint_position_target(joint_pos)
            tiago.reset()
            print("----------------------------------------")
            print("[INFO]: Resetting object state...")

        tiago.write_data_to_sim()
        sim.step()
        if count % 100 == 0:
            for path in paths:
                check_overlapping(path)

        sim_time += sim_dt
        count += 1
        # update buffers
        tiago.update(sim_dt)

RodsCoimbra avatar Apr 10 '25 16:04 RodsCoimbra

Thank you for posting this. Could you clarify if the images you posted are before stepping the simulation or after how many steps? Also, please share the versions of Isaac Sim and Isaac Lab you are using. Thanks.

RandomOakForest avatar Apr 11 '25 10:04 RandomOakForest

Hi, Thanks for the quick reply. I'm using Isaac Sim 4.5.0 and Isaac Lab 2.0.2. Both images were taken after stepping the simulation. In my setup, collisions are checked every 100 simulation steps using the code below:

sim.step()
if count % 100 == 0:
    for path in paths:
        check_overlapping(path)

sim_time += sim_dt
count += 1

To clarify, the images I posted are from two separate runs of the code shown in the initial post, not from the same simulation instance. The only difference between the two runs is the joint_pos field defined in the robot's config (ArticulationCfg.InitialStateCfg). In one run, all joint positions were set to 0, which corresponds to the image where the arm is extended. This is also the default configuration when I spawn the USD directly in Isaac Sim. In the other run, the robot config had the following joint positions:

joint_pos={
"arm_1_joint": 0.5689773361501514, 
"arm_2_joint": -0.9791297103688189,
"arm_3_joint": -1.6266468628587152,
"arm_4_joint": 1.9879300180215413,
"arm_5_joint": -0.0715584993317675,
"arm_6_joint": 0.19722220547535926,
"arm_7_joint": -0.04363323129985824,
} 

I didn’t move the arm dynamically during the simulation. Instead, I set the pose during reset using:

root_state_tiago = tiago.data.default_root_state.clone()
tiago.write_root_pose_to_sim(root_state_tiago[:, :7])
tiago.write_root_velocity_to_sim(root_state_tiago[:, 7:])
#joints
joint_pos, joint_vel = tiago.data.default_joint_pos.clone(), tiago.data.default_joint_vel.clone()
tiago.write_joint_state_to_sim(joint_pos, joint_vel)
tiago.set_joint_position_target(joint_pos)

All of the above code snippets are included in the code of the original post, should you need further context.

RodsCoimbra avatar Apr 11 '25 12:04 RodsCoimbra

Following up on this, it seems some shapes cannot be checked with overlap_mesh. I'm tagging this as a bug report for the team to follow up. A workaround is to use a shape that has mesh you can visualize.

RandomOakForest avatar May 20 '25 12:05 RandomOakForest

A workaround is to use a shape that has mesh you can visualize.

What do you mean by a mesh I can visualize? Both the cube and the arm meshes are visible.

RodsCoimbra avatar May 20 '25 20:05 RodsCoimbra