| |
| |
| |
| |
|
|
|
|
| """ |
| This script checks the functionality of scale randomization. |
| """ |
|
|
| from __future__ import annotations |
|
|
| """Launch Isaac Sim Simulator first.""" |
|
|
| from isaaclab.app import AppLauncher |
|
|
| |
| app_launcher = AppLauncher(headless=True, enable_cameras=True) |
| simulation_app = app_launcher.app |
|
|
| """Rest everything follows.""" |
|
|
| import pytest |
| import torch |
|
|
| import omni.usd |
| from pxr import Sdf |
|
|
| import isaaclab.envs.mdp as mdp |
| import isaaclab.sim as sim_utils |
| from isaaclab.assets import AssetBaseCfg, RigidObject, RigidObjectCfg |
| from isaaclab.envs import ManagerBasedEnv, ManagerBasedEnvCfg |
| from isaaclab.managers import ActionTerm, ActionTermCfg, SceneEntityCfg |
| from isaaclab.managers import EventTermCfg as EventTerm |
| from isaaclab.managers import ObservationGroupCfg as ObsGroup |
| from isaaclab.managers import ObservationTermCfg as ObsTerm |
| from isaaclab.scene import InteractiveSceneCfg |
| from isaaclab.terrains import TerrainImporterCfg |
| from isaaclab.utils import configclass |
|
|
| |
| |
| |
|
|
|
|
| class CubeActionTerm(ActionTerm): |
| """Simple action term that implements a PD controller to track a target position. |
| |
| The action term is applied to the cube asset. It involves two steps: |
| |
| 1. **Process the raw actions**: Typically, this includes any transformations of the raw actions |
| that are required to map them to the desired space. This is called once per environment step. |
| 2. **Apply the processed actions**: This step applies the processed actions to the asset. |
| It is called once per simulation step. |
| |
| In this case, the action term simply applies the raw actions to the cube asset. The raw actions |
| are the desired target positions of the cube in the environment frame. The pre-processing step |
| simply copies the raw actions to the processed actions as no additional processing is required. |
| The processed actions are then applied to the cube asset by implementing a PD controller to |
| track the target position. |
| """ |
|
|
| _asset: RigidObject |
| """The articulation asset on which the action term is applied.""" |
|
|
| def __init__(self, cfg: CubeActionTermCfg, env: ManagerBasedEnv): |
| |
| super().__init__(cfg, env) |
| |
| self._raw_actions = torch.zeros(env.num_envs, 3, device=self.device) |
| self._processed_actions = torch.zeros(env.num_envs, 3, device=self.device) |
| self._vel_command = torch.zeros(self.num_envs, 6, device=self.device) |
| |
| self.p_gain = cfg.p_gain |
| self.d_gain = cfg.d_gain |
|
|
| """ |
| Properties. |
| """ |
|
|
| @property |
| def action_dim(self) -> int: |
| return self._raw_actions.shape[1] |
|
|
| @property |
| def raw_actions(self) -> torch.Tensor: |
| return self._raw_actions |
|
|
| @property |
| def processed_actions(self) -> torch.Tensor: |
| return self._processed_actions |
|
|
| """ |
| Operations |
| """ |
|
|
| def process_actions(self, actions: torch.Tensor): |
| |
| self._raw_actions[:] = actions |
| |
| self._processed_actions[:] = self._raw_actions[:] |
|
|
| def apply_actions(self): |
| |
| pos_error = self._processed_actions - (self._asset.data.root_pos_w - self._env.scene.env_origins) |
| vel_error = -self._asset.data.root_lin_vel_w |
| |
| self._vel_command[:, :3] = self.p_gain * pos_error + self.d_gain * vel_error |
| self._asset.write_root_velocity_to_sim(self._vel_command) |
|
|
|
|
| @configclass |
| class CubeActionTermCfg(ActionTermCfg): |
| """Configuration for the cube action term.""" |
|
|
| class_type: type = CubeActionTerm |
| """The class corresponding to the action term.""" |
|
|
| p_gain: float = 5.0 |
| """Proportional gain of the PD controller.""" |
| d_gain: float = 0.5 |
| """Derivative gain of the PD controller.""" |
|
|
|
|
| |
| |
| |
|
|
|
|
| def base_position(env: ManagerBasedEnv, asset_cfg: SceneEntityCfg) -> torch.Tensor: |
| """Root linear velocity in the asset's root frame.""" |
| |
| asset: RigidObject = env.scene[asset_cfg.name] |
| return asset.data.root_pos_w - env.scene.env_origins |
|
|
|
|
| |
| |
| |
|
|
|
|
| @configclass |
| class MySceneCfg(InteractiveSceneCfg): |
| """Example scene configuration. |
| |
| The scene comprises of a ground plane, light source and floating cubes (gravity disabled). |
| """ |
|
|
| |
| terrain = TerrainImporterCfg(prim_path="/World/ground", terrain_type="plane", debug_vis=False) |
|
|
| |
| cube1: RigidObjectCfg = RigidObjectCfg( |
| prim_path="{ENV_REGEX_NS}/cube1", |
| spawn=sim_utils.CuboidCfg( |
| size=(0.2, 0.2, 0.2), |
| rigid_props=sim_utils.RigidBodyPropertiesCfg(max_depenetration_velocity=1.0, disable_gravity=True), |
| mass_props=sim_utils.MassPropertiesCfg(mass=1.0), |
| physics_material=sim_utils.RigidBodyMaterialCfg(), |
| visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 0.0)), |
| ), |
| init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 5)), |
| ) |
|
|
| |
| cube2: RigidObjectCfg = RigidObjectCfg( |
| prim_path="{ENV_REGEX_NS}/cube2", |
| spawn=sim_utils.CuboidCfg( |
| size=(0.2, 0.2, 0.2), |
| rigid_props=sim_utils.RigidBodyPropertiesCfg(max_depenetration_velocity=1.0, disable_gravity=True), |
| mass_props=sim_utils.MassPropertiesCfg(mass=1.0), |
| physics_material=sim_utils.RigidBodyMaterialCfg(), |
| visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 0.0)), |
| ), |
| init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 5)), |
| ) |
|
|
| |
| light = AssetBaseCfg( |
| prim_path="/World/light", |
| spawn=sim_utils.DistantLightCfg(color=(0.75, 0.75, 0.75), intensity=3000.0), |
| ) |
|
|
|
|
| |
| |
| |
|
|
|
|
| @configclass |
| class ActionsCfg: |
| """Action specifications for the MDP.""" |
|
|
| joint_pos = CubeActionTermCfg(asset_name="cube1") |
|
|
|
|
| @configclass |
| class ObservationsCfg: |
| """Observation specifications for the MDP.""" |
|
|
| @configclass |
| class PolicyCfg(ObsGroup): |
| """Observations for policy group.""" |
|
|
| |
| position = ObsTerm(func=base_position, params={"asset_cfg": SceneEntityCfg("cube1")}) |
|
|
| def __post_init__(self): |
| self.enable_corruption = True |
| self.concatenate_terms = True |
|
|
| |
| policy: PolicyCfg = PolicyCfg() |
|
|
|
|
| @configclass |
| class EventCfg: |
| """Configuration for events.""" |
|
|
| reset_base = EventTerm( |
| func=mdp.reset_root_state_uniform, |
| mode="reset", |
| params={ |
| "pose_range": {"x": (-0.5, 0.5), "y": (-0.5, 0.5), "yaw": (-3.14, 3.14)}, |
| "velocity_range": { |
| "x": (-0.5, 0.5), |
| "y": (-0.5, 0.5), |
| "z": (-0.5, 0.5), |
| }, |
| "asset_cfg": SceneEntityCfg("cube1"), |
| }, |
| ) |
|
|
| |
| randomize_cube1__scale = EventTerm( |
| func=mdp.randomize_rigid_body_scale, |
| mode="prestartup", |
| params={ |
| "scale_range": {"x": (0.5, 1.5), "y": (0.5, 1.5), "z": (0.5, 1.5)}, |
| "asset_cfg": SceneEntityCfg("cube1"), |
| }, |
| ) |
|
|
| |
| randomize_cube2__scale = EventTerm( |
| func=mdp.randomize_rigid_body_scale, |
| mode="prestartup", |
| params={ |
| "scale_range": {"x": (1.0, 1.0), "y": (1.0, 1.0), "z": (1.0, 1.0)}, |
| "asset_cfg": SceneEntityCfg("cube2"), |
| }, |
| ) |
|
|
|
|
| |
| |
| |
|
|
|
|
| @configclass |
| class CubeEnvCfg(ManagerBasedEnvCfg): |
| """Configuration for the locomotion velocity-tracking environment.""" |
|
|
| |
| scene: MySceneCfg = MySceneCfg(num_envs=10, env_spacing=2.5, replicate_physics=False) |
| |
| observations: ObservationsCfg = ObservationsCfg() |
| actions: ActionsCfg = ActionsCfg() |
| events: EventCfg = EventCfg() |
|
|
| def __post_init__(self): |
| """Post initialization.""" |
| |
| self.decimation = 2 |
| |
| self.sim.dt = 0.01 |
| self.sim.physics_material = self.scene.terrain.physics_material |
| self.sim.render_interval = self.decimation |
|
|
|
|
| @pytest.mark.parametrize("device", ["cpu", "cuda"]) |
| def test_scale_randomization(device): |
| """Test scale randomization for cube environment.""" |
| |
| omni.usd.get_context().new_stage() |
|
|
| |
| env_cfg = CubeEnvCfg() |
| env_cfg.sim.device = device |
|
|
| |
| env = ManagerBasedEnv(cfg=env_cfg) |
| |
| target_position = torch.rand(env.num_envs, 3, device=env.device) * 2 |
| target_position[:, 2] += 2.0 |
| |
| target_position -= env.scene.env_origins |
|
|
| |
| all_prim_paths = sim_utils.find_matching_prim_paths("/World/envs/env_.*/cube.*/.*") |
| assert len(all_prim_paths) == (env.num_envs * 2) |
|
|
| |
| applied_scaling_randomization = set() |
| prim_paths = sim_utils.find_matching_prim_paths("/World/envs/env_.*/cube1") |
|
|
| |
| stage = omni.usd.get_context().get_stage() |
|
|
| |
| for i in range(3): |
| prim_spec = Sdf.CreatePrimInLayer(stage.GetRootLayer(), prim_paths[i]) |
| scale_spec = prim_spec.GetAttributeAtPath(prim_paths[i] + ".xformOp:scale") |
| if scale_spec.default in applied_scaling_randomization: |
| raise ValueError( |
| "Detected repeat in applied scale values - indication scaling randomization is not working." |
| ) |
| applied_scaling_randomization.add(scale_spec.default) |
|
|
| |
| prim_paths = sim_utils.find_matching_prim_paths("/World/envs/env_.*/cube2") |
| for i in range(3): |
| prim_spec = Sdf.CreatePrimInLayer(stage.GetRootLayer(), prim_paths[i]) |
| scale_spec = prim_spec.GetAttributeAtPath(prim_paths[i] + ".xformOp:scale") |
| assert tuple(scale_spec.default) == (1.0, 1.0, 1.0) |
|
|
| |
| with torch.inference_mode(): |
| for count in range(200): |
| |
| if count % 100 == 0: |
| env.reset() |
| |
| env.step(target_position) |
|
|
| env.close() |
|
|
|
|
| def test_scale_randomization_failure_replicate_physics(): |
| """Test scale randomization failure when replicate physics is set to True.""" |
| |
| omni.usd.get_context().new_stage() |
| |
| cfg_failure = CubeEnvCfg() |
| cfg_failure.scene.replicate_physics = True |
|
|
| |
| with pytest.raises(RuntimeError): |
| env = ManagerBasedEnv(cfg_failure) |
| env.close() |
|
|