33#
44# SPDX-License-Identifier: BSD-3-Clause
55
6+ from isaaclab_newton .physics import MJWarpSolverCfg , NewtonCfg
67from isaaclab_physx .physics import PhysxCfg
78
89import isaaclab .envs .mdp as mdp
910import isaaclab .sim as sim_utils
11+ from isaaclab .actuators import ImplicitActuatorCfg
1012from isaaclab .assets import ArticulationCfg , RigidObjectCfg
1113from isaaclab .envs import DirectMARLEnvCfg
1214from isaaclab .managers import EventTermCfg as EventTerm
1618from isaaclab .sim import SimulationCfg
1719from isaaclab .sim .spawners .materials .physics_materials_cfg import RigidBodyMaterialCfg
1820from isaaclab .utils import configclass
21+ from isaaclab .utils .assets import ISAAC_NUCLEUS_DIR
22+
23+ from isaaclab_tasks .utils import PresetCfg
1924
2025from isaaclab_assets .robots .shadow_hand import SHADOW_HAND_CFG
2126
2227
2328@configclass
2429class 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,226 @@ class EventCfg:
113124 )
114125
115126
127+ # Newton requires a USD that explicitly declares ImplicitActuators. The PhysX
128+ # Shadow Hand USD relies on tendons that Newton cannot import, so the Newton
129+ # variant uses a separate USD file shipped alongside the standard one.
130+ _SHADOW_HAND_NEWTON_USD = f"{ ISAAC_NUCLEUS_DIR } /Robots/ShadowRobot/ShadowHand/shadow_hand_instanceable_newton.usd"
131+
132+
133+ def _newton_shadow_hand_cfg (
134+ prim_path : str , init_pos : tuple [float , float , float ], init_rot : tuple [float , float , float , float ]
135+ ) -> ArticulationCfg :
136+ """Newton variant of the Shadow Hand articulation cfg.
137+
138+ Mirrors the single-agent shadow_hand_env_cfg Newton port: explicit
139+ :class:`ImplicitActuatorCfg` per finger, USD that pre-declares actuators,
140+ and a deliberate ``rot`` re-application via spawn (Newton's
141+ ``import_usd.py`` discards the root body's native USD orientation).
142+ """
143+ return ArticulationCfg (
144+ prim_path = prim_path ,
145+ spawn = sim_utils .UsdFileCfg (
146+ usd_path = _SHADOW_HAND_NEWTON_USD ,
147+ activate_contact_sensors = False ,
148+ rigid_props = sim_utils .RigidBodyPropertiesCfg (
149+ disable_gravity = True ,
150+ retain_accelerations = True ,
151+ max_depenetration_velocity = 1000.0 ,
152+ ),
153+ articulation_props = sim_utils .ArticulationRootPropertiesCfg (enabled_self_collisions = True ),
154+ joint_drive_props = sim_utils .JointDrivePropertiesCfg (drive_type = "force" ),
155+ ),
156+ init_state = ArticulationCfg .InitialStateCfg (
157+ pos = init_pos ,
158+ # WARNING(Octi): Newton's import_usd.py bakes the USD body xformOp rotation into
159+ # joint_X_p for the root fixed joint, which cancels with the matching localPose1
160+ # rotation in joint_X_c during FK (joint_X_p * inv(joint_X_c) ≈ identity). This
161+ # discards the root body's native USD orientation, so we must re-apply it here as a
162+ # spawn rotation. PhysX or USD does not have this issue. Remove once Newton fixes
163+ # root joint transform handling in import_usd.py.
164+ rot = init_rot ,
165+ joint_pos = {".*" : 0.0 },
166+ ),
167+ actuators = {
168+ "fingers" : ImplicitActuatorCfg (
169+ joint_names_expr = ["robot0_WR.*" , "robot0_(FF|MF|RF|LF|TH)J(3|2|1)" , "robot0_(LF|TH)J4" , "robot0_THJ0" ],
170+ effort_limit_sim = {
171+ "robot0_WRJ1" : 4.785 ,
172+ "robot0_WRJ0" : 2.175 ,
173+ "robot0_(FF|MF|RF|LF)J1" : 0.7245 ,
174+ "robot0_FFJ(3|2)" : 0.9 ,
175+ "robot0_MFJ(3|2)" : 0.9 ,
176+ "robot0_RFJ(3|2)" : 0.9 ,
177+ "robot0_LFJ(4|3|2)" : 0.9 ,
178+ "robot0_THJ4" : 2.3722 ,
179+ "robot0_THJ3" : 1.45 ,
180+ "robot0_THJ(2|1)" : 0.99 ,
181+ "robot0_THJ0" : 0.81 ,
182+ },
183+ # PhysX uses stiffness=1.0 / damping=0.1 here, but layers an
184+ # additional fixed_tendons_props(limit_stiffness=30, damping=0.1)
185+ # contribution and runs solver_position_iteration_count=8 per
186+ # substep — both amplify the effective torque per unit nominal
187+ # gain. Newton's MJWarp implicit-PD path has neither: the
188+ # MjcTendon constraint contributes coupling but no extra
189+ # actuator stiffness, and the solver does not multi-iterate
190+ # corrections. Empirically Newton needs gains ~20× larger to
191+ # produce comparable joint authority; with stiffness=1.0 the
192+ # MAPPO mean reward at iter 200 is ~27 (vs PhysX's ~247),
193+ # and the policy never learns the catch. With stiffness=20.0 /
194+ # damping=2.0 it reaches ~777 mean reward and learns reliably.
195+ stiffness = 20.0 ,
196+ damping = 2.0 ,
197+ friction = 1e-2 ,
198+ armature = 2e-3 ,
199+ ),
200+ # J0 distal joints are passive in PhysX (fixed tendon, limit_stiffness=30,
201+ # damping=0.1). The Newton USD bakes stiffness=286/damping=57 from the
202+ # MJCF translation, which fights the MjcTendon coupling and bounces the
203+ # ball. Override with stiffness=10 (1/3 of PhysX limit_stiffness=30) so
204+ # the tendon constraint dominates without going fully limp.
205+ "distal_passive" : ImplicitActuatorCfg (
206+ joint_names_expr = ["robot0_(FF|MF|RF|LF)J0" ],
207+ stiffness = 10.0 ,
208+ damping = 0.1 ,
209+ friction = 1e-2 ,
210+ armature = 2e-3 ,
211+ ),
212+ },
213+ soft_joint_pos_limit_factor = 1.0 ,
214+ )
215+
216+
217+ @configclass
218+ class RightRobotCfg (PresetCfg ):
219+ physx = SHADOW_HAND_CFG .replace (prim_path = "/World/envs/env_.*/RightRobot" ).replace (
220+ init_state = ArticulationCfg .InitialStateCfg (
221+ pos = (0.0 , 0.0 , 0.5 ),
222+ rot = (0.0 , 0.0 , 0.0 , 1.0 ),
223+ joint_pos = {".*" : 0.0 },
224+ )
225+ )
226+ newton = _newton_shadow_hand_cfg (
227+ prim_path = "/World/envs/env_.*/RightRobot" ,
228+ init_pos = (0.0 , 0.0 , 0.5 ),
229+ init_rot = (0.0 , 0.0 , 0.0 , 1.0 ),
230+ )
231+ default = physx
232+
233+
234+ @configclass
235+ class LeftRobotCfg (PresetCfg ):
236+ physx = SHADOW_HAND_CFG .replace (prim_path = "/World/envs/env_.*/LeftRobot" ).replace (
237+ init_state = ArticulationCfg .InitialStateCfg (
238+ pos = (0.0 , - 1.0 , 0.5 ),
239+ rot = (0.0 , 0.0 , 1.0 , 0.0 ),
240+ joint_pos = {".*" : 0.0 },
241+ )
242+ )
243+ newton = _newton_shadow_hand_cfg (
244+ prim_path = "/World/envs/env_.*/LeftRobot" ,
245+ init_pos = (0.0 , - 1.0 , 0.5 ),
246+ init_rot = (0.0 , 0.0 , 1.0 , 0.0 ),
247+ )
248+ default = physx
249+
250+
251+ @configclass
252+ class ObjectCfg (PresetCfg ):
253+ """Hand-over object preset.
254+
255+ Both backends spawn the same procedural sphere as a free rigid body:
256+ Newton's :class:`~isaaclab_newton.assets.RigidObject` resolves the
257+ asset via the ``UsdPhysics.RigidBodyAPI`` that
258+ :class:`~isaaclab.sim.RigidBodyPropertiesCfg` applies. The Newton
259+ variant drops PhysX-only knobs (per-shape solver iterations, sleep
260+ thresholds, max depenetration velocity, custom physics material).
261+ """
262+
263+ physx = RigidObjectCfg (
264+ prim_path = "/World/envs/env_.*/object" ,
265+ spawn = sim_utils .SphereCfg (
266+ radius = 0.0335 ,
267+ visual_material = sim_utils .PreviewSurfaceCfg (diffuse_color = (0.8 , 1.0 , 0.0 )),
268+ physics_material = sim_utils .RigidBodyMaterialCfg (static_friction = 0.7 ),
269+ rigid_props = sim_utils .RigidBodyPropertiesCfg (
270+ kinematic_enabled = False ,
271+ disable_gravity = False ,
272+ enable_gyroscopic_forces = True ,
273+ solver_position_iteration_count = 8 ,
274+ solver_velocity_iteration_count = 0 ,
275+ sleep_threshold = 0.005 ,
276+ stabilization_threshold = 0.0025 ,
277+ max_depenetration_velocity = 1000.0 ,
278+ ),
279+ collision_props = sim_utils .CollisionPropertiesCfg (),
280+ mass_props = sim_utils .MassPropertiesCfg (density = 500.0 ),
281+ ),
282+ init_state = RigidObjectCfg .InitialStateCfg (pos = (0.0 , - 0.39 , 0.54 ), rot = (0.0 , 0.0 , 0.0 , 1.0 )),
283+ )
284+ newton = RigidObjectCfg (
285+ prim_path = "/World/envs/env_.*/object" ,
286+ spawn = sim_utils .SphereCfg (
287+ radius = 0.0335 ,
288+ visual_material = sim_utils .PreviewSurfaceCfg (diffuse_color = (0.8 , 1.0 , 0.0 )),
289+ rigid_props = sim_utils .RigidBodyPropertiesCfg (
290+ kinematic_enabled = False ,
291+ disable_gravity = False ,
292+ enable_gyroscopic_forces = True ,
293+ ),
294+ collision_props = sim_utils .CollisionPropertiesCfg (),
295+ mass_props = sim_utils .MassPropertiesCfg (density = 500.0 ),
296+ ),
297+ init_state = RigidObjectCfg .InitialStateCfg (pos = (0.0 , - 0.39 , 0.54 ), rot = (0.0 , 0.0 , 0.0 , 1.0 )),
298+ )
299+ default = physx
300+
301+
302+ @configclass
303+ class ShadowHandOverSceneCfg (PresetCfg ):
304+ """Scene preset.
305+
306+ PhysX supports ``clone_in_fabric=True`` for faster cloning. Newton's
307+ cloning path does not, so the Newton variant disables Fabric cloning.
308+ """
309+
310+ physx : InteractiveSceneCfg = InteractiveSceneCfg (
311+ num_envs = 2048 , env_spacing = 1.5 , replicate_physics = True , clone_in_fabric = True
312+ )
313+ newton : InteractiveSceneCfg = InteractiveSceneCfg (
314+ num_envs = 2048 , env_spacing = 1.5 , replicate_physics = True , clone_in_fabric = False
315+ )
316+ default : InteractiveSceneCfg = physx
317+
318+
319+ @configclass
320+ class PhysicsCfg (PresetCfg ):
321+ """Physics-backend preset (PhysX vs Newton/MJWarp).
322+
323+ Newton settings mirror the single-agent ShadowHand Newton port: elliptic
324+ cone, ``impratio=10`` (favors normal contacts over friction), 100 solver
325+ iterations, 2 substeps. Empirically converges on the single-agent ShadowHand
326+ tasks; tuning may be needed for handover-specific contact dynamics.
327+ """
328+
329+ physx = PhysxCfg (bounce_threshold_velocity = 0.2 )
330+ newton = NewtonCfg (
331+ solver_cfg = MJWarpSolverCfg (
332+ solver = "newton" ,
333+ integrator = "implicitfast" ,
334+ njmax = 200 ,
335+ nconmax = 70 ,
336+ impratio = 10.0 ,
337+ cone = "elliptic" ,
338+ update_data_interval = 2 ,
339+ iterations = 100 ,
340+ ),
341+ num_substeps = 2 ,
342+ debug_mode = False ,
343+ )
344+ default = physx
345+
346+
116347@configclass
117348class ShadowHandOverEnvCfg (DirectMARLEnvCfg ):
118349 # env
@@ -131,25 +362,11 @@ class ShadowHandOverEnvCfg(DirectMARLEnvCfg):
131362 static_friction = 1.0 ,
132363 dynamic_friction = 1.0 ,
133364 ),
134- physics = PhysxCfg (
135- bounce_threshold_velocity = 0.2 ,
136- ),
365+ physics = PhysicsCfg (),
137366 )
138367 # 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- )
368+ right_robot_cfg : RightRobotCfg = RightRobotCfg ()
369+ left_robot_cfg : LeftRobotCfg = LeftRobotCfg ()
153370 actuated_joint_names = [
154371 "robot0_WRJ1" ,
155372 "robot0_WRJ0" ,
@@ -181,27 +398,7 @@ class ShadowHandOverEnvCfg(DirectMARLEnvCfg):
181398 ]
182399
183400 # 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- )
401+ object_cfg : ObjectCfg = ObjectCfg ()
205402 # goal object
206403 goal_object_cfg : VisualizationMarkersCfg = VisualizationMarkersCfg (
207404 prim_path = "/Visuals/goal_marker" ,
@@ -212,8 +409,8 @@ class ShadowHandOverEnvCfg(DirectMARLEnvCfg):
212409 ),
213410 },
214411 )
215- # scene
216- scene : InteractiveSceneCfg = InteractiveSceneCfg ( num_envs = 2048 , env_spacing = 1.5 , replicate_physics = True )
412+ # scene - use ShadowHandOverSceneCfg so that --preset newton disables clone_in_fabric automatically
413+ scene : ShadowHandOverSceneCfg = ShadowHandOverSceneCfg ( )
217414
218415 # reset
219416 reset_position_noise = 0.01 # range of position at reset
0 commit comments