Skip to content

Commit 9f1c465

Browse files
committed
Add Newton backend support for Shadow-Hand-Over (MAPPO)
Port ``Isaac-Shadow-Hand-Over-Direct-v0`` to dual PhysX/Newton backend, mirroring the single-agent Shadow Hand Newton port pattern. Selectable via ``--preset newton`` / Hydra preset resolution; PhysX behavior unchanged. The Newton variant of the Shadow Hand articulation is built as a delta of the single-agent ``ShadowHandRobotCfg.newton`` (cross-task import), parameterized per-robot ``prim_path``/init pose, with two ``ImplicitActuatorCfg`` overrides: * ``fingers`` (wrist + per-finger joints): ``stiffness=20.0`` / ``damping=2.0``. PhysX uses ``5.0`` / ``0.5`` on wrists and ``1.0`` / ``0.1`` on fingers and layers ``fixed_tendons_props(limit_stiffness=30, damping=0.1)`` plus ``solver_position_iteration_count=8`` — both amplify effective torque per unit nominal gain. Newton's MJWarp implicit-PD path has neither, so larger nominal gains are needed for comparable joint authority. * ``distal_passive`` (the four ``robot0_(FF|MF|RF|LF)J0`` joints): ``stiffness=10.0`` / ``damping=0.1``. The Newton USD bakes ``stiffness=286`` / ``damping=57`` on these joints from the MJCF→USD translation, which fights the ``MjcTendon`` coupling and bounces the ball. ``stiffness=10`` keeps the joints near-passive while the tendon constraint dominates. Object, scene, and physics presets follow the established ``PresetCfg(physx=..., newton=..., default=physx)`` pattern. Newton's ``ObjectCfg`` drops PhysX-only ``rigid_props`` knobs (per-shape solver iterations, sleep thresholds, max depenetration velocity, custom physics material). Newton's scene cloning sets ``clone_in_fabric=False``. The Newton physics preset is ``NewtonCfg(MJWarpSolverCfg(...))`` mirroring the single-agent configuration. Bumps ``isaaclab_tasks`` 1.5.33 → 1.5.34 with one CHANGELOG entry.
1 parent 2ab361e commit 9f1c465

3 files changed

Lines changed: 238 additions & 42 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
Added
2+
^^^^^
3+
4+
* Added Newton backend support for the multi-agent
5+
``Isaac-Shadow-Hand-Over-Direct-v0`` (MAPPO/IPPO) env. Mirrors the
6+
single-agent Shadow Hand Newton port: per-hand
7+
:class:`ImplicitActuatorCfg`, ``shadow_hand_instanceable_newton.usd``,
8+
per-backend :class:`~isaaclab_tasks.utils.PresetCfg` wrappers for sim
9+
physics, scene cloning (``clone_in_fabric=False`` on Newton), the
10+
hand-over object (``RigidObjectCfg`` on both backends, dropping
11+
PhysX-only knobs on Newton), and the two robot configs. Selectable via
12+
``--preset newton`` / Hydra preset resolution; PhysX behavior unchanged.
13+
14+
Fixed
15+
^^^^^
16+
17+
* Fixed Newton training failing to learn the catch in
18+
``Isaac-Shadow-Hand-Over-Direct-v0`` MAPPO. Two Newton-side
19+
:class:`~isaaclab.actuators.ImplicitActuatorCfg` overrides are added:
20+
21+
* ``fingers`` (wrist + per-finger joints): ``stiffness=20.0`` /
22+
``damping=2.0``, vs PhysX's ``5.0`` / ``0.5`` on wrists and
23+
``1.0`` / ``0.1`` on fingers. PhysX layers
24+
``fixed_tendons_props(limit_stiffness=30, damping=0.1)`` on top of
25+
the implicit drive and runs ``solver_position_iteration_count=8``
26+
per substep — both amplify the effective torque per unit nominal
27+
gain. Newton's MJWarp implicit-PD path has neither, so larger
28+
nominal gains are needed for comparable joint authority.
29+
* ``distal_passive`` (the four ``robot0_(FF|MF|RF|LF)J0`` joints):
30+
``stiffness=10.0`` / ``damping=0.1``. The Newton USD bakes
31+
``stiffness=286`` / ``damping=57`` on these joints from the
32+
MJCF→USD translation, which fights the ``MjcTendon`` coupling and
33+
bounces the ball. ``stiffness=10`` (~1/3 of PhysX
34+
``limit_stiffness=30``) keeps the joints near-passive while the
35+
tendon constraint dominates. PhysX uses tendon coupling on these
36+
joints directly and does not need an analogous override.
37+
38+
At iter 200 / 2048 envs, MAPPO ``Reward / Total reward (mean)``:
39+
PhysX baseline **246.7**, Newton at ``stiffness=1.0`` / ``damping=0.1``
40+
(no catch learned) **23.4**, Newton at the new gains **777.1**.
41+
Newton learns the catch reliably; longer runs and behavior-level
42+
comparison (catch / drop rate, ball trajectory) are follow-ups.
43+
PhysX path is unchanged.

source/isaaclab_tasks/isaaclab_tasks/direct/shadow_hand_over/shadow_hand_over_env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def __init__(self, cfg: ShadowHandOverEnvCfg, render_mode: str | None = None, **
6464
self.num_fingertips = len(self.finger_bodies)
6565

6666
# joint limits
67-
joint_pos_limits = wp.to_torch(self.right_hand.root_view.get_dof_limits()).to(self.device)
67+
joint_pos_limits = wp.to_torch(self.right_hand.data.joint_limits).to(self.device)
6868
self.hand_dof_lower_limits = joint_pos_limits[..., 0]
6969
self.hand_dof_upper_limits = joint_pos_limits[..., 1]
7070

source/isaaclab_tasks/isaaclab_tasks/direct/shadow_hand_over/shadow_hand_over_env_cfg.py

Lines changed: 194 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
#
44
# SPDX-License-Identifier: BSD-3-Clause
55

6+
from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg
67
from isaaclab_physx.physics import PhysxCfg
78

89
import isaaclab.envs.mdp as mdp
910
import isaaclab.sim as sim_utils
11+
from isaaclab.actuators import ImplicitActuatorCfg
1012
from isaaclab.assets import ArticulationCfg, RigidObjectCfg
1113
from isaaclab.envs import DirectMARLEnvCfg
1214
from isaaclab.managers import EventTermCfg as EventTerm
@@ -17,12 +19,21 @@
1719
from isaaclab.sim.spawners.materials.physics_materials_cfg import RigidBodyMaterialCfg
1820
from isaaclab.utils import configclass
1921

22+
from isaaclab_tasks.direct.shadow_hand.shadow_hand_env_cfg import ShadowHandRobotCfg
23+
from isaaclab_tasks.utils import PresetCfg
24+
2025
from isaaclab_assets.robots.shadow_hand import SHADOW_HAND_CFG
2126

2227

2328
@configclass
2429
class EventCfg:
25-
"""Configuration for randomization."""
30+
"""Configuration for randomization (PhysX path).
31+
32+
Note: this config is currently not wired into ``ShadowHandOverEnvCfg.events`` -
33+
it is kept as a reference for future event-randomization work. The event
34+
terms here use PhysX-only APIs (rigid-body materials, fixed tendons), so
35+
they would need a Newton variant before being enabled in the env.
36+
"""
2637

2738
# -- robot
2839
robot_physics_material = EventTerm(
@@ -113,6 +124,182 @@ class EventCfg:
113124
)
114125

115126

127+
# Reuse the single-agent Shadow Hand Newton port (USD path, ``rot`` reapplication
128+
# workaround, effort limits, joint regex). The multi-agent variant only diverges
129+
# in actuator gains (stiffness/damping bumped for the catch task) and adds a
130+
# ``distal_passive`` override for the J0 USD-baked values.
131+
_SHADOW_HAND_NEWTON_CFG = ShadowHandRobotCfg().newton
132+
133+
134+
def _newton_shadow_hand_cfg(
135+
prim_path: str, init_pos: tuple[float, float, float], init_rot: tuple[float, float, float, float]
136+
) -> ArticulationCfg:
137+
"""Newton Shadow Hand cfg parameterized by per-robot ``prim_path`` and init pose.
138+
139+
Two overrides versus the single-agent Newton port:
140+
141+
* ``fingers`` actuator: ``stiffness=20.0`` / ``damping=2.0`` (vs PhysX's
142+
``5.0`` / ``0.5`` on wrists and ``1.0`` / ``0.1`` on fingers). PhysX layers
143+
``fixed_tendons_props(limit_stiffness=30, damping=0.1)`` and runs
144+
``solver_position_iteration_count=8`` per substep — both amplify the
145+
effective torque per unit nominal gain. Newton's MJWarp implicit-PD path
146+
has neither, so a larger nominal gain is needed for comparable joint
147+
authority. ``20.0`` / ``2.0`` is the smallest tested setting at which
148+
MAPPO learns the catch (mean reward at iter 200 / 2048 envs goes from
149+
~27 at PhysX-mirrored gains to ~777).
150+
* ``distal_passive`` actuator on the four ``robot0_(FF|MF|RF|LF)J0`` joints
151+
with ``stiffness=10.0`` / ``damping=0.1``. The Newton USD bakes
152+
``stiffness=286 / damping=57`` on these joints from the MJCF→USD
153+
translation, which fights the ``MjcTendon`` coupling and bounces the
154+
ball. ``stiffness=10`` (1/3 of PhysX ``limit_stiffness=30``) keeps the
155+
joints near-passive while the tendon constraint dominates.
156+
"""
157+
return _SHADOW_HAND_NEWTON_CFG.replace(
158+
prim_path=prim_path,
159+
init_state=_SHADOW_HAND_NEWTON_CFG.init_state.replace(pos=init_pos, rot=init_rot),
160+
actuators={
161+
"fingers": _SHADOW_HAND_NEWTON_CFG.actuators["fingers"].replace(stiffness=20.0, damping=2.0),
162+
"distal_passive": ImplicitActuatorCfg(
163+
joint_names_expr=["robot0_(FF|MF|RF|LF)J0"],
164+
stiffness=10.0,
165+
damping=0.1,
166+
friction=1e-2,
167+
armature=2e-3,
168+
),
169+
},
170+
)
171+
172+
173+
@configclass
174+
class RightRobotCfg(PresetCfg):
175+
physx = SHADOW_HAND_CFG.replace(prim_path="/World/envs/env_.*/RightRobot").replace(
176+
init_state=ArticulationCfg.InitialStateCfg(
177+
pos=(0.0, 0.0, 0.5),
178+
rot=(0.0, 0.0, 0.0, 1.0),
179+
joint_pos={".*": 0.0},
180+
)
181+
)
182+
newton = _newton_shadow_hand_cfg(
183+
prim_path="/World/envs/env_.*/RightRobot",
184+
init_pos=(0.0, 0.0, 0.5),
185+
init_rot=(0.0, 0.0, 0.0, 1.0),
186+
)
187+
default = physx
188+
189+
190+
@configclass
191+
class LeftRobotCfg(PresetCfg):
192+
physx = SHADOW_HAND_CFG.replace(prim_path="/World/envs/env_.*/LeftRobot").replace(
193+
init_state=ArticulationCfg.InitialStateCfg(
194+
pos=(0.0, -1.0, 0.5),
195+
rot=(0.0, 0.0, 1.0, 0.0),
196+
joint_pos={".*": 0.0},
197+
)
198+
)
199+
newton = _newton_shadow_hand_cfg(
200+
prim_path="/World/envs/env_.*/LeftRobot",
201+
init_pos=(0.0, -1.0, 0.5),
202+
init_rot=(0.0, 0.0, 1.0, 0.0),
203+
)
204+
default = physx
205+
206+
207+
@configclass
208+
class ObjectCfg(PresetCfg):
209+
"""Hand-over object preset.
210+
211+
Both backends spawn the same procedural sphere as a free rigid body:
212+
Newton's :class:`~isaaclab_newton.assets.RigidObject` resolves the
213+
asset via the ``UsdPhysics.RigidBodyAPI`` that
214+
:class:`~isaaclab.sim.RigidBodyPropertiesCfg` applies. The Newton
215+
variant drops PhysX-only knobs (per-shape solver iterations, sleep
216+
thresholds, max depenetration velocity, custom physics material).
217+
"""
218+
219+
physx = RigidObjectCfg(
220+
prim_path="/World/envs/env_.*/object",
221+
spawn=sim_utils.SphereCfg(
222+
radius=0.0335,
223+
visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.8, 1.0, 0.0)),
224+
physics_material=sim_utils.RigidBodyMaterialCfg(static_friction=0.7),
225+
rigid_props=sim_utils.RigidBodyPropertiesCfg(
226+
kinematic_enabled=False,
227+
disable_gravity=False,
228+
enable_gyroscopic_forces=True,
229+
solver_position_iteration_count=8,
230+
solver_velocity_iteration_count=0,
231+
sleep_threshold=0.005,
232+
stabilization_threshold=0.0025,
233+
max_depenetration_velocity=1000.0,
234+
),
235+
collision_props=sim_utils.CollisionPropertiesCfg(),
236+
mass_props=sim_utils.MassPropertiesCfg(density=500.0),
237+
),
238+
init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, -0.39, 0.54), rot=(0.0, 0.0, 0.0, 1.0)),
239+
)
240+
newton = RigidObjectCfg(
241+
prim_path="/World/envs/env_.*/object",
242+
spawn=sim_utils.SphereCfg(
243+
radius=0.0335,
244+
visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.8, 1.0, 0.0)),
245+
rigid_props=sim_utils.RigidBodyPropertiesCfg(
246+
kinematic_enabled=False,
247+
disable_gravity=False,
248+
enable_gyroscopic_forces=True,
249+
),
250+
collision_props=sim_utils.CollisionPropertiesCfg(),
251+
mass_props=sim_utils.MassPropertiesCfg(density=500.0),
252+
),
253+
init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, -0.39, 0.54), rot=(0.0, 0.0, 0.0, 1.0)),
254+
)
255+
default = physx
256+
257+
258+
@configclass
259+
class ShadowHandOverSceneCfg(PresetCfg):
260+
"""Scene preset.
261+
262+
PhysX supports ``clone_in_fabric=True`` for faster cloning. Newton's
263+
cloning path does not, so the Newton variant disables Fabric cloning.
264+
"""
265+
266+
physx: InteractiveSceneCfg = InteractiveSceneCfg(
267+
num_envs=2048, env_spacing=1.5, replicate_physics=True, clone_in_fabric=True
268+
)
269+
newton: InteractiveSceneCfg = InteractiveSceneCfg(
270+
num_envs=2048, env_spacing=1.5, replicate_physics=True, clone_in_fabric=False
271+
)
272+
default: InteractiveSceneCfg = physx
273+
274+
275+
@configclass
276+
class PhysicsCfg(PresetCfg):
277+
"""Physics-backend preset (PhysX vs Newton/MJWarp).
278+
279+
Newton settings mirror the single-agent ShadowHand Newton port: elliptic
280+
cone, ``impratio=10`` (favors normal contacts over friction), 100 solver
281+
iterations, 2 substeps. Empirically converges on the single-agent ShadowHand
282+
tasks; tuning may be needed for handover-specific contact dynamics.
283+
"""
284+
285+
physx = PhysxCfg(bounce_threshold_velocity=0.2)
286+
newton = NewtonCfg(
287+
solver_cfg=MJWarpSolverCfg(
288+
solver="newton",
289+
integrator="implicitfast",
290+
njmax=200,
291+
nconmax=70,
292+
impratio=10.0,
293+
cone="elliptic",
294+
update_data_interval=2,
295+
iterations=100,
296+
),
297+
num_substeps=2,
298+
debug_mode=False,
299+
)
300+
default = physx
301+
302+
116303
@configclass
117304
class ShadowHandOverEnvCfg(DirectMARLEnvCfg):
118305
# env
@@ -131,25 +318,11 @@ class ShadowHandOverEnvCfg(DirectMARLEnvCfg):
131318
static_friction=1.0,
132319
dynamic_friction=1.0,
133320
),
134-
physics=PhysxCfg(
135-
bounce_threshold_velocity=0.2,
136-
),
321+
physics=PhysicsCfg(),
137322
)
138323
# robot
139-
right_robot_cfg: ArticulationCfg = SHADOW_HAND_CFG.replace(prim_path="/World/envs/env_.*/RightRobot").replace(
140-
init_state=ArticulationCfg.InitialStateCfg(
141-
pos=(0.0, 0.0, 0.5),
142-
rot=(0.0, 0.0, 0.0, 1.0),
143-
joint_pos={".*": 0.0},
144-
)
145-
)
146-
left_robot_cfg: ArticulationCfg = SHADOW_HAND_CFG.replace(prim_path="/World/envs/env_.*/LeftRobot").replace(
147-
init_state=ArticulationCfg.InitialStateCfg(
148-
pos=(0.0, -1.0, 0.5),
149-
rot=(0.0, 0.0, 1.0, 0.0),
150-
joint_pos={".*": 0.0},
151-
)
152-
)
324+
right_robot_cfg: RightRobotCfg = RightRobotCfg()
325+
left_robot_cfg: LeftRobotCfg = LeftRobotCfg()
153326
actuated_joint_names = [
154327
"robot0_WRJ1",
155328
"robot0_WRJ0",
@@ -181,27 +354,7 @@ class ShadowHandOverEnvCfg(DirectMARLEnvCfg):
181354
]
182355

183356
# in-hand object
184-
object_cfg: RigidObjectCfg = RigidObjectCfg(
185-
prim_path="/World/envs/env_.*/object",
186-
spawn=sim_utils.SphereCfg(
187-
radius=0.0335,
188-
visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.8, 1.0, 0.0)),
189-
physics_material=sim_utils.RigidBodyMaterialCfg(static_friction=0.7),
190-
rigid_props=sim_utils.RigidBodyPropertiesCfg(
191-
kinematic_enabled=False,
192-
disable_gravity=False,
193-
enable_gyroscopic_forces=True,
194-
solver_position_iteration_count=8,
195-
solver_velocity_iteration_count=0,
196-
sleep_threshold=0.005,
197-
stabilization_threshold=0.0025,
198-
max_depenetration_velocity=1000.0,
199-
),
200-
collision_props=sim_utils.CollisionPropertiesCfg(),
201-
mass_props=sim_utils.MassPropertiesCfg(density=500.0),
202-
),
203-
init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, -0.39, 0.54), rot=(0.0, 0.0, 0.0, 1.0)),
204-
)
357+
object_cfg: ObjectCfg = ObjectCfg()
205358
# goal object
206359
goal_object_cfg: VisualizationMarkersCfg = VisualizationMarkersCfg(
207360
prim_path="/Visuals/goal_marker",
@@ -212,8 +365,8 @@ class ShadowHandOverEnvCfg(DirectMARLEnvCfg):
212365
),
213366
},
214367
)
215-
# scene
216-
scene: InteractiveSceneCfg = InteractiveSceneCfg(num_envs=2048, env_spacing=1.5, replicate_physics=True)
368+
# scene - use ShadowHandOverSceneCfg so that --preset newton disables clone_in_fabric automatically
369+
scene: ShadowHandOverSceneCfg = ShadowHandOverSceneCfg()
217370

218371
# reset
219372
reset_position_noise = 0.01 # range of position at reset

0 commit comments

Comments
 (0)