- README.md +18 -0
- src/isaaclab_tutorial/tasks/place_vial/config/so101/__init__.py +10 -17
- src/isaaclab_tutorial/tasks/place_vial/config/so101/agents/rsl_rl_distillation_cfg.py +3 -37
- src/isaaclab_tutorial/tasks/place_vial/config/so101/agents/rsl_rl_ppo_cfg.py +3 -32
- src/isaaclab_tutorial/tasks/place_vial/config/so101/camera_env_cfg.py +15 -90
- src/isaaclab_tutorial/tasks/place_vial/config/so101/env_cfg.py +50 -217
- src/isaaclab_tutorial/tasks/place_vial/config/so101/inspection_env_cfg.py +34 -0
- src/isaaclab_tutorial/tasks/place_vial/config/so101/lesson_scaffold.py +61 -0
- tests/test_config.py +8 -2
README.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# SO-101 vial placement
|
| 2 |
|
| 3 |
<p align="center"><img src="media/demo.gif" alt="SO-101 workshop demonstration" width="100%"></p>
|
|
|
|
| 1 |
+
# Isaac Lab Tutorial Learning Path
|
| 2 |
+
|
| 3 |
+
This branch is the exercise scaffold for the completed code on `main`.
|
| 4 |
+
Follow the **Train a Robot Arm** module, applying its marked regions in lesson order.
|
| 5 |
+
Imports, workshop assets, the reset dataset, and supporting utilities are supplied.
|
| 6 |
+
The initial scene intentionally has no robot. Lesson 02 makes inspection runnable;
|
| 7 |
+
lesson 04 adds actions, lesson 05 the state teacher, and lesson 06 camera distillation.
|
| 8 |
+
Run the upstream tests after completing lesson 06, not against unfinished exercises.
|
| 9 |
+
|
| 10 |
+
Compare with the completed reference without switching branches:
|
| 11 |
+
|
| 12 |
+
```bash
|
| 13 |
+
git diff main -- src/
|
| 14 |
+
```
|
| 15 |
+
|
| 16 |
+
The reference documentation below describes the completed task and includes
|
| 17 |
+
optional upstream workflows outside the course.
|
| 18 |
+
|
| 19 |
# SO-101 vial placement
|
| 20 |
|
| 21 |
<p align="center"><img src="media/demo.gif" alt="SO-101 workshop demonstration" width="100%"></p>
|
src/isaaclab_tutorial/tasks/place_vial/config/so101/__init__.py
CHANGED
|
@@ -4,25 +4,18 @@ import gymnasium as gym
|
|
| 4 |
|
| 5 |
_PACKAGE = "isaaclab_tutorial.tasks.place_vial.config.so101"
|
| 6 |
|
| 7 |
-
gym.register(
|
| 8 |
-
id="IsaacTutorial-Place-Vial-SO101",
|
| 9 |
-
entry_point="isaaclab.envs:ManagerBasedRLEnv",
|
| 10 |
-
|
| 11 |
-
kwargs={
|
| 12 |
-
"env_cfg_entry_point": f"{_PACKAGE}.env_cfg:SO101VialEnvCfg",
|
| 13 |
-
"rsl_rl_cfg_entry_point": f"{_PACKAGE}.agents.rsl_rl_ppo_cfg:SO101StatePPORunnerCfg",
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
| 16 |
-
|
|
|
|
| 17 |
|
| 18 |
gym.register(
|
| 19 |
-
id="IsaacTutorial-
|
| 20 |
entry_point="isaaclab.envs:ManagerBasedRLEnv",
|
| 21 |
disable_env_checker=True,
|
| 22 |
-
kwargs={
|
| 23 |
-
|
| 24 |
-
"rsl_rl_cfg_entry_point": f"{_PACKAGE}.agents.rsl_rl_distillation_cfg:SO101CameraDistillationRunnerCfg",
|
| 25 |
-
"rsl_rl_ppo_cfg_entry_point": f"{_PACKAGE}.agents.rsl_rl_ppo_cfg:SO101CameraPPORunnerCfg",
|
| 26 |
-
"default_agent": "rsl_rl",
|
| 27 |
-
},
|
| 28 |
)
|
|
|
|
| 4 |
|
| 5 |
_PACKAGE = "isaaclab_tutorial.tasks.place_vial.config.so101"
|
| 6 |
|
|
|
|
|
|
|
|
|
|
| 7 |
+
# BEGIN lesson-01-registration
|
|
|
|
|
|
|
|
|
|
| 8 |
+
# Complete task registration in the matching lesson.
|
| 9 |
+
# END lesson-01-registration
|
| 10 |
+
|
| 11 |
+
# BEGIN lesson-06-camera-registration
|
| 12 |
+
# Complete task registration in the matching lesson.
|
| 13 |
+
# END lesson-06-camera-registration
|
| 14 |
+
|
| 15 |
|
| 16 |
gym.register(
|
| 17 |
+
id="IsaacTutorial-Inspect-SO101",
|
| 18 |
entry_point="isaaclab.envs:ManagerBasedRLEnv",
|
| 19 |
disable_env_checker=True,
|
|
|
|
| 20 |
+
kwargs={"env_cfg_entry_point": f"{_PACKAGE}.inspection_env_cfg:SO101InspectionEnvCfg"},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
)
|
src/isaaclab_tutorial/tasks/place_vial/config/so101/agents/rsl_rl_distillation_cfg.py
CHANGED
|
@@ -23,42 +23,8 @@ class BoundedTeacherDistillation(Distillation):
|
|
| 23 |
return actions
|
| 24 |
|
| 25 |
|
|
|
|
| 26 |
@configclass
|
| 27 |
class SO101CameraDistillationRunnerCfg(RslRlDistillationRunnerCfg):
|
| 28 |
-
"""DAgger-style distillation: the student acts, the loaded state teacher labels every visited state."""
|
| 29 |
-
|
| 30 |
-
seed = 42
|
| 31 |
-
num_steps_per_env = 32
|
| 32 |
-
max_iterations = 1600
|
| 33 |
-
save_interval = 100
|
| 34 |
-
experiment_name = "so101_vial_camera_distillation"
|
| 35 |
-
run_name = ""
|
| 36 |
-
obs_groups = {"student": ["wrist_rgb", "proprioception"], "teacher": ["teacher_state"]}
|
| 37 |
-
clip_actions = 1.0
|
| 38 |
-
student = RslRlCNNModelCfg(
|
| 39 |
-
# Compact encoder for the 64 x 48 wrist image.
|
| 40 |
-
cnn_cfg=RslRlCNNModelCfg.CNNCfg(
|
| 41 |
-
output_channels=[16, 32, 32], kernel_size=[5, 3, 3], stride=[2, 2, 2], activation="elu"
|
| 42 |
-
|
| 43 |
-
hidden_dims=[256, 128],
|
| 44 |
-
activation="elu",
|
| 45 |
-
obs_normalization=True,
|
| 46 |
-
distribution_cfg=RslRlCNNModelCfg.GaussianDistributionCfg(init_std=0.05, std_type="log"),
|
| 47 |
-
)
|
| 48 |
-
# Must mirror the state actor so its checkpoint loads; the teacher always acts deterministically.
|
| 49 |
-
teacher = RslRlMLPModelCfg(
|
| 50 |
-
hidden_dims=[256, 256, 128],
|
| 51 |
-
activation="elu",
|
| 52 |
-
obs_normalization=True,
|
| 53 |
-
distribution_cfg=RslRlMLPModelCfg.GaussianDistributionCfg(init_std=0.2, std_type="log", std_range=(0.05, 0.3)),
|
| 54 |
-
)
|
| 55 |
-
|
| 56 |
-
class_name=(
|
| 57 |
-
"isaaclab_tutorial.tasks.place_vial.config.so101.agents.rsl_rl_distillation_cfg:BoundedTeacherDistillation"
|
| 58 |
-
),
|
| 59 |
-
num_learning_epochs=4,
|
| 60 |
-
learning_rate=5.0e-4,
|
| 61 |
-
gradient_length=1,
|
| 62 |
-
max_grad_norm=1.0,
|
| 63 |
-
loss_type="mse",
|
| 64 |
-
)
|
|
|
|
| 23 |
return actions
|
| 24 |
|
| 25 |
|
| 26 |
+
# BEGIN lesson-06-distillation
|
| 27 |
@configclass
|
| 28 |
class SO101CameraDistillationRunnerCfg(RslRlDistillationRunnerCfg):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
+
# END lesson-06-distillation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/isaaclab_tutorial/tasks/place_vial/config/so101/agents/rsl_rl_ppo_cfg.py
CHANGED
|
@@ -5,40 +5,11 @@ from isaaclab.utils.configclass import configclass
|
|
| 5 |
from isaaclab_rl.rsl_rl import RslRlCNNModelCfg, RslRlMLPModelCfg, RslRlOnPolicyRunnerCfg, RslRlPpoAlgorithmCfg
|
| 6 |
|
| 7 |
|
|
|
|
| 8 |
@configclass
|
| 9 |
class SO101StatePPORunnerCfg(RslRlOnPolicyRunnerCfg):
|
| 10 |
-
"""State teacher: fully observed actor, privileged critic."""
|
| 11 |
-
|
| 12 |
-
seed = 42
|
| 13 |
-
num_steps_per_env = 64
|
| 14 |
-
max_iterations = 800
|
| 15 |
-
save_interval = 50
|
| 16 |
-
experiment_name = "so101_vial_state"
|
| 17 |
-
run_name = ""
|
| 18 |
-
obs_groups = {"actor": ["policy"], "critic": ["critic"]}
|
| 19 |
-
|
| 20 |
-
actor = RslRlMLPModelCfg(
|
| 21 |
-
hidden_dims=[256, 256, 128],
|
| 22 |
-
activation="elu",
|
| 23 |
-
obs_normalization=True,
|
| 24 |
-
# Clamp the std: with clipped actions and an entropy bonus, PPO otherwise grows it without bound.
|
| 25 |
-
distribution_cfg=RslRlMLPModelCfg.GaussianDistributionCfg(init_std=0.2, std_type="log", std_range=(0.05, 0.3)),
|
| 26 |
-
)
|
| 27 |
-
critic = RslRlMLPModelCfg(hidden_dims=[256, 256, 128], activation="elu", obs_normalization=True)
|
| 28 |
-
algorithm = RslRlPpoAlgorithmCfg(
|
| 29 |
-
|
| 30 |
-
use_clipped_value_loss=True,
|
| 31 |
-
clip_param=0.2,
|
| 32 |
-
entropy_coef=0.005,
|
| 33 |
-
num_learning_epochs=5,
|
| 34 |
-
num_mini_batches=8,
|
| 35 |
-
learning_rate=3.0e-4,
|
| 36 |
-
schedule="adaptive",
|
| 37 |
-
gamma=0.995,
|
| 38 |
-
lam=0.95,
|
| 39 |
-
desired_kl=0.01,
|
| 40 |
-
max_grad_norm=1.0,
|
| 41 |
-
)
|
| 42 |
|
| 43 |
|
| 44 |
@configclass
|
|
|
|
| 5 |
from isaaclab_rl.rsl_rl import RslRlCNNModelCfg, RslRlMLPModelCfg, RslRlOnPolicyRunnerCfg, RslRlPpoAlgorithmCfg
|
| 6 |
|
| 7 |
|
| 8 |
+
# BEGIN lesson-05-ppo
|
| 9 |
@configclass
|
| 10 |
class SO101StatePPORunnerCfg(RslRlOnPolicyRunnerCfg):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
+
# END lesson-05-ppo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
|
| 15 |
@configclass
|
src/isaaclab_tutorial/tasks/place_vial/config/so101/camera_env_cfg.py
CHANGED
|
@@ -48,98 +48,32 @@ _HORIZONTAL_APERTURE = 20.955 * 80 / 64
|
|
| 48 |
WRIST_FOCAL_LENGTH_PIXELS = 80 * _FOCAL_LENGTH / _HORIZONTAL_APERTURE
|
| 49 |
|
| 50 |
|
|
|
|
| 51 |
@configclass
|
| 52 |
class SO101CameraSceneCfg(SO101SceneCfg):
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
prim_path="{ENV_REGEX_NS}/Robot/gripper/wowrobo_2MP_camera/pinhole",
|
| 57 |
-
spawn=PinholeCameraCfg(focal_length=_FOCAL_LENGTH, horizontal_aperture=_HORIZONTAL_APERTURE),
|
| 58 |
-
offset=CameraCfg.OffsetCfg(convention="opengl"),
|
| 59 |
-
data_types=["rgb"],
|
| 60 |
-
width=80,
|
| 61 |
-
height=60,
|
| 62 |
-
update_period=1.0 / 30.0,
|
| 63 |
-
update_latest_camera_pose=True,
|
| 64 |
-
renderer_cfg=_camera_renderer_cfg(),
|
| 65 |
-
background_color=(0.3, 0.3, 0.3),
|
| 66 |
-
)
|
| 67 |
-
|
| 68 |
-
# The Newton renderer only reads material color.
|
| 69 |
-
robot_visual = VisualMaterialCfg(prim_path="{ENV_REGEX_NS}/Robot/Looks/material_a_d_printed", spawn=None)
|
| 70 |
-
desk_visual = VisualMaterialCfg(prim_path="{ENV_REGEX_NS}/Desk/Looks/Wood", spawn=None)
|
| 71 |
-
rack_visual = VisualMaterialCfg(prim_path="{ENV_REGEX_NS}/Rack/WorkshopVisual/Looks/OmniPBR", spawn=None)
|
| 72 |
-
vial_body_visual = VisualMaterialCfg(prim_path="{ENV_REGEX_NS}/Vial/Looks/Vial_plastic", spawn=None)
|
| 73 |
-
vial_cap_visual = VisualMaterialCfg(prim_path="{ENV_REGEX_NS}/Vial/Looks/Vial_cap", spawn=None)
|
| 74 |
-
vial_label_visual = VisualMaterialCfg(prim_path="{ENV_REGEX_NS}/Vial/Looks/Label", spawn=None)
|
| 75 |
-
|
| 76 |
-
def __post_init__(self):
|
| 77 |
-
self.robot.spawn.hide_camera_housing = True
|
| 78 |
-
self.vial.spawn.usd_path = str(VIAL_LABELED_USD)
|
| 79 |
|
| 80 |
|
|
|
|
| 81 |
@configclass
|
| 82 |
class WristImageCfg(ObsGroup):
|
| 83 |
-
"""Two-frame wrist RGB history with episode-consistent photometric (and, for sim2real, intrinsics) variation."""
|
| 84 |
-
|
| 85 |
-
image = ObsTerm(
|
| 86 |
-
func=mdp.wrist_rgb,
|
| 87 |
-
|
| 88 |
-
"sensor_cfg": SceneEntityCfg("wrist_camera"),
|
| 89 |
-
"exposure_range": (0.75, 1.25),
|
| 90 |
-
"contrast_range": (0.85, 1.15),
|
| 91 |
-
"white_balance_range": (0.90, 1.10),
|
| 92 |
-
"brightness_range": (-0.05, 0.05),
|
| 93 |
-
"gamma_range": preset(default=(1.0, 1.0), sim2real=(0.85, 1.15)),
|
| 94 |
-
"blur_range": preset(default=(0.0, 0.0), sim2real=(0.0, 0.5)),
|
| 95 |
-
"history_length": 2,
|
| 96 |
-
"projection_size": POLICY_IMAGE_SIZE,
|
| 97 |
-
"focal_length_pixels": WRIST_FOCAL_LENGTH_PIXELS,
|
| 98 |
-
"focal_scale_range": preset(default=(1.0, 1.0), sim2real=(0.95, 1.05)),
|
| 99 |
-
"principal_point_pixels": preset(default=0.0, sim2real=1.5),
|
| 100 |
-
"radial_distortion_range": preset(default=(0.0, 0.0), sim2real=(-0.04, 0.04)),
|
| 101 |
-
},
|
| 102 |
-
noise=UniformNoiseCfg(n_min=-0.025, n_max=0.025),
|
| 103 |
-
)
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
self.enable_corruption = True
|
| 107 |
|
| 108 |
|
|
|
|
| 109 |
@configclass
|
| 110 |
class ProprioceptionCfg(ObsGroup):
|
| 111 |
-
"""Proprioceptive observations available on the real robot, in the encoders' (possibly biased) coordinates."""
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
func=mdp.encoder_joint_pos,
|
| 115 |
-
params={"asset_cfg": SceneEntityCfg("robot")},
|
| 116 |
-
noise=UniformNoiseCfg(n_min=-0.01, n_max=0.01),
|
| 117 |
-
)
|
| 118 |
-
joint_vel = ObsTerm(
|
| 119 |
-
func=mdp.joint_vel,
|
| 120 |
-
params={"asset_cfg": SceneEntityCfg("robot")},
|
| 121 |
-
noise=UniformNoiseCfg(n_min=-0.02, n_max=0.02),
|
| 122 |
-
)
|
| 123 |
-
joint_target = ObsTerm(
|
| 124 |
-
func=mdp.encoder_joint_target,
|
| 125 |
-
params={"asset_cfg": SceneEntityCfg("robot")},
|
| 126 |
-
noise=UniformNoiseCfg(n_min=-0.005, n_max=0.005),
|
| 127 |
-
)
|
| 128 |
-
previous_action = ObsTerm(func=mdp.last_action)
|
| 129 |
-
|
| 130 |
-
def __post_init__(self):
|
| 131 |
-
|
| 132 |
-
self.concatenate_terms = True
|
| 133 |
|
| 134 |
|
|
|
|
| 135 |
@configclass
|
| 136 |
class CameraObservationsCfg:
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
proprioception: ProprioceptionCfg = ProprioceptionCfg()
|
| 141 |
-
teacher_state: PolicyStateGroupCfg = PolicyStateGroupCfg()
|
| 142 |
-
critic: CriticStateGroupCfg = CriticStateGroupCfg()
|
| 143 |
|
| 144 |
|
| 145 |
def _color_event(materials: str, color_range) -> EventTerm:
|
|
@@ -173,17 +107,8 @@ class CameraSim2RealEventsCfg(Sim2RealEventsCfg):
|
|
| 173 |
)
|
| 174 |
|
| 175 |
|
|
|
|
| 176 |
@configclass
|
| 177 |
class SO101VialCameraEnvCfg(SO101VialEnvCfg):
|
| 178 |
-
"""Vial placement from wrist RGB and proprioception, distilled from the state teacher."""
|
| 179 |
-
|
| 180 |
-
scene: SO101CameraSceneCfg = SO101CameraSceneCfg(num_envs=1024, env_spacing=0.9, replicate_physics=True)
|
| 181 |
-
observations: CameraObservationsCfg = CameraObservationsCfg()
|
| 182 |
-
events: EventsCfg = preset(default=EventsCfg(), sim2real=CameraSim2RealEventsCfg())
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
from isaaclab_tutorial.utils import evaluation
|
| 186 |
-
|
| 187 |
-
super().play_mode()
|
| 188 |
-
|
| 189 |
-
self.scene.num_envs = min(self.scene.num_envs, 8)
|
|
|
|
| 48 |
WRIST_FOCAL_LENGTH_PIXELS = 80 * _FOCAL_LENGTH / _HORIZONTAL_APERTURE
|
| 49 |
|
| 50 |
|
| 51 |
+
# BEGIN lesson-06-SO101CameraSceneCfg
|
| 52 |
@configclass
|
| 53 |
class SO101CameraSceneCfg(SO101SceneCfg):
|
| 54 |
+
pass
|
|
|
|
| 55 |
+
# END lesson-06-SO101CameraSceneCfg
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
|
| 58 |
+
# BEGIN lesson-06-WristImageCfg
|
| 59 |
@configclass
|
| 60 |
class WristImageCfg(ObsGroup):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
+
# END lesson-06-WristImageCfg
|
|
|
|
| 63 |
|
| 64 |
|
| 65 |
+
# BEGIN lesson-06-ProprioceptionCfg
|
| 66 |
@configclass
|
| 67 |
class ProprioceptionCfg(ObsGroup):
|
|
|
|
|
|
|
| 68 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
+
# END lesson-06-ProprioceptionCfg
|
|
|
|
| 70 |
|
| 71 |
|
| 72 |
+
# BEGIN lesson-06-CameraObservationsCfg
|
| 73 |
@configclass
|
| 74 |
class CameraObservationsCfg:
|
| 75 |
+
pass
|
|
|
|
| 76 |
+
# END lesson-06-CameraObservationsCfg
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
|
| 79 |
def _color_event(materials: str, color_range) -> EventTerm:
|
|
|
|
| 107 |
)
|
| 108 |
|
| 109 |
|
| 110 |
+
# BEGIN lesson-06-SO101VialCameraEnvCfg
|
| 111 |
@configclass
|
| 112 |
class SO101VialCameraEnvCfg(SO101VialEnvCfg):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
+
pass
|
|
|
|
|
|
|
|
|
|
| 114 |
+
# END lesson-06-SO101VialCameraEnvCfg
|
|
|
src/isaaclab_tutorial/tasks/place_vial/config/so101/env_cfg.py
CHANGED
|
@@ -14,7 +14,10 @@ from pxr import Gf, Sdf, Usd, UsdGeom, UsdPhysics, UsdShade
|
|
| 14 |
|
| 15 |
import isaaclab.sim as sim_utils
|
| 16 |
from isaaclab.assets import AssetBaseCfg, RigidObjectCfg
|
| 17 |
-
from isaaclab.envs import
|
|
|
|
|
|
|
|
|
|
| 18 |
from isaaclab.envs.mdp.actions.actions_cfg import RelativeJointPositionActionCfg
|
| 19 |
from isaaclab.managers import EventTermCfg as EventTerm
|
| 20 |
from isaaclab.managers import ObservationGroupCfg as ObsGroup
|
|
@@ -45,6 +48,14 @@ from isaaclab_tutorial.assets import DESK_USD, RACK_USD, RESET_DATASET, VIAL_USD
|
|
| 45 |
from isaaclab_tutorial.tasks.place_vial import mdp
|
| 46 |
from isaaclab_tutorial.tasks.place_vial.reset.curriculum import ALL_PHASES, CANONICAL_START
|
| 47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
JOINTS = ["shoulder_pan", "shoulder_lift", "elbow_flex", "wrist_flex", "wrist_roll", "gripper"]
|
| 49 |
ARM_JOINTS = JOINTS[:-1]
|
| 50 |
|
|
@@ -438,77 +449,14 @@ def _jaw_support_sensor(link: str) -> ContactSensorCfg:
|
|
| 438 |
class SO101SceneCfg(InteractiveSceneCfg):
|
| 439 |
"""One SO-101, one vial, one rack, and a desk; sim2real adds the gray mat and jaw/support contact sensing."""
|
| 440 |
|
|
|
|
| 441 |
-
robot =
|
| 442 |
-
prim_path="{ENV_REGEX_NS}/Robot",
|
| 443 |
-
init_state=SO101_CFG.init_state.replace(
|
| 444 |
-
pos=preset(default=(-0.05, 0.0, 0.0), sim2real=(-0.05, 0.0, BASE_HEIGHT_CORRECTION)),
|
| 445 |
-
|
| 446 |
-
rot=(0.0, 0.0, 0.7071068, 0.7071068),
|
| 447 |
-
joint_pos=dict(zip(JOINTS, WORKSHOP_INITIAL_JOINT_POSITION, strict=True)),
|
| 448 |
-
),
|
| 449 |
-
soft_joint_pos_limit_factor=0.98,
|
| 450 |
-
)
|
| 451 |
|
| 452 |
-
|
| 453 |
-
prim_path="{ENV_REGEX_NS}/Vial",
|
| 454 |
-
spawn=VialSpawnCfg(
|
| 455 |
-
usd_path=str(VIAL_USD),
|
| 456 |
-
rigid_props=preset(
|
| 457 |
-
default=None,
|
| 458 |
-
physx=PhysxRigidBodyCfg(solver_position_iteration_count=PHYSX_SOLVER_POSITION_ITERATIONS),
|
| 459 |
-
),
|
| 460 |
-
activate_contact_sensors=True,
|
| 461 |
-
randomize_diameters=preset(default=False, sim2real=True),
|
| 462 |
-
),
|
| 463 |
-
init_state=RigidObjectCfg.InitialStateCfg(
|
| 464 |
-
pos=TABLETOP_VIAL_POSITION,
|
| 465 |
-
|
| 466 |
-
rot=(0.0, 0.7071068, 0.0, 0.7071068),
|
| 467 |
-
|
| 468 |
-
)
|
| 469 |
-
|
| 470 |
-
rack = RigidObjectCfg(
|
| 471 |
-
prim_path="{ENV_REGEX_NS}/Rack",
|
| 472 |
-
spawn=sim_utils.UsdFileCfg(usd_path=str(RACK_USD), func=_spawn_usd_with_contact_material),
|
| 473 |
-
init_state=RigidObjectCfg.InitialStateCfg(pos=(0.18, 0.08, 0.04)),
|
| 474 |
-
)
|
| 475 |
-
|
| 476 |
-
desk = AssetBaseCfg(
|
| 477 |
-
prim_path="{ENV_REGEX_NS}/Desk",
|
| 478 |
-
spawn=sim_utils.UsdFileCfg(usd_path=str(DESK_USD), func=_spawn_usd_with_contact_material),
|
| 479 |
-
init_state=AssetBaseCfg.InitialStateCfg(pos=(0.22, 0.0, 0.032), rot=(0.0, 0.0, 0.7071068, 0.7071068)),
|
| 480 |
-
)
|
| 481 |
-
|
| 482 |
-
support = preset(
|
| 483 |
-
default=None,
|
| 484 |
-
|
| 485 |
-
prim_path="{ENV_REGEX_NS}/Support",
|
| 486 |
-
spawn=SupportSpawnCfg(
|
| 487 |
-
size=(0.39, 0.39, 0.005),
|
| 488 |
-
rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(kinematic_enabled=True),
|
| 489 |
-
collision_props=sim_utils.UsdPhysicsCollisionCfg(),
|
| 490 |
-
mass_props=sim_utils.MassCfg(mass=1.0),
|
| 491 |
-
physics_material=WORKSHOP_CONTACT_MATERIAL,
|
| 492 |
-
visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.07, 0.07, 0.07)),
|
| 493 |
-
),
|
| 494 |
-
init_state=RigidObjectCfg.InitialStateCfg(pos=(0.22, 0.02, 0.0325)),
|
| 495 |
-
),
|
| 496 |
-
)
|
| 497 |
-
|
| 498 |
-
# Unfiltered: OV PhysX cannot build a filtered contact view for the gripper link in a cloned scene.
|
| 499 |
-
fixed_jaw_contact = ContactSensorCfg(prim_path="{ENV_REGEX_NS}/Robot/gripper", history_length=4)
|
| 500 |
-
moving_jaw_contact = ContactSensorCfg(
|
| 501 |
-
prim_path="{ENV_REGEX_NS}/Robot/moving_jaw_so101_v1",
|
| 502 |
-
filter_prim_paths_expr=["{ENV_REGEX_NS}/Vial"],
|
| 503 |
-
history_length=4,
|
| 504 |
-
)
|
| 505 |
-
vial_rack_contact = ContactSensorCfg(
|
| 506 |
-
prim_path="{ENV_REGEX_NS}/Vial",
|
| 507 |
-
filter_prim_paths_expr=["{ENV_REGEX_NS}/Rack"],
|
| 508 |
-
history_length=4,
|
| 509 |
-
)
|
| 510 |
-
fixed_jaw_support = preset(default=None, sim2real=_jaw_support_sensor("gripper"))
|
| 511 |
-
moving_jaw_support = preset(default=None, sim2real=_jaw_support_sensor("moving_jaw_so101_v1"))
|
| 512 |
|
| 513 |
light = AssetBaseCfg(
|
| 514 |
prim_path="/World/Light",
|
|
@@ -521,108 +469,39 @@ class SO101SceneCfg(InteractiveSceneCfg):
|
|
| 521 |
)
|
| 522 |
|
| 523 |
|
|
|
|
| 524 |
@configclass
|
| 525 |
class ActionsCfg:
|
| 526 |
-
"""Joint targets relative to the measured position, matching the real SO-101 position interface."""
|
| 527 |
-
|
| 528 |
-
arm_action = RelativeJointPositionActionCfg(
|
| 529 |
-
asset_name="robot",
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
# Small offsets stall the real shoulder and elbow, so sim2real gives them larger steps.
|
| 533 |
-
scale=preset(
|
| 534 |
-
default=0.033,
|
| 535 |
-
sim2real={
|
| 536 |
-
"shoulder_pan": 0.033,
|
| 537 |
-
"shoulder_lift": 0.12,
|
| 538 |
-
"elbow_flex": 0.07,
|
| 539 |
-
"wrist_flex": 0.033,
|
| 540 |
-
"wrist_roll": 0.033,
|
| 541 |
-
},
|
| 542 |
-
),
|
| 543 |
-
use_zero_offset=True,
|
| 544 |
-
)
|
| 545 |
-
gripper_action = RelativeJointPositionActionCfg(
|
| 546 |
-
asset_name="robot",
|
| 547 |
-
joint_names=["gripper"],
|
| 548 |
-
# Avoid opening a grasp rapidly from a small policy bias.
|
| 549 |
-
scale=0.02,
|
| 550 |
-
use_zero_offset=True,
|
| 551 |
-
)
|
| 552 |
|
| 553 |
|
|
|
|
| 554 |
@configclass
|
| 555 |
class PolicyStateGroupCfg(ObsGroup):
|
| 556 |
-
"""Fully observed state actor inputs."""
|
| 557 |
-
|
| 558 |
-
joint_pos = ObsTerm(func=mdp.joint_pos, params={"asset_cfg": SceneEntityCfg("robot", joint_names=JOINTS)})
|
| 559 |
-
joint_vel = ObsTerm(func=mdp.joint_vel, params={"asset_cfg": SceneEntityCfg("robot", joint_names=JOINTS)})
|
| 560 |
-
joint_target = ObsTerm(func=mdp.joint_target, params={"asset_cfg": SceneEntityCfg("robot", joint_names=JOINTS)})
|
| 561 |
-
previous_action = ObsTerm(func=mdp.last_action)
|
| 562 |
-
end_effector = ObsTerm(
|
| 563 |
-
func=mdp.body_state_b,
|
| 564 |
-
|
| 565 |
-
"body_asset_cfg": SceneEntityCfg("robot", body_names="gripper"),
|
| 566 |
-
"base_asset_cfg": SceneEntityCfg("robot"),
|
| 567 |
-
},
|
| 568 |
-
)
|
| 569 |
-
vial = ObsTerm(
|
| 570 |
-
func=mdp.body_state_b,
|
| 571 |
-
params={"body_asset_cfg": SceneEntityCfg("vial"), "base_asset_cfg": SceneEntityCfg("robot")},
|
| 572 |
-
)
|
| 573 |
-
vial_in_rack = ObsTerm(func=mdp.vial_in_rack_frame)
|
| 574 |
-
|
| 575 |
-
# Latched milestones make the once-per-episode milestone rewards Markov.
|
| 576 |
-
progress = ObsTerm(func=mdp.progress_flags)
|
| 577 |
-
|
| 578 |
-
def __post_init__(self):
|
| 579 |
-
self.enable_corruption = False
|
| 580 |
-
self.concatenate_terms = True
|
| 581 |
|
| 582 |
|
|
|
|
| 583 |
@configclass
|
| 584 |
class CriticStateGroupCfg(PolicyStateGroupCfg):
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
|
| 588 |
|
| 589 |
|
|
|
|
| 590 |
@configclass
|
| 591 |
class ObservationsCfg:
|
| 592 |
-
|
| 593 |
-
|
| 594 |
|
| 595 |
|
|
|
|
| 596 |
@configclass
|
| 597 |
class EventsCfg:
|
| 598 |
-
"""Task-horizon resets from the physics-validated dataset plus modest vial variation."""
|
| 599 |
-
|
| 600 |
-
vial_material = EventTerm(
|
| 601 |
-
func=mdp.randomize_rigid_body_material,
|
| 602 |
-
mode="startup",
|
| 603 |
-
|
| 604 |
-
"asset_cfg": SceneEntityCfg("vial"),
|
| 605 |
-
"static_friction_range": preset(default=VIAL_FRICTION_RANGE, physx=PHYSX_VIAL_FRICTION_RANGE),
|
| 606 |
-
"dynamic_friction_range": preset(default=VIAL_FRICTION_RANGE, physx=PHYSX_VIAL_FRICTION_RANGE),
|
| 607 |
-
"restitution_range": (0.0, 0.02),
|
| 608 |
-
"num_buckets": 32,
|
| 609 |
-
},
|
| 610 |
-
)
|
| 611 |
-
vial_mass = EventTerm(
|
| 612 |
-
func=mdp.randomize_rigid_body_mass,
|
| 613 |
-
mode="startup",
|
| 614 |
-
params={
|
| 615 |
-
"asset_cfg": SceneEntityCfg("vial"),
|
| 616 |
-
# Newton cannot reliably infer mass from the detailed mesh.
|
| 617 |
-
"mass_distribution_params": (0.015, 0.025),
|
| 618 |
-
"operation": "abs",
|
| 619 |
-
},
|
| 620 |
-
)
|
| 621 |
-
|
| 622 |
-
func=mdp.ResetFromDataset,
|
| 623 |
-
mode="reset",
|
| 624 |
-
params={"dataset_path": str(RESET_DATASET), "sequential": False, "phase_weights": ALL_PHASES},
|
| 625 |
-
)
|
| 626 |
|
| 627 |
|
| 628 |
def _material_event(asset_cfg: SceneEntityCfg, friction_range: tuple[float, float]) -> EventTerm:
|
|
@@ -702,43 +581,18 @@ class Sim2RealEventsCfg(EventsCfg):
|
|
| 702 |
)
|
| 703 |
|
| 704 |
|
|
|
|
| 705 |
@configclass
|
| 706 |
class RewardsCfg:
|
| 707 |
-
"""Sparse physical milestones, a success bonus, two dense shaping terms, and light regularization."""
|
| 708 |
-
|
| 709 |
-
# Sim2real aims the pad centre slightly above the cap, favouring a shallower pinch clear of the mat.
|
| 710 |
-
|
| 711 |
-
func=mdp.approach_progress, weight=1.0, params={"height_offset": preset(default=0.0, sim2real=0.008)}
|
| 712 |
-
)
|
| 713 |
-
held_goal = RewTerm(func=mdp.held_goal, weight=0.1)
|
| 714 |
-
milestones = RewTerm(func=mdp.milestones, weight=10.0)
|
| 715 |
-
success = RewTerm(func=mdp.success, weight=200.0)
|
| 716 |
-
|
| 717 |
-
func=mdp.is_terminated_term, weight=-50.0, params={"term_keys": ["vial_out_of_bounds", "vial_dropped"]}
|
| 718 |
-
)
|
| 719 |
-
action_rate = RewTerm(func=mdp.action_rate_l2, weight=-0.002)
|
| 720 |
-
joint_velocity = RewTerm(func=mdp.joint_vel_l2, weight=-0.0002)
|
| 721 |
-
joint_limits = RewTerm(func=mdp.joint_pos_limits, weight=-1.0)
|
| 722 |
-
jaw_support_force = preset(
|
| 723 |
-
default=None,
|
| 724 |
-
sim2real=RewTerm(func=mdp.jaw_support_force, weight=-8.0, params={"free_force": 0.25, "force_span": 2.0}),
|
| 725 |
-
)
|
| 726 |
|
| 727 |
|
|
|
|
| 728 |
@configclass
|
| 729 |
class TerminationsCfg:
|
| 730 |
-
success = DoneTerm(func=mdp.placement_success)
|
| 731 |
-
vial_dropped = DoneTerm(func=mdp.vial_dropped)
|
| 732 |
-
# The robot base frame is yawed +90 degrees, so these bounds are 0.5 m to its sides and 0.62 m ahead/behind.
|
| 733 |
-
vial_out_of_bounds = DoneTerm(
|
| 734 |
-
func=mdp.out_of_bound,
|
| 735 |
-
|
| 736 |
-
"asset_cfg": SceneEntityCfg("vial"),
|
| 737 |
-
"in_bound_range": {"x": (-0.55, 0.45), "y": (-0.62, 0.62), "z": (-0.01, 1.0)},
|
| 738 |
-
},
|
| 739 |
-
)
|
| 740 |
-
unstable_robot = DoneTerm(func=mdp.abnormal_robot_state)
|
| 741 |
-
|
| 742 |
|
| 743 |
|
| 744 |
@configclass
|
|
@@ -768,44 +622,23 @@ class PhysicsCfg(PresetCfg):
|
|
| 768 |
default = newton_mjwarp
|
| 769 |
|
| 770 |
|
|
|
|
| 771 |
@configclass
|
| 772 |
class SO101VialEnvCfg(ManagerBasedRLEnvCfg):
|
| 773 |
-
"""
|
| 774 |
|
| 775 |
-
scene: SO101SceneCfg = SO101SceneCfg(num_envs=
|
| 776 |
-
actions:
|
| 777 |
-
observations:
|
| 778 |
-
events: EventsCfg = preset(default=EventsCfg(), sim2real=Sim2RealEventsCfg())
|
| 779 |
-
rewards: RewardsCfg = RewardsCfg()
|
| 780 |
-
|
|
|
|
|
|
|
| 781 |
|
| 782 |
def __post_init__(self):
|
| 783 |
self.decimation = 4
|
| 784 |
-
# The measured shoulder response is slower, so sim2real attempts get more time.
|
| 785 |
-
self.episode_length_s = preset(default=20.0, sim2real=30.0)
|
| 786 |
-
self.
|
| 787 |
self.sim.dt = 1.0 / 120.0
|
| 788 |
self.sim.render_interval = self.decimation
|
| 789 |
self.sim.physics = PhysicsCfg()
|
| 790 |
self.sim.default_visualizer_cfg = VisualizerCfg(eye=(0.64, 0.0, 0.36), lookat=(0.19, 0.02, 0.075))
|
| 791 |
-
|
| 792 |
-
def play_mode(self):
|
| 793 |
-
"""Play and evaluate complete episodes from the home pose, in dataset order.
|
| 794 |
-
|
| 795 |
-
The nominal task pins the vial to its workshop material and mass; sim2real keeps its physics randomization.
|
| 796 |
-
"""
|
| 797 |
-
from isaaclab_tutorial.utils import evaluation
|
| 798 |
-
|
| 799 |
-
requested_num_envs = self.scene.num_envs
|
| 800 |
-
|
| 801 |
-
if evaluation.EXACT_EVALUATION_ACTIVE:
|
| 802 |
-
self.scene.num_envs = min(requested_num_envs, evaluation.EVALUATION_EPISODES)
|
| 803 |
-
else:
|
| 804 |
-
self.scene.num_envs = min(self.scene.num_envs, 16)
|
| 805 |
-
self.events.reset_from_dataset.params["sequential"] = True
|
| 806 |
-
self.events.reset_from_dataset.params["phase_weights"] = CANONICAL_START
|
| 807 |
-
if not isinstance(self.events, Sim2RealEventsCfg):
|
| 808 |
-
self.events.vial_material.params.update(
|
| 809 |
-
static_friction_range=(0.7, 0.7), dynamic_friction_range=(0.7, 0.7), restitution_range=(0.0, 0.0)
|
| 810 |
-
)
|
| 811 |
-
self.events.vial_mass.params["mass_distribution_params"] = (0.020, 0.020)
|
|
|
|
| 14 |
|
| 15 |
import isaaclab.sim as sim_utils
|
| 16 |
from isaaclab.assets import AssetBaseCfg, RigidObjectCfg
|
| 17 |
+
from isaaclab.envs import (
|
| 18 |
+
ManagerBasedRLEnvCfg,
|
| 19 |
+
VideoRecorderCfg, # noqa: F401 -- used by the lesson 04 replacement
|
| 20 |
+
)
|
| 21 |
from isaaclab.envs.mdp.actions.actions_cfg import RelativeJointPositionActionCfg
|
| 22 |
from isaaclab.managers import EventTermCfg as EventTerm
|
| 23 |
from isaaclab.managers import ObservationGroupCfg as ObsGroup
|
|
|
|
| 48 |
from isaaclab_tutorial.tasks.place_vial import mdp
|
| 49 |
from isaaclab_tutorial.tasks.place_vial.reset.curriculum import ALL_PHASES, CANONICAL_START
|
| 50 |
|
| 51 |
+
from .lesson_scaffold import ( # noqa: F401 -- supplied for the lesson 01-04 replacements
|
| 52 |
+
AgentObservationGroupsCfg,
|
| 53 |
+
EmptyActionsCfg,
|
| 54 |
+
InspectionObservationsCfg,
|
| 55 |
+
LessonEventsCfg,
|
| 56 |
+
LessonTerminationsCfg,
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
JOINTS = ["shoulder_pan", "shoulder_lift", "elbow_flex", "wrist_flex", "wrist_roll", "gripper"]
|
| 60 |
ARM_JOINTS = JOINTS[:-1]
|
| 61 |
|
|
|
|
| 449 |
class SO101SceneCfg(InteractiveSceneCfg):
|
| 450 |
"""One SO-101, one vial, one rack, and a desk; sim2real adds the gray mat and jaw/support contact sensing."""
|
| 451 |
|
| 452 |
+
# BEGIN lesson-02-robot
|
| 453 |
+
robot = None
|
|
|
|
|
|
|
|
|
|
| 454 |
+
# END lesson-02-robot
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
|
| 456 |
+
# BEGIN lesson-03-objects
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 457 |
+
# Add the vial, rack, desk, and contact sensors in lesson 03.
|
|
|
|
| 458 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 459 |
+
# END lesson-03-objects
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 460 |
|
| 461 |
light = AssetBaseCfg(
|
| 462 |
prim_path="/World/Light",
|
|
|
|
| 469 |
)
|
| 470 |
|
| 471 |
|
| 472 |
+
# BEGIN lesson-04-actions
|
| 473 |
@configclass
|
| 474 |
class ActionsCfg:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
+
pass
|
| 476 |
+
# END lesson-04-actions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
|
| 478 |
|
| 479 |
+
# BEGIN lesson-05-policy-observations
|
| 480 |
@configclass
|
| 481 |
class PolicyStateGroupCfg(ObsGroup):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 482 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 483 |
+
# END lesson-05-policy-observations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 484 |
|
| 485 |
|
| 486 |
+
# BEGIN lesson-05-critic-observations
|
| 487 |
@configclass
|
| 488 |
class CriticStateGroupCfg(PolicyStateGroupCfg):
|
| 489 |
+
pass
|
|
|
|
| 490 |
+
# END lesson-05-critic-observations
|
| 491 |
|
| 492 |
|
| 493 |
+
# BEGIN lesson-05-observation-groups
|
| 494 |
@configclass
|
| 495 |
class ObservationsCfg:
|
| 496 |
+
pass
|
| 497 |
+
# END lesson-05-observation-groups
|
| 498 |
|
| 499 |
|
| 500 |
+
# BEGIN lesson-05-dataset-resets
|
| 501 |
@configclass
|
| 502 |
class EventsCfg:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 503 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
+
# END lesson-05-dataset-resets
|
|
|
|
|
|
|
|
|
|
|
|
|
| 505 |
|
| 506 |
|
| 507 |
def _material_event(asset_cfg: SceneEntityCfg, friction_range: tuple[float, float]) -> EventTerm:
|
|
|
|
| 581 |
)
|
| 582 |
|
| 583 |
|
| 584 |
+
# BEGIN lesson-05-rewards
|
| 585 |
@configclass
|
| 586 |
class RewardsCfg:
|
|
|
|
|
|
|
|
|
|
| 587 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 588 |
+
# END lesson-05-rewards
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
|
| 590 |
|
| 591 |
+
# BEGIN lesson-05-terminations
|
| 592 |
@configclass
|
| 593 |
class TerminationsCfg:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 594 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 595 |
+
# END lesson-05-terminations
|
| 596 |
|
| 597 |
|
| 598 |
@configclass
|
|
|
|
| 622 |
default = newton_mjwarp
|
| 623 |
|
| 624 |
|
| 625 |
+
# BEGIN lesson-environment
|
| 626 |
@configclass
|
| 627 |
class SO101VialEnvCfg(ManagerBasedRLEnvCfg):
|
| 628 |
+
"""Inspection scaffold; lesson 04 adds actions and lesson 05 adds training."""
|
| 629 |
|
| 630 |
+
scene: SO101SceneCfg = SO101SceneCfg(num_envs=1, env_spacing=0.9, replicate_physics=True)
|
| 631 |
+
actions: EmptyActionsCfg = EmptyActionsCfg()
|
| 632 |
+
observations: InspectionObservationsCfg = InspectionObservationsCfg()
|
|
|
|
|
|
|
| 633 |
+
events: LessonEventsCfg = LessonEventsCfg()
|
| 634 |
+
rewards = None
|
| 635 |
+
terminations = None
|
| 636 |
|
| 637 |
def __post_init__(self):
|
| 638 |
self.decimation = 4
|
|
|
|
|
|
|
| 639 |
+
self.episode_length_s = 20.0
|
| 640 |
self.sim.dt = 1.0 / 120.0
|
| 641 |
self.sim.render_interval = self.decimation
|
| 642 |
self.sim.physics = PhysicsCfg()
|
| 643 |
self.sim.default_visualizer_cfg = VisualizerCfg(eye=(0.64, 0.0, 0.36), lookat=(0.19, 0.02, 0.075))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 644 |
+
# END lesson-environment
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/isaaclab_tutorial/tasks/place_vial/config/so101/inspection_env_cfg.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Inspect the robot and scene before adding policy controls."""
|
| 2 |
+
|
| 3 |
+
from isaaclab.envs import ManagerBasedRLEnvCfg, VideoRecorderCfg
|
| 4 |
+
from isaaclab.utils.configclass import configclass
|
| 5 |
+
from isaaclab.visualizers import VisualizerCfg
|
| 6 |
+
|
| 7 |
+
from isaaclab_tasks.utils import preset
|
| 8 |
+
|
| 9 |
+
from .env_cfg import PhysicsCfg, SO101SceneCfg
|
| 10 |
+
from .lesson_scaffold import EmptyActionsCfg, InspectionObservationsCfg, LessonEventsCfg
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@configclass
|
| 14 |
+
class SO101InspectionEnvCfg(ManagerBasedRLEnvCfg):
|
| 15 |
+
scene: SO101SceneCfg = SO101SceneCfg(num_envs=1, env_spacing=0.9, replicate_physics=True)
|
| 16 |
+
actions: EmptyActionsCfg = EmptyActionsCfg()
|
| 17 |
+
observations: InspectionObservationsCfg = InspectionObservationsCfg()
|
| 18 |
+
events: LessonEventsCfg = LessonEventsCfg()
|
| 19 |
+
rewards = None
|
| 20 |
+
terminations = None
|
| 21 |
+
|
| 22 |
+
def __post_init__(self):
|
| 23 |
+
self.episode_length_s = 20.0
|
| 24 |
+
self.decimation = 4
|
| 25 |
+
self.sim.dt = 1.0 / 120.0
|
| 26 |
+
self.sim.render_interval = self.decimation
|
| 27 |
+
self.sim.physics = PhysicsCfg()
|
| 28 |
+
self.sim.default_visualizer_cfg = VisualizerCfg(eye=(0.64, 0.0, 0.36), lookat=(0.19, 0.02, 0.075))
|
| 29 |
+
self.video_recorders = preset(
|
| 30 |
+
default=[],
|
| 31 |
+
record_video=[VideoRecorderCfg(
|
| 32 |
+
source="visualizer:newton", output_dir="videos/inspection", video_length=120, fps=30,
|
| 33 |
+
)],
|
| 34 |
+
)
|
src/isaaclab_tutorial/tasks/place_vial/config/so101/lesson_scaffold.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Small runnable scene scaffold used before the teacher configuration is assembled."""
|
| 2 |
+
|
| 3 |
+
# Imports are supplied for the lesson snippets, including unfinished regions.
|
| 4 |
+
# ruff: noqa: F401
|
| 5 |
+
|
| 6 |
+
from isaaclab.managers import EventTermCfg as EventTerm
|
| 7 |
+
from isaaclab.managers import ObservationGroupCfg as ObsGroup
|
| 8 |
+
from isaaclab.managers import ObservationTermCfg as ObsTerm
|
| 9 |
+
from isaaclab.managers import SceneEntityCfg
|
| 10 |
+
from isaaclab.managers import TerminationTermCfg as DoneTerm
|
| 11 |
+
from isaaclab.utils.configclass import configclass
|
| 12 |
+
|
| 13 |
+
from isaaclab_tutorial.tasks.place_vial import mdp
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@configclass
|
| 17 |
+
class EmptyActionsCfg:
|
| 18 |
+
pass
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@configclass
|
| 22 |
+
class JointObservationsCfg(ObsGroup):
|
| 23 |
+
joint_pos = ObsTerm(func=mdp.joint_pos, params={"asset_cfg": SceneEntityCfg("robot")})
|
| 24 |
+
joint_vel = ObsTerm(func=mdp.joint_vel, params={"asset_cfg": SceneEntityCfg("robot")})
|
| 25 |
+
|
| 26 |
+
def __post_init__(self):
|
| 27 |
+
self.enable_corruption = False
|
| 28 |
+
self.concatenate_terms = True
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@configclass
|
| 32 |
+
class InspectionObservationsCfg:
|
| 33 |
+
policy: JointObservationsCfg = JointObservationsCfg()
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
# BEGIN lesson-04-AgentObservationsCfg
|
| 37 |
+
@configclass
|
| 38 |
+
class AgentObservationsCfg(JointObservationsCfg):
|
| 39 |
+
pass
|
| 40 |
+
# END lesson-04-AgentObservationsCfg
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
@configclass
|
| 44 |
+
class AgentObservationGroupsCfg:
|
| 45 |
+
policy: AgentObservationsCfg = AgentObservationsCfg()
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
@configclass
|
| 49 |
+
class LessonEventsCfg:
|
| 50 |
+
reset_scene = EventTerm(func=mdp.reset_scene_to_default, mode="reset", params={"reset_joint_targets": True})
|
| 51 |
+
|
| 52 |
+
# BEGIN lesson-03-vial-mass
|
| 53 |
+
pass
|
| 54 |
+
# END lesson-03-vial-mass
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
# BEGIN lesson-04-LessonTerminationsCfg
|
| 58 |
+
@configclass
|
| 59 |
+
class LessonTerminationsCfg:
|
| 60 |
+
pass
|
| 61 |
+
# END lesson-04-LessonTerminationsCfg
|
tests/test_config.py
CHANGED
|
@@ -42,8 +42,14 @@ def _camera(*presets):
|
|
| 42 |
|
| 43 |
def test_tasks_are_registered_with_rsl_rl_entry_points():
|
| 44 |
ids = sorted(task for task in gym.registry if task.startswith("IsaacTutorial-"))
|
|
|
|
|
|
|
|
|
|
| 45 |
-
|
|
|
|
| 46 |
-
state, camera = (gym.spec(task).kwargs for task in ids)
|
|
|
|
|
|
|
| 47 |
assert state["env_cfg_entry_point"].endswith("env_cfg:SO101VialEnvCfg")
|
| 48 |
assert state["rsl_rl_cfg_entry_point"].endswith("rsl_rl_ppo_cfg:SO101StatePPORunnerCfg")
|
| 49 |
assert camera["env_cfg_entry_point"].endswith("camera_env_cfg:SO101VialCameraEnvCfg")
|
|
|
|
| 42 |
|
| 43 |
def test_tasks_are_registered_with_rsl_rl_entry_points():
|
| 44 |
ids = sorted(task for task in gym.registry if task.startswith("IsaacTutorial-"))
|
| 45 |
+
assert ids == [
|
| 46 |
+
"IsaacTutorial-Inspect-SO101",
|
| 47 |
+
"IsaacTutorial-Place-Vial-SO101",
|
| 48 |
+
"IsaacTutorial-Place-Vial-SO101-Camera",
|
| 49 |
+
]
|
| 50 |
+
inspection, state, camera = (gym.spec(task).kwargs for task in ids)
|
| 51 |
+
assert inspection["env_cfg_entry_point"].endswith("inspection_env_cfg:SO101InspectionEnvCfg")
|
| 52 |
+
assert "rsl_rl_cfg_entry_point" not in inspection
|
| 53 |
assert state["env_cfg_entry_point"].endswith("env_cfg:SO101VialEnvCfg")
|
| 54 |
assert state["rsl_rl_cfg_entry_point"].endswith("rsl_rl_ppo_cfg:SO101StatePPORunnerCfg")
|
| 55 |
assert camera["env_cfg_entry_point"].endswith("camera_env_cfg:SO101VialCameraEnvCfg")
|