接触传感器

接触传感器#

带有过滤功能的接触传感器

接触传感器的设计目的是返回作用于给定刚体的净接触力。该传感器被编写为表现得像一个物理对象,因此接触传感器的 “范围” 仅限于定义它的物体(或物体)。根据您过滤来自接触的力的需求,有多种方法可以定义此范围。

默认情况下,报告的力是总接触力,但您的应用程序可能只关心特定物体产生的接触力。从特定物体中检索接触力需要过滤,并且这只能以 “多对一” 的方式完成。一个需要可过滤接触信息的多足机器人,必须在环境中为每个足部定义一个传感器,而一个在每个手指尖端都有接触传感器的机器人手,则可以通过一个传感器来定义。

考虑一个简单的环境,其中包含一个 Anymal Quadruped 和一个 block

@configclass
class ContactSensorsSceneCfg(InteractiveSceneCfg):
    """Design the scene with sensors on the robot."""

    # ground plane
    ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())

    # lights
    dome_light = AssetBaseCfg(
        prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
    )

    # robot
    robot = ANYMAL_C_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")

    # Rigid Object
    cube = RigidObjectCfg(
        prim_path="{ENV_REGEX_NS}/Cube",
        spawn=sim_utils.CuboidCfg(
            size=(0.5,0.5,0.1),
            rigid_props=sim_utils.RigidBodyPropertiesCfg(),
            mass_props=sim_utils.MassPropertiesCfg(mass=100.0),
            collision_props=sim_utils.CollisionPropertiesCfg(),
            physics_material=sim_utils.RigidBodyMaterialCfg(static_friction=1.0),
            visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2),
        ),
        init_state=RigidObjectCfg.InitialStateCfg(pos=(0.5, 0.5, 0.05)),
    )

    contact_forces_LF = ContactSensorCfg(
        prim_path="{ENV_REGEX_NS}/Robot/LF_FOOT",
        update_period=0.0,
        history_length=6,
        debug_vis=True,
        filter_prim_paths_expr=["{ENV_REGEX_NS}/Cube"],
    )

    contact_forces_RF = ContactSensorCfg(
        prim_path="{ENV_REGEX_NS}/Robot/RF_FOOT",
        update_period=0.0,
        history_length=6,
        debug_vis=True,
        filter_prim_paths_expr=["{ENV_REGEX_NS}/Cube"],
    )

    contact_forces_H = ContactSensorCfg(
        prim_path="{ENV_REGEX_NS}/Robot/.*H_FOOT",
        update_period=0.0,
        history_length=6,
        debug_vis=True,
    )

我们以两种不同的方式定义机器人脚上的传感器。前脚是独立的传感器(每只脚一个传感器体),并且 “Cube” 被放置在左脚下方。后脚定义为一个传感器,包含多个传感器体。

我们可以然后运行场景并打印来自传感器的数据

def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
  .
  .
  .
  # Simulate physics
  while simulation_app.is_running():
    .
    .
    .
    # print information from the sensors
    print("-------------------------------")
    print(scene["contact_forces_LF"])
    print("Received force matrix of: ", scene["contact_forces_LF"].data.force_matrix_w)
    print("Received contact force of: ", scene["contact_forces_LF"].data.net_forces_w)
    print("-------------------------------")
    print(scene["contact_forces_RF"])
    print("Received force matrix of: ", scene["contact_forces_RF"].data.force_matrix_w)
    print("Received contact force of: ", scene["contact_forces_RF"].data.net_forces_w)
    print("-------------------------------")
    print(scene["contact_forces_H"])
    print("Received force matrix of: ", scene["contact_forces_H"].data.force_matrix_w)
    print("Received contact force of: ", scene["contact_forces_H"].data.net_forces_w)

在这里,我们打印出场景中每个接触传感器的净接触力和过滤后的力矩阵。前左脚和前右脚报告以下内容

-------------------------------
Contact sensor @ '/World/envs/env_.*/Robot/LF_FOOT':
        view type         : <class 'omni.physics.tensors.impl.api.RigidBodyView'>
        update period (s) : 0.0
        number of bodies  : 1
        body names        : ['LF_FOOT']

Received force matrix of:  tensor([[[[-1.3923e-05,  1.5727e-04,  1.1032e+02]]]], device='cuda:0')
Received contact force of:  tensor([[[-1.3923e-05,  1.5727e-04,  1.1032e+02]]], device='cuda:0')
-------------------------------
Contact sensor @ '/World/envs/env_.*/Robot/RF_FOOT':
        view type         : <class 'omni.physics.tensors.impl.api.RigidBodyView'>
        update period (s) : 0.0
        number of bodies  : 1
        body names        : ['RF_FOOT']

Received force matrix of:  tensor([[[[0., 0., 0.]]]], device='cuda:0')
Received contact force of:  tensor([[[1.3529e-05, 0.0000e+00, 1.0069e+02]]], device='cuda:0')

注意,即使进行过滤,两个传感器仍然报告作用在脚上的净接触力。然而,只有左脚具有非零的 “力矩阵” ,因为右脚没有站在被过滤的物体上,/World/envs/env_.*/Cube 。现在,查看来自后脚的数据!

-------------------------------
Contact sensor @ '/World/envs/env_.*/Robot/.*H_FOOT':
        view type         : <class 'omni.physics.tensors.impl.api.RigidBodyView'>
        update period (s) : 0.0
        number of bodies  : 2
        body names        : ['LH_FOOT', 'RH_FOOT']

Received force matrix of:  None
Received contact force of:  tensor([[[9.7227e-06, 0.0000e+00, 7.2364e+01],
        [2.4322e-05, 0.0000e+00, 1.8102e+02]]], device='cuda:0')

在这种情况下,接触传感器有两个主体:左侧和右侧后脚。当查询力矩阵时,结果是 None,因为这是一个多主体传感器,而目前 Isaac Lab 仅支持 “多对一” 接触力过滤。与单主体接触传感器不同,报告的力张量具有多个条目,每一 “行” 对应于传感器单一主体上的接触力(与构造时的顺序匹配)。

contact_sensor.py 的代码
  1# Copyright (c) 2022-2024, The Isaac Lab Project Developers.
  2# All rights reserved.
  3#
  4# SPDX-License-Identifier: BSD-3-Clause
  5
  6"""Launch Isaac Sim Simulator first."""
  7
  8import argparse
  9
 10from omni.isaac.lab.app import AppLauncher
 11
 12# add argparse arguments
 13parser = argparse.ArgumentParser(description="Tutorial on adding sensors on a robot.")
 14parser.add_argument("--num_envs", type=int, default=1, help="Number of environments to spawn.")
 15# append AppLauncher cli args
 16AppLauncher.add_app_launcher_args(parser)
 17# parse the arguments
 18args_cli = parser.parse_args()
 19
 20# launch omniverse app
 21app_launcher = AppLauncher(args_cli)
 22simulation_app = app_launcher.app
 23
 24"""Rest everything follows."""
 25
 26import torch
 27
 28import omni.isaac.lab.sim as sim_utils
 29from omni.isaac.lab.assets import AssetBaseCfg, RigidObjectCfg
 30from omni.isaac.lab.scene import InteractiveScene, InteractiveSceneCfg
 31from omni.isaac.lab.sensors import ContactSensorCfg
 32from omni.isaac.lab.utils import configclass
 33
 34##
 35# Pre-defined configs
 36##
 37from omni.isaac.lab_assets.anymal import ANYMAL_C_CFG  # isort: skip
 38
 39
 40@configclass
 41class ContactSensorSceneCfg(InteractiveSceneCfg):
 42    """Design the scene with sensors on the robot."""
 43
 44    # ground plane
 45    ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())
 46
 47    # lights
 48    dome_light = AssetBaseCfg(
 49        prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
 50    )
 51
 52    # robot
 53    robot = ANYMAL_C_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
 54
 55    # Rigid Object
 56    cube = RigidObjectCfg(
 57        prim_path="{ENV_REGEX_NS}/Cube",
 58        spawn=sim_utils.CuboidCfg(
 59            size=(0.5, 0.5, 0.1),
 60            rigid_props=sim_utils.RigidBodyPropertiesCfg(),
 61            mass_props=sim_utils.MassPropertiesCfg(mass=100.0),
 62            collision_props=sim_utils.CollisionPropertiesCfg(),
 63            physics_material=sim_utils.RigidBodyMaterialCfg(static_friction=1.0),
 64            visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2),
 65        ),
 66        init_state=RigidObjectCfg.InitialStateCfg(pos=(0.5, 0.5, 0.05)),
 67    )
 68
 69    contact_forces_LF = ContactSensorCfg(
 70        prim_path="{ENV_REGEX_NS}/Robot/LF_FOOT",
 71        update_period=0.0,
 72        history_length=6,
 73        debug_vis=True,
 74        filter_prim_paths_expr=["{ENV_REGEX_NS}/Cube"],
 75    )
 76
 77    contact_forces_RF = ContactSensorCfg(
 78        prim_path="{ENV_REGEX_NS}/Robot/RF_FOOT",
 79        update_period=0.0,
 80        history_length=6,
 81        debug_vis=True,
 82        filter_prim_paths_expr=["{ENV_REGEX_NS}/Cube"],
 83    )
 84
 85    contact_forces_H = ContactSensorCfg(
 86        prim_path="{ENV_REGEX_NS}/Robot/.*H_FOOT",
 87        update_period=0.0,
 88        history_length=6,
 89        debug_vis=True,
 90    )
 91
 92
 93def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
 94    """Run the simulator."""
 95    # Define simulation stepping
 96    sim_dt = sim.get_physics_dt()
 97    sim_time = 0.0
 98    count = 0
 99
100    # Simulate physics
101    while simulation_app.is_running():
102
103        if count % 500 == 0:
104            # reset counter
105            count = 0
106            # reset the scene entities
107            # root state
108            # we offset the root state by the origin since the states are written in simulation world frame
109            # if this is not done, then the robots will be spawned at the (0, 0, 0) of the simulation world
110            root_state = scene["robot"].data.default_root_state.clone()
111            root_state[:, :3] += scene.env_origins
112            scene["robot"].write_root_state_to_sim(root_state)
113            # set joint positions with some noise
114            joint_pos, joint_vel = (
115                scene["robot"].data.default_joint_pos.clone(),
116                scene["robot"].data.default_joint_vel.clone(),
117            )
118            joint_pos += torch.rand_like(joint_pos) * 0.1
119            scene["robot"].write_joint_state_to_sim(joint_pos, joint_vel)
120            # clear internal buffers
121            scene.reset()
122            print("[INFO]: Resetting robot state...")
123        # Apply default actions to the robot
124        # -- generate actions/commands
125        targets = scene["robot"].data.default_joint_pos
126        # -- apply action to the robot
127        scene["robot"].set_joint_position_target(targets)
128        # -- write data to sim
129        scene.write_data_to_sim()
130        # perform step
131        sim.step()
132        # update sim-time
133        sim_time += sim_dt
134        count += 1
135        # update buffers
136        scene.update(sim_dt)
137
138        # print information from the sensors
139        print("-------------------------------")
140        print(scene["contact_forces_LF"])
141        print("Received force matrix of: ", scene["contact_forces_LF"].data.force_matrix_w)
142        print("Received contact force of: ", scene["contact_forces_LF"].data.net_forces_w)
143        print("-------------------------------")
144        print(scene["contact_forces_RF"])
145        print("Received force matrix of: ", scene["contact_forces_RF"].data.force_matrix_w)
146        print("Received contact force of: ", scene["contact_forces_RF"].data.net_forces_w)
147        print("-------------------------------")
148        print(scene["contact_forces_H"])
149        print("Received force matrix of: ", scene["contact_forces_H"].data.force_matrix_w)
150        print("Received contact force of: ", scene["contact_forces_H"].data.net_forces_w)
151
152
153def main():
154    """Main function."""
155
156    # Initialize the simulation context
157    sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device)
158    sim = sim_utils.SimulationContext(sim_cfg)
159    # Set main camera
160    sim.set_camera_view(eye=[3.5, 3.5, 3.5], target=[0.0, 0.0, 0.0])
161    # design scene
162    scene_cfg = ContactSensorSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0)
163    scene = InteractiveScene(scene_cfg)
164    # Play the simulator
165    sim.reset()
166    # Now we are ready!
167    print("[INFO]: Setup complete...")
168    # Run the simulator
169    run_simulator(sim, scene)
170
171
172if __name__ == "__main__":
173    # run the main function
174    main()
175    # close sim app
176    simulation_app.close()