@@ -638,71 +638,116 @@ def randomize_rigid_body_com(
638638 ranges [:, 0 ], ranges [:, 1 ], (len (env_ids ), 3 ), device = asset .device
639639 ).unsqueeze (1 )
640640
641- # get the current com of the bodies (num_assets, num_bodies)
641+ # get the current com of the bodies (num_assets, num_bodies, 7 )
642642 coms = wp .to_torch (asset .data .body_com_pose_b ).clone ()
643643
644- # Randomize the com in range
644+ # Randomize the com position
645645 coms [env_ids [:, None ], body_ids , :3 ] += rand_samples
646646
647- # Set the new coms
648- asset .set_coms_index (coms = coms , env_ids = env_ids )
649-
650-
651- def randomize_rigid_body_collider_offsets (
652- env : ManagerBasedEnv ,
653- env_ids : torch .Tensor | None ,
654- asset_cfg : SceneEntityCfg ,
655- rest_offset_distribution_params : tuple [float , float ] | None = None ,
656- contact_offset_distribution_params : tuple [float , float ] | None = None ,
657- distribution : Literal ["uniform" , "log_uniform" , "gaussian" ] = "uniform" ,
658- ):
659- """Randomize the collider parameters of rigid bodies in an asset by adding, scaling, or setting random values.
647+ # Newton's set_coms_index expects position-only (vec3f), while PhysX expects
648+ # the full pose (pos + quat). Detect backend to pass the correct shape.
649+ manager_name = env .sim .physics_manager .__name__ .lower ()
650+ if "newton" in manager_name :
651+ asset .set_coms_index (coms = coms [..., :3 ], env_ids = env_ids )
652+ else :
653+ asset .set_coms_index (coms = coms , env_ids = env_ids )
660654
661- This function allows randomizing the collider parameters of the asset, such as rest and contact offsets.
662- These correspond to the physics engine collider properties that affect the collision checking.
663655
664- The function samples random values from the given distribution parameters and applies the operation to
665- the collider properties. It then sets the values into the physics simulation. If the distribution parameters
666- are not provided for a particular property, the function does not modify the property.
656+ class randomize_rigid_body_collider_offsets (ManagerTermBase ):
657+ """Randomize the collider parameters of rigid bodies by setting random values.
667658
668- Currently, the distribution parameters are applied as absolute values.
659+ Supports both PhysX (rest/contact offset) and Newton (shape_margin/shape_gap).
660+ For Newton, ``rest_offset`` maps to ``shape_margin`` and ``contact_offset`` is
661+ converted to ``shape_gap`` via ``gap = contact_offset - margin``.
669662
670663 .. tip::
671- This function uses CPU tensors to assign the collision properties. It is recommended to use this function
672- only during the initialization of the environment.
664+ It is recommended to use this function only during environment initialization.
673665 """
674- # extract the used quantities (to enable type-hinting)
675- asset : RigidObject | Articulation = env .scene [asset_cfg .name ]
676666
677- # resolve environment ids
678- if env_ids is None :
679- env_ids = torch .arange (env .scene .num_envs , device = "cpu" )
667+ def __init__ (self , cfg : EventTermCfg , env : ManagerBasedEnv ):
668+ from isaaclab .assets import BaseArticulation , BaseRigidObject
680669
681- # sample collider properties from the given ranges and set into the physics simulation
682- # -- rest offsets
683- if rest_offset_distribution_params is not None :
684- rest_offset = wp .to_torch (asset .root_view .get_rest_offsets ()).clone ()
685- rest_offset = _randomize_prop_by_op (
686- rest_offset ,
687- rest_offset_distribution_params ,
688- None ,
689- slice (None ),
690- operation = "abs" ,
691- distribution = distribution ,
692- )
693- asset .root_view .set_rest_offsets (rest_offset , env_ids .cpu ())
694- # -- contact offsets
695- if contact_offset_distribution_params is not None :
696- contact_offset = wp .to_torch (asset .root_view .get_contact_offsets ()).clone ()
697- contact_offset = _randomize_prop_by_op (
698- contact_offset ,
699- contact_offset_distribution_params ,
700- None ,
701- slice (None ),
702- operation = "abs" ,
703- distribution = distribution ,
704- )
705- asset .root_view .set_contact_offsets (contact_offset , env_ids .cpu ())
670+ super ().__init__ (cfg , env )
671+
672+ self .asset_cfg : SceneEntityCfg = cfg .params ["asset_cfg" ]
673+ self .asset : RigidObject | Articulation = env .scene [self .asset_cfg .name ]
674+
675+ if not isinstance (self .asset , (BaseRigidObject , BaseArticulation )):
676+ raise ValueError (
677+ f"Randomization term 'randomize_rigid_body_collider_offsets' not supported for asset:"
678+ f" '{ self .asset_cfg .name } ' with type: '{ type (self .asset )} '."
679+ )
680+
681+ if "newton" in env .sim .physics_manager .__name__ .lower ():
682+ self ._init_newton ()
683+ else :
684+ self ._init_physx ()
685+
686+ def _init_physx (self ) -> None :
687+ asset = self .asset
688+ self ._default_rest = wp .to_torch (asset .root_view .get_rest_offsets ()).clone () # type: ignore[union-attr]
689+ self ._default_contact = wp .to_torch (asset .root_view .get_contact_offsets ()).clone () # type: ignore[union-attr]
690+ self ._env_ids_device = "cpu"
691+ self ._write_rest = lambda data , ids : asset .root_view .set_rest_offsets (data , ids ) # type: ignore[union-attr]
692+ self ._write_contact = lambda data , ids : asset .root_view .set_contact_offsets (data , ids ) # type: ignore[union-attr]
693+
694+ def _init_newton (self ) -> None :
695+ import isaaclab_newton .physics .newton_manager as nm
696+ from newton .solvers import SolverNotifyFlags
697+
698+ notify = lambda : nm .NewtonManager .add_model_change (SolverNotifyFlags .SHAPE_PROPERTIES ) # noqa: E731
699+
700+ model = nm .NewtonManager .get_model ()
701+ margin_buf = self .asset ._root_view .get_attribute ("shape_margin" , model )[:, 0 ] # type: ignore[union-attr]
702+ gap_buf = self .asset ._root_view .get_attribute ("shape_gap" , model )[:, 0 ] # type: ignore[union-attr]
703+
704+ self ._default_rest = wp .to_torch (margin_buf ).clone ()
705+ self ._default_contact = wp .to_torch (gap_buf ).clone ()
706+ self ._env_ids_device = self ._default_rest .device
707+ margin_view = wp .to_torch (margin_buf )
708+ gap_view = wp .to_torch (gap_buf )
709+
710+ def _write_margin (data : torch .Tensor , ids : torch .Tensor ) -> None :
711+ self ._default_rest [ids ] = data [ids ]
712+ margin_view [ids ] = data [ids ]
713+ notify ()
714+
715+ def _write_gap (data : torch .Tensor , ids : torch .Tensor ) -> None :
716+ gap = torch .clamp (data - self ._default_rest , min = 0.0 )
717+ self ._default_contact [ids ] = gap [ids ]
718+ gap_view [ids ] = gap [ids ]
719+ notify ()
720+
721+ self ._write_rest = _write_margin
722+ self ._write_contact = _write_gap
723+
724+ def __call__ (
725+ self ,
726+ env : ManagerBasedEnv ,
727+ env_ids : torch .Tensor | None ,
728+ asset_cfg : SceneEntityCfg ,
729+ rest_offset_distribution_params : tuple [float , float ] | None = None ,
730+ contact_offset_distribution_params : tuple [float , float ] | None = None ,
731+ distribution : Literal ["uniform" , "log_uniform" , "gaussian" ] = "uniform" ,
732+ ):
733+ if env_ids is None :
734+ env_ids = torch .arange (env .scene .num_envs , device = self ._env_ids_device )
735+ else :
736+ env_ids = env_ids .to (self ._env_ids_device )
737+
738+ if rest_offset_distribution_params is not None :
739+ data = _randomize_prop_by_op (
740+ self ._default_rest .clone (), rest_offset_distribution_params ,
741+ None , slice (None ), operation = "abs" , distribution = distribution ,
742+ )
743+ self ._write_rest (data , env_ids )
744+
745+ if contact_offset_distribution_params is not None :
746+ data = _randomize_prop_by_op (
747+ self ._default_contact .clone (), contact_offset_distribution_params ,
748+ None , slice (None ), operation = "abs" , distribution = distribution ,
749+ )
750+ self ._write_contact (data , env_ids )
706751
707752
708753class randomize_physics_scene_gravity (ManagerTermBase ):
0 commit comments