Question about getting multi contact data to simulate robot skin

Hello, everyone. I am currently investigate how to simulate a robot skin module in isaac lab. The robot skin module is consist of multi small contact sensor cell and connected with each other to become a grid or hive shape. Like this,

In this case, each contact cell will have its own force sensor data. According to my understand about isaac sim and isaac lab, I think there are following possible way to simulate it properly:

RigidContactView

Create a omni.isaac.core.prims.RigidBody for every cell, connected them with UsdPhysics.FixedJoint, and use a omni.isaac.core.prims.RigidContactView to query all cell in all environment at the same time. This is the same idea as ContactSensor from omni.isaac.lab.

However, this method doesn’t work because when the amount is getting higher, it gives this error message.

2024-08-22 12:25:15 [25,841ms] [Error] [omni.physx.plugin] PhysX error: PhysX Internal CUDA error. Simulation cannot continue! Error code 700!
, FILE /builds/omniverse/physics/physx/source/physx/src/NpScene.cpp, LINE 3059
2024-08-22 12:25:15 [25,841ms] [Error] [omni.physx.plugin] Cuda context manager error, simulation will be stopped and new cuda context manager will be created.
2024-08-22 12:25:15 [25,841ms] [Error] [omni.physx.tensors.plugin] CUDA error: an illegal memory access was encountered: ../../../extensions/runtime/source/omni.physx.tensors/plugins/gpu/GpuSimulationData.cpp: 152
2024-08-22 12:25:15 [25,841ms] [Error] [omni.physx.tensors.plugin] CUDA context validation failed
2024-08-22 12:25:15 [25,841ms] [Error] [omni.physx.tensors.plugin] CUDA error: an illegal memory access was encountered: ../../../extensions/runtime/source/omni.physx.tensors/plugins/gpu/GpuSimulationData.cpp: 199
2024-08-22 12:25:15 [25,841ms] [Error] [omni.physx.tensors.plugin] Failed to initialize GPU simulation data
Traceback (most recent call last):
  File "/home/yi/Program/IsaacLab/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/asset_base.py", line 92, in <lambda>
    lambda event, obj=weakref.proxy(self): obj._initialize_callback(event),
  File "/home/yi/Program/IsaacLab/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/asset_base.py", line 263, in _initialize_callback
    self._initialize_impl()
  File "/home/yi/Program/IsaacLab/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/articulation/articulation.py", line 946, in _initialize_impl
    self._physics_sim_view = physx.create_simulation_view(self._backend)
  File "/home/yi/.local/share/ov/pkg/isaac-sim-4.1.0/extsPhysics/omni.physics.tensors/omni/physics/tensors/impl/api.py", line 12, in create_simulation_view
    raise Exception("Failed to create simulation view backend")
Exception: Failed to create simulation view backend
Traceback (most recent call last):
  File "/home/yi/Program/IsaacLab/source/extensions/omni.isaac.lab/omni/isaac/lab/sensors/sensor_base.py", line 64, in <lambda>
    lambda event, obj=weakref.proxy(self): obj._initialize_callback(event),
  File "/home/yi/Program/IsaacLab/source/extensions/omni.isaac.lab/omni/isaac/lab/sensors/sensor_base.py", line 271, in _initialize_callback
    self._initialize_impl()
  File "/home/yi/Program/isaac_skin_simulation/exts/isaac_skin/isaac_skin/sensors/skin/skin.py", line 191, in _initialize_impl
    super()._initialize_impl()
  File "/home/yi/Program/IsaacLab/source/extensions/omni.isaac.lab/omni/isaac/lab/sensors/sensor_base.py", line 225, in _initialize_impl
    self._is_outdated = torch.ones(self._num_envs, dtype=torch.bool, device=self._device)
RuntimeError: CUDA error: an illegal memory access was encountered
CUDA kernel errors might be asynchronously reported at some other API call, so the stacktrace below might be incorrect.
For debugging consider passing CUDA_LAUNCH_BLOCKING=1.
Compile with `TORCH_USE_CUDA_DSA` to enable device-side assertions.
2024-08-22 12:25:21 [31,781ms] [Error] [carb.graphics-vulkan.plugin] VkResult: ERROR_INITIALIZATION_FAILED
2024-08-22 12:25:21 [31,781ms] [Error] [carb.graphics-vulkan.plugin] vkCreateRayTracingPipelinesKHR failed.
2024-08-22 12:25:21 [31,781ms] [Error] [carb.graphics-vulkan.plugin] No Collection/Library can be nullptr with eCollection as compileType.
2024-08-22 12:25:21 [31,781ms] [Error] [gpu.foundation.plugin] Failed to create the raytracing pipeline.

I think it could be the issue of creating too many rigid body in an inefficient way.

Callback function

Instead of creat a RigidBody for each cell, we can also create only one RigidBody for the whole group and consider each cell as an individual Collider under the overall rigid body.

Use a customed callback function, which can get the detailed collider information like the following code. This code comes from the example in ContactReportDemo and ContactReportDirectAPIDemo from omni.physx.demos

    def update(self, stage, dt, viewport, physxIFace):
        if not self._contact_report_sub:
            if self.use_full_report:
                self._contact_report_sub = get_physx_simulation_interface().subscribe_full_contact_report_events(self._on_full_contact_report_event)
            else:
                self._contact_report_sub = get_physx_simulation_interface().subscribe_contact_report_events(self._on_contact_report_event)
        
    def _on_contact_report_event(self, contact_headers, contact_data):
        self.print_contacts(contact_headers, contact_data, None)
                        
    def _on_full_contact_report_event(self, contact_headers, contact_data, friction_anchors):
        self.print_contacts(contact_headers, contact_data, friction_anchors)

    def print_contacts(self, contact_headers, contact_data, friction_anchors):
        for contact_header in contact_headers:
            print("Got contact header type: " + str(contact_header.type))
            print("Actor0: " + str(PhysicsSchemaTools.intToSdfPath(contact_header.actor0)))
            print("Actor1: " + str(PhysicsSchemaTools.intToSdfPath(contact_header.actor1)))
            print("Collider0: " + str(PhysicsSchemaTools.intToSdfPath(contact_header.collider0)))
            print("Collider1: " + str(PhysicsSchemaTools.intToSdfPath(contact_header.collider1)))
            print("StageId: " + str(contact_header.stage_id))
            print("Number of contacts: " + str(contact_header.num_contact_data))
            
            contact_data_offset = contact_header.contact_data_offset
            num_contact_data = contact_header.num_contact_data
            
            for index in range(contact_data_offset, contact_data_offset + num_contact_data, 1):
                print("Contact:")
                print("Contact position: " + str(contact_data[index].position))
                print("Contact normal: " + str(contact_data[index].normal))
                print("Contact impulse: " + str(contact_data[index].impulse))
                print("Contact separation: " + str(contact_data[index].separation))
                print("Contact faceIndex0: " + str(contact_data[index].face_index0))
                print("Contact faceIndex1: " + str(contact_data[index].face_index1))
                print("Contact material0: " + str(PhysicsSchemaTools.intToSdfPath(contact_data[index].material0)))
                print("Contact material1: " + str(PhysicsSchemaTools.intToSdfPath(contact_data[index].material1)))

            if friction_anchors is not None:
                friction_anchors_data_offset = contact_header.friction_anchors_offset
                num_friction_anchor_data = contact_header.num_friction_anchors_data

                for index in range(friction_anchors_data_offset, friction_anchors_data_offset + num_friction_anchor_data, 1):
                    print("Friction anchor:")
                    print("Friction position: " + str(friction_anchors[index].position))
                    print("Friction impulse: " + str(friction_anchors[index].impulse))

However, this way doesn’t make much sense since it should query the information one by one, which means if you have 10 env and each env has 100 cell, you need to run this call function 1000 times. What’s more, I am not sure if this call function runs on cpu or gpu, and will it effect the preformance of RL or not.

Calculate the cell assignment manually

Since RigidContactView can also report contact position, it will be possible to use the contact position to determine which cell is collided. However, this could increase the computation complexity.

Question

My question is, is there a better way to do this or can we use any of the above idea but do it efficiently. Btw, is it possible that the get_contact_force_data function in RigidContactView return collider information as well?
If anyone could give same advice, I will be super grateful. Thanks in advance!