Skip to content

Commit 970a1cf

Browse files
committed
Add manager-based counterparts for the reorientation tasks
Rebuilt from the reviewed lump so the manager counterparts land with the review round folded in: - Sim settings come from the ShadowHandTaskCfgBase/OpenAI and Allegro task-cfg mixins instead of module-level cfg variables. - Backend scene presets declare only backend-specific fields; shared invariants live as inner-class defaults. - Legacy manager reward terms carry deprecation directives and one-shot DeprecationWarnings pointing at ReorientReward. - The manager terms are named ReorientReward and ReorientTimeout; the Direct-parity contract is stated in their docstrings. - The orientation error comes from isaaclab.utils.math.quat_error_magnitude. Lump review commits folded: 2c22af0, 792e400, 42675b6, 1f05f85, c6140f9, 173e9dc.
1 parent 21dbb17 commit 970a1cf

13 files changed

Lines changed: 1339 additions & 552 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Added
2+
^^^^^
3+
4+
* Added manager-based counterparts for the Allegro and Shadow cube
5+
reorientation tasks (state and OpenAI FF/LSTM observation variants), sharing
6+
the Direct tasks' scalar parameters and boolean success metrics through
7+
common MDP terms.
8+
* Added opt-in domain randomization to the manager-based Allegro environment
9+
(``enable_domain_randomization``, disabled by default; enabling requires
10+
retraining).
11+
* Added a Newton physics preset to the manager-based Allegro environment.
12+
13+
Removed
14+
^^^^^^^
15+
16+
* Removed the legacy manager-based reorientation configuration
17+
``ReorientObjectEnvCfg`` and the manager terms only it consumed
18+
(``success_bonus``, ``track_pos_l2``, ``track_orientation_inv_l2``,
19+
``max_consecutive_success``, ``object_away_from_goal``, and
20+
``object_away_from_robot``). Use the Direct-compatible manager
21+
configurations and terms instead (e.g.
22+
:class:`~isaaclab_tasks.core.reorient.mdp.ReorientReward` and
23+
:class:`~isaaclab_tasks.core.reorient.mdp.ReorientTimeout`).
24+
25+
Changed
26+
^^^^^^^
27+
28+
* **Breaking:** Changed the manager-based Allegro reorientation environment to
29+
match the Direct observation, action, reward, reset, termination, success,
30+
asset, and benchmark contracts. Existing manager checkpoints are
31+
incompatible and must be retrained.

source/isaaclab_tasks/isaaclab_tasks/core/reorient/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88
This package consolidates the direct-workflow and manager-based-workflow in-hand
99
manipulation tasks, where a dexterous hand reorients an object to match a goal
1010
orientation. The shared direct base environment lives in
11-
:mod:`~isaaclab_tasks.core.reorient.reorient_direct_env` and the shared manager-based
12-
base configuration in :mod:`~isaaclab_tasks.core.reorient.reorient_manager_env_cfg`.
13-
Robot-specific tasks are organized under the ``config`` subpackage
14-
(``config/allegro_hand`` and ``config/shadow_hand``).
11+
:mod:`~isaaclab_tasks.core.reorient.reorient_direct_env`; the manager-based
12+
configurations live with their robot-specific tasks under the ``config``
13+
subpackage (``config/allegro_hand`` and ``config/shadow_hand``).
1514
1615
These environments are based on the `dexterous cube manipulation`_ environments
1716
provided in IsaacGymEnvs repository from NVIDIA. However, they contain certain

source/isaaclab_tasks/isaaclab_tasks/core/reorient/config/allegro_hand/allegro_hand_manager_env_cfg.py

Lines changed: 297 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,315 @@
33
#
44
# SPDX-License-Identifier: BSD-3-Clause
55

6+
"""Manager-based counterpart of the Allegro Hand Direct reorientation task."""
7+
8+
import isaaclab.sim as sim_utils
9+
from isaaclab.assets import ArticulationCfg, AssetBaseCfg
10+
from isaaclab.envs import ManagerBasedRLEnvCfg
11+
from isaaclab.managers import EventTermCfg as EventTerm
12+
from isaaclab.managers import ObservationGroupCfg as ObsGroup
13+
from isaaclab.managers import ObservationTermCfg as ObsTerm
14+
from isaaclab.managers import RewardTermCfg as RewTerm
15+
from isaaclab.managers import SceneEntityCfg
16+
from isaaclab.managers import TerminationTermCfg as DoneTerm
17+
from isaaclab.scene import InteractiveSceneCfg
618
from isaaclab.utils.configclass import configclass
719

8-
from isaaclab_tasks.core.reorient.reorient_manager_env_cfg import ReorientObjectEnvCfg
20+
import isaaclab_tasks.core.reorient.mdp as mdp
21+
from isaaclab_tasks.core.reorient.config.allegro_hand.allegro_hand_direct_env_cfg import (
22+
GOAL_OBJECT_CFG,
23+
OBJECT_CFG,
24+
ROBOT_CFG,
25+
AllegroHandTaskCfgBase,
26+
ObjectCfg,
27+
)
28+
from isaaclab_tasks.core.reorient.reorient_task_base import (
29+
ALLEGRO_ACTUATED_JOINT_NAMES,
30+
ALLEGRO_FINGERTIP_BODY_NAMES,
31+
GOAL_MARKER_POSITION,
32+
IN_HAND_POS_OFFSET,
33+
)
34+
from isaaclab_tasks.utils import PresetCfg
35+
36+
37+
@configclass
38+
class AllegroCubeSceneCfg(PresetCfg):
39+
"""Backend-specific scene cloning settings matching the Direct task."""
40+
41+
@configclass
42+
class SceneCfg(InteractiveSceneCfg):
43+
"""Allegro scene shared by the backend alternatives."""
44+
45+
num_envs = 8192
46+
env_spacing = 0.75
47+
replicate_physics = True
948

10-
##
11-
# Pre-defined configs
12-
##
13-
from isaaclab_assets import ALLEGRO_HAND_CFG # isort: skip
49+
ground = AssetBaseCfg(prim_path="/World/ground", spawn=sim_utils.GroundPlaneCfg())
50+
robot: ArticulationCfg = ROBOT_CFG
51+
object: ObjectCfg = OBJECT_CFG
52+
light = AssetBaseCfg(
53+
prim_path="/World/Light",
54+
spawn=sim_utils.DomeLightCfg(intensity=2000.0, color=(0.75, 0.75, 0.75)),
55+
)
56+
57+
physx = SceneCfg(clone_in_fabric=True)
58+
newton_mjwarp = SceneCfg(clone_in_fabric=False)
59+
ovphysx = SceneCfg(clone_in_fabric=True)
60+
default = physx
61+
62+
def set_num_envs(self, num_envs: int) -> None:
63+
"""Set the environment count on every backend alternative."""
64+
self.physx.num_envs = num_envs
65+
self.newton_mjwarp.num_envs = num_envs
66+
self.ovphysx.num_envs = num_envs
67+
self.default.num_envs = num_envs
1468

1569

1670
@configclass
17-
class AllegroCubeEnvCfg(ReorientObjectEnvCfg):
18-
def __post_init__(self):
19-
# post init of parent
20-
super().__post_init__()
71+
class CommandsCfg:
72+
"""Object pose goal matching the Direct in-hand target."""
73+
74+
object_pose = mdp.ReorientEpisodeCommandCfg(
75+
asset_name="object",
76+
init_pos_offset=IN_HAND_POS_OFFSET,
77+
update_goal_on_success=True,
78+
orientation_success_threshold=0.2,
79+
make_quat_unique=False,
80+
fixed_marker_pos=GOAL_MARKER_POSITION,
81+
goal_pose_visualizer_cfg=GOAL_OBJECT_CFG,
82+
debug_vis=True,
83+
)
84+
85+
86+
@configclass
87+
class ActionsCfg:
88+
"""Sixteen actuated Allegro Hand joints in Direct order."""
89+
90+
joint_pos = mdp.EMAJointPositionToLimitsActionCfg(
91+
asset_name="robot",
92+
joint_names=ALLEGRO_ACTUATED_JOINT_NAMES,
93+
alpha=1.0,
94+
rescale_to_limits=True,
95+
)
96+
97+
98+
@configclass
99+
class ObservationsCfg:
100+
"""Full 124-dimensional state observation in Direct order."""
101+
102+
@configclass
103+
class PolicyCfg(ObsGroup):
104+
joint_pos = ObsTerm(
105+
func=mdp.joint_pos_limit_normalized,
106+
params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*", preserve_order=False)},
107+
)
108+
joint_vel = ObsTerm(
109+
func=mdp.joint_vel,
110+
scale=0.2,
111+
params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*", preserve_order=False)},
112+
)
113+
object_pos = ObsTerm(func=mdp.root_pos_w, params={"asset_cfg": SceneEntityCfg("object")})
114+
object_quat = ObsTerm(
115+
func=mdp.root_quat_w,
116+
params={"asset_cfg": SceneEntityCfg("object"), "make_quat_unique": False},
117+
)
118+
object_lin_vel = ObsTerm(func=mdp.root_lin_vel_w, params={"asset_cfg": SceneEntityCfg("object")})
119+
object_ang_vel = ObsTerm(
120+
func=mdp.root_ang_vel_w,
121+
scale=0.2,
122+
params={"asset_cfg": SceneEntityCfg("object")},
123+
)
124+
goal_pose = ObsTerm(func=mdp.generated_commands, params={"command_name": "object_pose"})
125+
goal_quat_diff = ObsTerm(
126+
func=mdp.goal_quat_diff,
127+
params={"asset_cfg": SceneEntityCfg("object"), "command_name": "object_pose", "make_quat_unique": False},
128+
)
129+
fingertip_pos = ObsTerm(
130+
func=mdp.fingertip_pos,
131+
params={
132+
"asset_cfg": SceneEntityCfg("robot", body_names=ALLEGRO_FINGERTIP_BODY_NAMES, preserve_order=False)
133+
},
134+
)
135+
fingertip_quat = ObsTerm(
136+
func=mdp.fingertip_quat,
137+
params={
138+
"asset_cfg": SceneEntityCfg("robot", body_names=ALLEGRO_FINGERTIP_BODY_NAMES, preserve_order=False)
139+
},
140+
)
141+
fingertip_vel = ObsTerm(
142+
func=mdp.fingertip_vel,
143+
params={
144+
"asset_cfg": SceneEntityCfg("robot", body_names=ALLEGRO_FINGERTIP_BODY_NAMES, preserve_order=False)
145+
},
146+
)
147+
last_action = ObsTerm(func=mdp.reorient_last_action, params={"action_name": "joint_pos"})
148+
149+
def __post_init__(self):
150+
self.enable_corruption = False
151+
self.concatenate_terms = True
152+
153+
policy: PolicyCfg = PolicyCfg()
154+
21155

22-
# switch robot to allegro hand
23-
self.scene.robot = ALLEGRO_HAND_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
24-
# enable clone in fabric
25-
self.scene.clone_in_fabric = True
156+
@configclass
157+
class EventCfg:
158+
"""Reset distributions matching the Direct task, plus opt-in domain randomization.
159+
160+
The domain-randomization terms reproduce the legacy manager recipe. They are
161+
startup-mode terms and are dropped by default (see
162+
:attr:`AllegroCubeEnvCfg.enable_domain_randomization`): the Direct task has no
163+
domain randomization, and the validated benchmark thresholds were calibrated
164+
without it. Enabling them requires retraining.
165+
"""
166+
167+
# -- opt-in domain randomization (legacy manager recipe parameters)
168+
robot_physics_material = EventTerm(
169+
func=mdp.randomize_rigid_body_material,
170+
mode="startup",
171+
params={
172+
"asset_cfg": SceneEntityCfg("robot", body_names=".*"),
173+
"static_friction_range": (0.7, 1.3),
174+
"dynamic_friction_range": (0.7, 1.3),
175+
"restitution_range": (0.0, 0.0),
176+
"num_buckets": 250,
177+
},
178+
)
179+
robot_scale_mass = EventTerm(
180+
func=mdp.randomize_rigid_body_mass,
181+
mode="startup",
182+
params={
183+
"asset_cfg": SceneEntityCfg("robot", body_names=".*"),
184+
"mass_distribution_params": (0.95, 1.05),
185+
"operation": "scale",
186+
},
187+
)
188+
robot_joint_stiffness_and_damping = EventTerm(
189+
func=mdp.randomize_actuator_gains,
190+
mode="startup",
191+
params={
192+
"asset_cfg": SceneEntityCfg("robot", joint_names=".*"),
193+
"stiffness_distribution_params": (0.3, 3.0),
194+
"damping_distribution_params": (0.75, 1.5),
195+
"operation": "scale",
196+
"distribution": "log_uniform",
197+
},
198+
)
199+
object_physics_material = EventTerm(
200+
func=mdp.randomize_rigid_body_material,
201+
mode="startup",
202+
params={
203+
"asset_cfg": SceneEntityCfg("object", body_names=".*"),
204+
"static_friction_range": (0.7, 1.3),
205+
"dynamic_friction_range": (0.7, 1.3),
206+
"restitution_range": (0.0, 0.0),
207+
"num_buckets": 250,
208+
},
209+
)
210+
object_scale_mass = EventTerm(
211+
func=mdp.randomize_rigid_body_mass,
212+
mode="startup",
213+
params={
214+
"asset_cfg": SceneEntityCfg("object"),
215+
"mass_distribution_params": (0.4, 1.6),
216+
"operation": "scale",
217+
},
218+
)
219+
220+
reset_state = EventTerm(
221+
func=mdp.reset_reorient_state,
222+
mode="reset",
223+
params={
224+
"position_noise": 0.01,
225+
"joint_position_noise": 0.2,
226+
"joint_velocity_noise": 0.0,
227+
"action_name": "joint_pos",
228+
},
229+
)
230+
231+
232+
@configclass
233+
class RewardsCfg:
234+
"""Direct-compatible reward and success accounting."""
235+
236+
reorient = RewTerm(
237+
func=mdp.ReorientReward,
238+
weight=1.0,
239+
params={
240+
"command_name": "object_pose",
241+
"distance_scale": -10.0,
242+
"rotation_scale": 1.0,
243+
"rotation_epsilon": 0.1,
244+
"action_penalty_scale": -0.0002,
245+
"success_tolerance": 0.2,
246+
"success_bonus": 250.0,
247+
"fall_distance": 0.24,
248+
"fall_penalty": 0.0,
249+
"averaging_factor": 0.1,
250+
"success_count_threshold": 1,
251+
"object_cfg": SceneEntityCfg("object"),
252+
},
253+
)
254+
255+
256+
@configclass
257+
class TerminationsCfg:
258+
"""Termination conditions matching the Direct task."""
259+
260+
object_out_of_reach = DoneTerm(
261+
func=mdp.object_reorientation_out_of_reach,
262+
params={
263+
"threshold": 0.24,
264+
"command_name": "object_pose",
265+
"object_cfg": SceneEntityCfg("object"),
266+
},
267+
)
268+
time_out = DoneTerm(func=mdp.time_out, time_out=True)
269+
270+
271+
@configclass
272+
class AllegroCubeEnvCfg(AllegroHandTaskCfgBase, ManagerBasedRLEnvCfg):
273+
"""Manager-based Allegro Hand task with Direct-compatible semantics."""
274+
275+
scene: AllegroCubeSceneCfg = AllegroCubeSceneCfg()
276+
observations: ObservationsCfg = ObservationsCfg()
277+
actions: ActionsCfg = ActionsCfg()
278+
commands: CommandsCfg = CommandsCfg()
279+
rewards: RewardsCfg = RewardsCfg()
280+
terminations: TerminationsCfg = TerminationsCfg()
281+
events: EventCfg = EventCfg()
282+
283+
enable_domain_randomization: bool = False
284+
"""Enable the legacy startup domain-randomization terms.
285+
286+
Disabled by default: the validated reference training runs and the benchmark
287+
thresholds were produced without domain randomization, so enabling it
288+
requires retraining and threshold recalibration.
289+
"""
290+
291+
_DOMAIN_RANDOMIZATION_TERMS = (
292+
"robot_physics_material",
293+
"robot_scale_mass",
294+
"robot_joint_stiffness_and_damping",
295+
"object_physics_material",
296+
"object_scale_mass",
297+
)
298+
299+
def __post_init__(self):
300+
self.decimation = 4
301+
self.episode_length_s = 10.0
302+
self.sim.render_interval = self.decimation
303+
self.viewer.eye = (2.0, 2.0, 2.0)
304+
if not self.enable_domain_randomization:
305+
for term_name in self._DOMAIN_RANDOMIZATION_TERMS:
306+
setattr(self.events, term_name, None)
26307

27308

28309
@configclass
29310
class AllegroCubeEnvCfg_PLAY(AllegroCubeEnvCfg):
311+
"""Reduced, deterministic play configuration."""
312+
30313
def __post_init__(self):
31-
# post init of parent
32314
super().__post_init__()
33-
# make a smaller scene for play
34-
self.scene.num_envs = 50
35-
# disable randomization for play
315+
self.scene.set_num_envs(50)
36316
self.observations.policy.enable_corruption = False
37-
# remove termination due to timeouts
38317
self.terminations.time_out = None

0 commit comments

Comments
 (0)