接触传感器#
接触传感器的设计目的是返回作用于给定刚体的净接触力。该传感器被编写为表现得像一个物理对象,因此接触传感器的 “范围” 仅限于定义它的物体(或物体)。根据您过滤来自接触的力的需求,有多种方法可以定义此范围。
默认情况下,报告的力是总接触力,但您的应用程序可能只关心特定物体产生的接触力。从特定物体中检索接触力需要过滤,并且这只能以 “多对一” 的方式完成。一个需要可过滤接触信息的多足机器人,必须在环境中为每个足部定义一个传感器,而一个在每个手指尖端都有接触传感器的机器人手,则可以通过一个传感器来定义。
考虑一个简单的环境,其中包含一个 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()