26
26
27
27
import omni .isaac .lab .sim as sim_utils
28
28
import omni .isaac .lab .utils .math as math_utils
29
+ from omni .isaac .lab .assets import RigidObjectCfg
29
30
from omni .isaac .lab .scene import InteractiveScene , InteractiveSceneCfg
30
31
from omni .isaac .lab .sensors import FrameTransformerCfg , OffsetCfg
31
32
from omni .isaac .lab .terrains import TerrainImporterCfg
@@ -62,6 +63,19 @@ class MySceneCfg(InteractiveSceneCfg):
62
63
# sensors - frame transformer (filled inside unit test)
63
64
frame_transformer : FrameTransformerCfg = None
64
65
66
+ # block
67
+ cube : RigidObjectCfg = RigidObjectCfg (
68
+ prim_path = "{ENV_REGEX_NS}/cube" ,
69
+ spawn = sim_utils .CuboidCfg (
70
+ size = (0.2 , 0.2 , 0.2 ),
71
+ rigid_props = sim_utils .RigidBodyPropertiesCfg (max_depenetration_velocity = 1.0 ),
72
+ mass_props = sim_utils .MassPropertiesCfg (mass = 1.0 ),
73
+ physics_material = sim_utils .RigidBodyMaterialCfg (),
74
+ visual_material = sim_utils .PreviewSurfaceCfg (diffuse_color = (0.5 , 0.0 , 0.0 )),
75
+ ),
76
+ init_state = RigidObjectCfg .InitialStateCfg (pos = (2.0 , 0.0 , 5 )),
77
+ )
78
+
65
79
66
80
class TestFrameTransformer (unittest .TestCase ):
67
81
"""Test for frame transformer sensor."""
@@ -71,7 +85,7 @@ def setUp(self):
71
85
# Create a new stage
72
86
stage_utils .create_new_stage ()
73
87
# Load kit helper
74
- self .sim = sim_utils .SimulationContext (sim_utils .SimulationCfg (dt = 0.005 ))
88
+ self .sim = sim_utils .SimulationContext (sim_utils .SimulationCfg (dt = 0.005 , device = "cpu" ))
75
89
# Set main camera
76
90
self .sim .set_camera_view (eye = [5 , 5 , 5 ], target = [0.0 , 0.0 , 0.0 ])
77
91
@@ -90,8 +104,7 @@ def tearDown(self):
90
104
def test_frame_transformer_feet_wrt_base (self ):
91
105
"""Test feet transformations w.r.t. base source frame.
92
106
93
- In this test, the source frame is the robot base. This frame is at index 0, when
94
- the frame bodies are sorted in the order of the regex matching in the frame transformer.
107
+ In this test, the source frame is the robot base.
95
108
"""
96
109
# Spawn things into stage
97
110
scene_cfg = MySceneCfg (num_envs = 32 , env_spacing = 5.0 , lazy_sensor_update = False )
@@ -141,9 +154,15 @@ def test_frame_transformer_feet_wrt_base(self):
141
154
feet_indices , feet_names = scene .articulations ["robot" ].find_bodies (
142
155
["LF_FOOT" , "RF_FOOT" , "LH_FOOT" , "RH_FOOT" ]
143
156
)
144
- # Check names are parsed the same order
145
- user_feet_names = [f"{ name } _USER" for name in feet_names ]
146
- self .assertListEqual (scene .sensors ["frame_transformer" ].data .target_frame_names , user_feet_names )
157
+
158
+ target_frame_names = scene .sensors ["frame_transformer" ].data .target_frame_names
159
+
160
+ # Reorder the feet indices to match the order of the target frames with _USER suffix removed
161
+ target_frame_names = [name .split ("_USER" )[0 ] for name in target_frame_names ]
162
+
163
+ # Find the indices of the feet in the order of the target frames
164
+ reordering_indices = [feet_names .index (name ) for name in target_frame_names ]
165
+ feet_indices = [feet_indices [i ] for i in reordering_indices ]
147
166
148
167
# default joint targets
149
168
default_actions = scene .articulations ["robot" ].data .default_joint_pos .clone ()
@@ -185,6 +204,7 @@ def test_frame_transformer_feet_wrt_base(self):
185
204
source_quat_w_tf = scene .sensors ["frame_transformer" ].data .source_quat_w
186
205
feet_pos_w_tf = scene .sensors ["frame_transformer" ].data .target_pos_w
187
206
feet_quat_w_tf = scene .sensors ["frame_transformer" ].data .target_quat_w
207
+
188
208
# check if they are same
189
209
torch .testing .assert_close (root_pose_w [:, :3 ], source_pos_w_tf , rtol = 1e-3 , atol = 1e-3 )
190
210
torch .testing .assert_close (root_pose_w [:, 3 :], source_quat_w_tf , rtol = 1e-3 , atol = 1e-3 )
@@ -302,6 +322,87 @@ def test_frame_transformer_feet_wrt_thigh(self):
302
322
torch .testing .assert_close (feet_pos_source_tf [:, index ], foot_pos_b , rtol = 1e-3 , atol = 1e-3 )
303
323
torch .testing .assert_close (feet_quat_source_tf [:, index ], foot_quat_b , rtol = 1e-3 , atol = 1e-3 )
304
324
325
+ def test_frame_transformer_body_wrt_cube (self ):
326
+ """Test body transformation w.r.t. base source frame.
327
+
328
+ In this test, the source frame is the robot base.
329
+
330
+ The target_frame is a cube in the scene.
331
+ """
332
+ # Spawn things into stage
333
+ scene_cfg = MySceneCfg (num_envs = 2 , env_spacing = 5.0 , lazy_sensor_update = False )
334
+ scene_cfg .frame_transformer = FrameTransformerCfg (
335
+ prim_path = "{ENV_REGEX_NS}/Robot/base" ,
336
+ target_frames = [
337
+ FrameTransformerCfg .FrameCfg (
338
+ name = "CUBE_USER" ,
339
+ prim_path = "{ENV_REGEX_NS}/cube" ,
340
+ ),
341
+ ],
342
+ )
343
+ scene = InteractiveScene (scene_cfg )
344
+
345
+ # Play the simulator
346
+ self .sim .reset ()
347
+
348
+ # default joint targets
349
+ default_actions = scene .articulations ["robot" ].data .default_joint_pos .clone ()
350
+ # Define simulation stepping
351
+ sim_dt = self .sim .get_physics_dt ()
352
+ # Simulate physics
353
+ for count in range (100 ):
354
+ # # reset
355
+ if count % 25 == 0 :
356
+ # reset root state
357
+ root_state = scene .articulations ["robot" ].data .default_root_state .clone ()
358
+ root_state [:, :3 ] += scene .env_origins
359
+ joint_pos = scene .articulations ["robot" ].data .default_joint_pos
360
+ joint_vel = scene .articulations ["robot" ].data .default_joint_vel
361
+ # -- set root state
362
+ # -- robot
363
+ scene .articulations ["robot" ].write_root_state_to_sim (root_state )
364
+ scene .articulations ["robot" ].write_joint_state_to_sim (joint_pos , joint_vel )
365
+ # reset buffers
366
+ scene .reset ()
367
+
368
+ # set joint targets
369
+ robot_actions = default_actions + 0.5 * torch .randn_like (default_actions )
370
+ scene .articulations ["robot" ].set_joint_position_target (robot_actions )
371
+ # write data to sim
372
+ scene .write_data_to_sim ()
373
+ # perform step
374
+ self .sim .step ()
375
+ # read data from sim
376
+ scene .update (sim_dt )
377
+
378
+ # check absolute frame transforms in world frame
379
+ # -- ground-truth
380
+ root_pose_w = scene .articulations ["robot" ].data .root_state_w [:, :7 ]
381
+ cube_pos_w_gt = scene .rigid_objects ["cube" ].data .root_state_w [:, :3 ]
382
+ cube_quat_w_gt = scene .rigid_objects ["cube" ].data .root_state_w [:, 3 :7 ]
383
+ # -- frame transformer
384
+ source_pos_w_tf = scene .sensors ["frame_transformer" ].data .source_pos_w
385
+ source_quat_w_tf = scene .sensors ["frame_transformer" ].data .source_quat_w
386
+ cube_pos_w_tf = scene .sensors ["frame_transformer" ].data .target_pos_w .squeeze ()
387
+ cube_quat_w_tf = scene .sensors ["frame_transformer" ].data .target_quat_w .squeeze ()
388
+
389
+ # check if they are same
390
+ torch .testing .assert_close (root_pose_w [:, :3 ], source_pos_w_tf , rtol = 1e-3 , atol = 1e-3 )
391
+ torch .testing .assert_close (root_pose_w [:, 3 :], source_quat_w_tf , rtol = 1e-3 , atol = 1e-3 )
392
+ torch .testing .assert_close (cube_pos_w_gt , cube_pos_w_tf , rtol = 1e-3 , atol = 1e-3 )
393
+ torch .testing .assert_close (cube_quat_w_gt , cube_quat_w_tf , rtol = 1e-3 , atol = 1e-3 )
394
+
395
+ # check if relative transforms are same
396
+ cube_pos_source_tf = scene .sensors ["frame_transformer" ].data .target_pos_source
397
+ cube_quat_source_tf = scene .sensors ["frame_transformer" ].data .target_quat_source
398
+ # ground-truth
399
+ cube_pos_b , cube_quat_b = math_utils .subtract_frame_transforms (
400
+ root_pose_w [:, :3 ], root_pose_w [:, 3 :], cube_pos_w_tf , cube_quat_w_tf
401
+ )
402
+ # check if they are same
403
+ torch .testing .assert_close (cube_pos_source_tf [:, 0 ], cube_pos_b , rtol = 1e-3 , atol = 1e-3 )
404
+ torch .testing .assert_close (cube_quat_source_tf [:, 0 ], cube_quat_b , rtol = 1e-3 , atol = 1e-3 )
405
+
305
406
306
407
if __name__ == "__main__" :
307
408
run_tests ()
0 commit comments