4040 BOARD_TOP_Z ,
4141 BOARD_USD_PATH ,
4242 CABLE_BEND_MODULUS ,
43+ CABLE_CONTACT_FRICTION ,
4344 CABLE_LENGTH ,
4445 CABLE_NUM_SEGMENTS ,
4546 CABLE_SEGMENT_LENGTH ,
5556 YAM_GRIPPER_CLOSED_POS ,
5657 YAM_GRIPPER_OPEN_POS ,
5758 YAM_LATERAL_OFFSET ,
59+ YAM_SOURCE_USD_PATH ,
5860 YAM_USD_PATH ,
5961 YAM_VISUAL_BASE_DEPTH ,
6062 YAM_VISUAL_BASE_WIDTH ,
@@ -210,6 +212,22 @@ def test_terminal_outcome_rewards_are_control_frequency_independent() -> None:
210212 torch .testing .assert_close (failure_return , torch .tensor ((0.0 , - 20.0 , - 20.0 )))
211213
212214
215+ def test_reset_replay_success_credit_excludes_invalid_terminal_states () -> None :
216+ """A simultaneous route completion and invalid state is a failed replay outcome."""
217+ term_values = {
218+ "invalid_cable" : torch .tensor ((False , True , False )),
219+ "invalid_robot_or_action" : torch .tensor ((False , False , True )),
220+ }
221+ command = CableRoutingCommand .__new__ (CableRoutingCommand )
222+ command .succeeded = torch .tensor ((True , True , True ))
223+ command .cfg = SimpleNamespace (failure_termination_names = tuple (term_values ))
224+ command ._env = SimpleNamespace (termination_manager = SimpleNamespace (get_term = lambda name : term_values [name ]))
225+
226+ terminal_success = command ._terminal_success (torch .arange (3 ))
227+
228+ torch .testing .assert_close (terminal_success , torch .tensor ((True , False , False )))
229+
230+
213231def test_cable_routing_ppo_starts_from_complete_1p2_second_rollouts () -> None :
214232 """The first SuccessMonitor outcomes come from complete episodes and sufficiently long rollouts."""
215233 env_cfg = CableRoutingEnvCfg ()
@@ -246,6 +264,7 @@ def test_cable_routing_manager_terms_use_menagerie_joint_and_body_names() -> Non
246264 assert term .params ["asset_cfg" ].joint_names == ["joint[1-6]" ]
247265
248266 assert cfg .events .reset_cable .params ["full_scene_replay_command_name" ] == "route"
267+ assert cfg .commands .route .failure_termination_names == cfg .rewards .success .params ["failure_termination_names" ]
249268
250269
251270def test_cable_routing_rewards_are_sparse_success_with_penalties_only () -> None :
@@ -533,8 +552,14 @@ def test_cable_routing_table_board_and_yams_have_front_edge_reachable_placement(
533552 assert yam .spawn .joint_drive_props [0 ].actuatorgravcomp is True
534553 assert set (yam .actuators ) == {"arm" , "gripper_drive" , "gripper_passive" }
535554 assert yam .actuators ["arm" ].joint_names_expr == ["joint[1-6]" ]
555+ assert yam .actuators ["arm" ].stiffness is None
556+ assert yam .actuators ["arm" ].damping is None
557+ assert yam .actuators ["arm" ].effort_limit_sim is None
558+ assert yam .actuators ["arm" ].velocity_limit_sim is None
559+ assert yam .actuators ["arm" ].armature is None
536560 assert yam .actuators ["gripper_drive" ].joint_names_expr == ["left_finger" ]
537- assert yam .actuators ["gripper_drive" ].stiffness == 2000.0
561+ assert yam .actuators ["gripper_drive" ].stiffness is None
562+ assert yam .actuators ["gripper_drive" ].damping is None
538563 assert yam .actuators ["gripper_passive" ].joint_names_expr == ["right_finger" ]
539564 assert yam .actuators ["gripper_passive" ].stiffness == 0.0
540565 assert yam .init_state .joint_pos ["left_finger" ] == YAM_GRIPPER_OPEN_POS
@@ -562,11 +587,13 @@ def test_cable_routing_table_board_and_yams_have_front_edge_reachable_placement(
562587
563588
564589def test_cable_routing_uses_complete_credential_free_yam_snapshot () -> None :
565- """Test the default YAM is the pinned, dependency-complete Menagerie package."""
590+ """Test the default YAM composes a calibration layer over the pinned Menagerie package."""
566591 yam_path = Path (YAM_USD_PATH )
592+ source_path = Path (YAM_SOURCE_USD_PATH )
567593
568594 assert yam_path .is_file ()
569- assert yam_path .name == "i2rt_yam_default.usda"
595+ assert yam_path .name == "i2rt_yam_cable_routing.usda"
596+ assert source_path .name == "i2rt_yam_default.usda"
570597 expected_hashes = {
571598 "i2rt_yam_default.usda" : "c1bedf1d978d1147f82d1c2cb5e56da1b5003eb14ec78bd0be89258c021404bc" ,
572599 "i2rt_yam/i2rt_yam.usda" : "d51c03bf78f1724c09ce46ae4c95cf64269871af25450fbc075bc2eb7cf08b55" ,
@@ -577,12 +604,12 @@ def test_cable_routing_uses_complete_credential_free_yam_snapshot() -> None:
577604 "i2rt_yam/Payload/MaterialsLibrary.usdc" : ("c20c4d119a844c7efbb74905589bef2bcab5eaeb0b5518f81d16f997d9da9adb" ),
578605 "i2rt_yam/Payload/Physics.usda" : "51f028b6a305fc788eb0286a7e67bf5d43cab54727ffcfeb20ef6502230b10c9" ,
579606 }
580- usd_files = {path .relative_to (yam_path .parent ).as_posix (): path for path in yam_path .parent .rglob ("*.usd*" )}
581- assert usd_files .keys () == expected_hashes .keys ()
607+ usd_files = {path .relative_to (source_path .parent ).as_posix (): path for path in source_path .parent .rglob ("*.usd*" )}
608+ assert usd_files .keys () == expected_hashes .keys () | { "i2rt_yam_cable_routing.usda" }
582609 for relative_path , expected_hash in expected_hashes .items ():
583610 assert hashlib .sha256 (usd_files [relative_path ].read_bytes ()).hexdigest () == expected_hash
584611
585- for usd_path in yam_path .parent .rglob ("*.usd*" ):
612+ for usd_path in source_path .parent .rglob ("*.usd*" ):
586613 if usd_path .suffix == ".usda" :
587614 contents = usd_path .read_text (encoding = "utf-8" )
588615 assert "omniverse://" not in contents
@@ -594,6 +621,7 @@ def test_cable_routing_uses_complete_credential_free_yam_snapshot() -> None:
594621 package_root = yam_path .parent .resolve ()
595622 resolved_layers = {Path (layer .identifier ).resolve ().relative_to (package_root ).as_posix () for layer in layers }
596623 assert resolved_layers == {
624+ "i2rt_yam_cable_routing.usda" ,
597625 "i2rt_yam_default.usda" ,
598626 "i2rt_yam/Payload/Contents.usda" ,
599627 "i2rt_yam/Payload/Geometry.usda" ,
@@ -615,7 +643,7 @@ def test_cable_routing_uses_disjoint_newton_collision_ownership() -> None:
615643 assert physics .use_cuda_graph
616644 assert physics .default_shape_cfg .ke == 4.0e4
617645 assert physics .default_shape_cfg .gap == 0.001
618- assert physics .default_shape_cfg .mu == 5.0
646+ assert physics .default_shape_cfg .mu == CABLE_CONTACT_FRICTION
619647
620648 coupler = physics .solver_cfg
621649 assert isinstance (coupler , CouplerProxyCfg )
@@ -651,15 +679,15 @@ def test_cable_routing_uses_disjoint_newton_collision_ownership() -> None:
651679 assert proxy .collide_interval == 2
652680 assert physics .num_substeps % proxy .collide_interval == 0
653681 assert proxy .bodies == [
654- r"/World/envs/env_.*/Yam(Left|Right)" ,
682+ r"/World/envs/env_.*/Yam(Left|Right)/Geometry/arm/link_1/link_2/link_3/link_4/link_5/link_6 " ,
655683 r"/World/envs/env_.*/(Table|Board)" ,
656684 r"/World/envs/env_.*/Peg(0|1)" ,
657685 ]
658686 assert coupler .model_cfg is None
659687
688+ assert cfg .scene .yam_left .spawn .physics_material is None
689+ assert cfg .scene .yam_right .spawn .physics_material is None
660690 for asset_cfg , expected_friction in (
661- (cfg .scene .yam_left , 5.0 ),
662- (cfg .scene .yam_right , 5.0 ),
663691 (cfg .scene .table , 0.5 ),
664692 (cfg .scene .board , 0.5 ),
665693 (cfg .scene .peg_0 , 0.5 ),
0 commit comments