Skip to content

Commit a094cef

Browse files
committed
Merge branch 'main' of github.com:isaac-sim/IsaacLab-Arena into peterd/lab_3_ga_main
2 parents 1409b33 + 0b63649 commit a094cef

12 files changed

Lines changed: 372 additions & 53 deletions

AGENTS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,22 @@ Lint and format tooling (`pre-commit` and the hooks it runs — black, flake8, i
6060

6161
## Conventions
6262

63+
### Coordinate-frame naming
64+
65+
Use active target-source notation for Arena-owned poses and transforms:
66+
67+
- `T_A_B` maps points from frame `B` into frame `A`.
68+
- `T_A_B = (t_A_B, q_A_B)` consists of translation `t_A_B` and rotation
69+
`q_A_B`, with the rotation represented as a quaternion.
70+
- For example, `T_W_O` maps points from object frame `O` into world frame `W`.
71+
- Transform composition follows `T_C_A = T_C_B * T_B_A`.
72+
- Frame letters are contextual. Define each near its first use when its meaning is not obvious.
73+
- Preserve external API names such as Isaac Lab's `root_pose_w`. Its lowercase `_w`, `_e`, and `_b` suffixes
74+
denote the simulation world, local environment, and robot base frames, respectively.
75+
- A lowercase API suffix names only the frame in which a quantity is expressed. When both source and target
76+
frames matter in Arena calculations, bind the value to an explicit transform name, for example
77+
`T_W_O = object.data.root_pose_w`.
78+
6379
### Wrapped Environment
6480

6581
`ArenaEnvBuilder.make_registered()` returns the gym-wrapped env (not the base env). Use `env.unwrapped` explicitly to access Isaac Lab-specific attributes (`cfg`, `device`, `step_dt`, etc.) that are not forwarded by gymnasium's `OrderEnforcing` wrapper:

CONTRIBUTING.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
This document describes the rules for contributing to Isaac Lab-Arena
44

55

6+
### Coordinate-frame naming
7+
8+
See [Coordinate-frame naming](AGENTS.md#coordinate-frame-naming) in `AGENTS.md`.
9+
10+
611
#### Signing Your Work
712

813
* We require that all contributors "sign-off" on their commits.

docs/pages/concepts/scene/concept_assets_design.rst

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ Backgrounds
5858

5959
Backgrounds are registered as ``BASE`` assets, but their composed USDs may contain
6060
dynamic rigid bodies and articulations whose states can change as they interact with
61-
the robot or other objects. Set ``reset_nested_physics=True`` on a ``Background`` to
62-
reset these nested physics roots.
61+
the robot or other objects. Arena resets these nested physics roots by default. Set
62+
``reset_nested_physics=False`` on a ``Background`` to opt out.
6363

6464
Arena registers the roots as private Isaac Lab reset views. After simulation and RTX
6565
initialization, Arena creates the views and records one environment-local pose and
@@ -87,6 +87,32 @@ ownership away from the background.
8787
Instanceable subtrees that contribute dynamic physics are materialized at spawn
8888
time because physics views cannot control dynamic instance proxies.
8989

90+
Rigid-body behavior
91+
^^^^^^^^^^^^^^^^^^^
92+
93+
A background's ``BASE`` type does not remove physics authored in its USD. The
94+
``rigid_props`` value in ``spawn_cfg_addon`` is forwarded to Isaac Lab's USD spawner.
95+
When ``rigid_props=None`` (the default), the spawner does not add or modify rigid-body
96+
properties. A prim that already has ``UsdPhysics.RigidBodyAPI`` therefore keeps its
97+
authored behavior; for example, an authored dynamic body can move when gravity or
98+
contact forces act on it.
99+
100+
For a background fixture that must not move, such as a table used only as a fixed
101+
work surface, make its rigid bodies kinematic:
102+
103+
.. code-block:: python
104+
105+
spawn_cfg_addon = {
106+
"rigid_props": sim_utils.RigidBodyPropertiesCfg(kinematic_enabled=True),
107+
}
108+
109+
A kinematic body still collides with dynamic objects, but gravity, forces, and contacts
110+
do not displace it. Its pose changes only when it is explicitly commanded. Do not use
111+
this setting when the background should respond physically. For example, a table that
112+
a humanoid is expected to push must remain dynamic: retain ``rigid_props=None`` when
113+
the USD already authors it as dynamic, or explicitly set ``kinematic_enabled=False``
114+
to override an authored kinematic setting.
115+
90116
Object references
91117
-----------------
92118

isaaclab_arena/assets/background.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def __init__(
3333
object_min_z: float,
3434
prim_path: str | None = None,
3535
initial_pose: Pose | None = None,
36-
reset_nested_physics: bool = False,
36+
reset_nested_physics: bool = True,
3737
**kwargs,
3838
):
3939
self.reset_nested_physics = reset_nested_physics

isaaclab_arena/assets/background_library.py

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,10 @@ class LibraryBackground(Background):
3030
object_min_z: float
3131
spawn_cfg_addon: dict[str, Any] = {}
3232
asset_cfg_addon: dict[str, Any] = {}
33-
reset_nested_physics: bool = False
3433

35-
def __init__(self, reset_nested_physics: bool | None = None, **kwargs):
34+
def __init__(self, **kwargs):
3635
# Check lazy USD paths are set by here
3736
assert self.usd_path is not None
38-
if reset_nested_physics is None:
39-
reset_nested_physics = self.reset_nested_physics
4037
super().__init__(
4138
name=self.name,
4239
tags=self.tags,
@@ -45,7 +42,6 @@ def __init__(self, reset_nested_physics: bool | None = None, **kwargs):
4542
object_min_z=self.object_min_z,
4643
spawn_cfg_addon=self.spawn_cfg_addon,
4744
asset_cfg_addon=self.asset_cfg_addon,
48-
reset_nested_physics=reset_nested_physics,
4945
**kwargs,
5046
)
5147

@@ -62,9 +58,6 @@ class KitchenBackground(LibraryBackground):
6258
initial_pose = Pose(position_xyz=(0.772, 3.39, -0.895), rotation_xyzw=(0, 0, -0.70711, 0.70711))
6359
object_min_z = -0.2
6460

65-
def __init__(self):
66-
super().__init__()
67-
6861

6962
@register_asset
7063
class KitchenWithOpenDrawerBackground(LibraryBackground):
@@ -80,9 +73,6 @@ class KitchenWithOpenDrawerBackground(LibraryBackground):
8073
initial_pose = Pose(position_xyz=(0.772, 3.39, -0.895), rotation_xyzw=(0, 0, -0.70711, 0.70711))
8174
object_min_z = -0.2
8275

83-
def __init__(self):
84-
super().__init__()
85-
8676

8777
@register_asset
8878
class PackingTableBackground(LibraryBackground):
@@ -96,9 +86,6 @@ class PackingTableBackground(LibraryBackground):
9686
initial_pose = Pose(position_xyz=(0.72193, -0.04727, -0.92512), rotation_xyzw=(0.0, 0.0, -0.70711, 0.70711))
9787
object_min_z = -0.2
9888

99-
def __init__(self):
100-
super().__init__()
101-
10289

10390
@register_asset
10491
class GalileoBackground(LibraryBackground):
@@ -112,9 +99,6 @@ class GalileoBackground(LibraryBackground):
11299
initial_pose = Pose(position_xyz=(4.420, 1.408, -0.795), rotation_xyzw=(0.0, 0.0, 0.0, 1.0))
113100
object_min_z = -0.2
114101

115-
def __init__(self):
116-
super().__init__()
117-
118102

119103
@register_asset
120104
class GalileoLocomanipBackground(LibraryBackground):
@@ -128,9 +112,6 @@ class GalileoLocomanipBackground(LibraryBackground):
128112
initial_pose = Pose(position_xyz=(4.420, 1.408, -0.795), rotation_xyzw=(0.0, 0.0, 0.0, 1.0))
129113
object_min_z = -0.2
130114

131-
def __init__(self):
132-
super().__init__()
133-
134115

135116
@register_asset
136117
class Table(LibraryBackground):
@@ -143,9 +124,6 @@ class Table(LibraryBackground):
143124
usd_path = f"{ISAAC_NUCLEUS_DIR}/Props/Mounts/SeattleLabTable/table_instanceable.usd"
144125
object_min_z = -0.05
145126

146-
def __init__(self):
147-
super().__init__()
148-
149127

150128
@register_asset
151129
class OfficeTableBackground(LibraryBackground):
@@ -162,8 +140,8 @@ class OfficeTableBackground(LibraryBackground):
162140
"rigid_props": sim_utils.RigidBodyPropertiesCfg(kinematic_enabled=True),
163141
}
164142

165-
def __init__(self):
166-
super().__init__(scale=self.scale)
143+
def __init__(self, **kwargs):
144+
super().__init__(scale=self.scale, **kwargs)
167145

168146

169147
@register_asset
@@ -179,7 +157,6 @@ class LightwheelKitchenBackground(LibraryBackground):
179157
object_min_z = -0.2
180158
layout_id = 1
181159
style_id = 1
182-
reset_nested_physics = True
183160

184161
def __init__(
185162
self,
@@ -228,9 +205,9 @@ class MapleTableRobolab(LibraryBackground):
228205
tags = ["background", "robolab"]
229206
usd_path = f"{ARENA_NUCLEUS_DIR}/Arena/assets/object_library/srl_robolab_assets/scenes/maple_table.usda"
230207
object_min_z = -0.05
231-
232-
def __init__(self):
233-
super().__init__()
208+
spawn_cfg_addon = {
209+
"rigid_props": sim_utils.RigidBodyPropertiesCfg(kinematic_enabled=True),
210+
}
234211

235212

236213
@register_asset
@@ -239,6 +216,9 @@ class TableOakRobolab(LibraryBackground):
239216
tags = ["background", "robolab"]
240217
usd_path = f"{ARENA_NUCLEUS_DIR}/Arena/assets/object_library/srl_robolab_assets/fixtures/table_oak.usd"
241218
object_min_z = -0.05
219+
spawn_cfg_addon = {
220+
"rigid_props": sim_utils.RigidBodyPropertiesCfg(kinematic_enabled=True),
221+
}
242222

243223

244224
# -----------------------------------------------------------------------------
@@ -252,7 +232,6 @@ class ReplicatorKitchenBackground(LibraryBackground):
252232
tags = ["background", "replicator"]
253233
initial_pose = Pose.identity()
254234
object_min_z = -0.2
255-
reset_nested_physics = True
256235

257236
def get_viewer_cfg(self) -> ViewerCfg:
258237
return ViewerCfg(eye=(0.0, -1.0, 1.65), lookat=(0.0, 0.0, 1.35))

isaaclab_arena/scene/scene.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,15 @@ def get_scene_cfg(self) -> Any:
101101
return scene_cfg
102102

103103
def get_background_physics_paths(self) -> dict[str, dict[str, ObjectType]]:
104-
"""Return opted-in backgrounds mapped to deferred-reset physics roots."""
104+
"""Return reset-enabled backgrounds mapped to deferred-reset physics roots."""
105105
return {background: dict(paths) for background, paths in self._background_physics_paths.items()}
106106

107107
def get_background_physics_referenced_paths(self) -> dict[str, dict[str, ObjectType]]:
108-
"""Return opted-in backgrounds mapped to object-reference-owned runtime paths."""
108+
"""Return reset-enabled backgrounds mapped to object-reference-owned runtime paths."""
109109
return {background: dict(paths) for background, paths in self._background_physics_referenced_paths.items()}
110110

111111
def get_background_physics_prim_paths(self) -> dict[str, str]:
112-
"""Return opted-in backgrounds mapped to their runtime root paths."""
112+
"""Return reset-enabled backgrounds mapped to their runtime root paths."""
113113
return dict(self._background_physics_prim_paths)
114114

115115
def get_observation_cfg(self) -> Any:

isaaclab_arena/terms/events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def _validate_runtime_composition(self, env: ManagerBasedEnv) -> None:
111111
for background_name, background_path_template in self._background_prim_paths.items():
112112
background_path = self._runtime_path(background_path_template, env_prim_path)
113113
background_prim = env.scene.stage.GetPrimAtPath(background_path)
114-
assert background_prim.IsValid(), f"Missing opted-in background prim at '{background_path}'"
114+
assert background_prim.IsValid(), f"Missing reset-enabled background prim at '{background_path}'"
115115
referenced_paths = {
116116
self._runtime_path(path, env_prim_path): object_type
117117
for path, object_type in self._referenced_paths[background_name].items()

isaaclab_arena/tests/test_background_physics_reset.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ def _test_background_physics_discovery_and_reset(
127127
"background",
128128
background_path,
129129
object_min_z=0.0,
130-
reset_nested_physics=True,
131130
)
131+
assert background.reset_nested_physics
132132
referenced_free_body = ObjectReference(
133133
name="referenced_free_body",
134134
prim_path="{ENV_REGEX_NS}/background/free_body",
@@ -290,3 +290,64 @@ def test_background_physics_reset_with_newton():
290290
check_interactivity=False,
291291
include_joint_network=False,
292292
)
293+
294+
295+
def _test_maple_table_pose_restored_on_reset(_) -> bool:
296+
"""Move the nested Maple table body and verify reset restores its cached pose."""
297+
import torch
298+
299+
from isaaclab_arena.assets.object_base import ObjectType
300+
from isaaclab_arena.assets.registries import AssetRegistry
301+
from isaaclab_arena.cli.isaaclab_arena_cli import arena_env_builder_cfg_from_argparse, get_isaaclab_arena_cli_parser
302+
from isaaclab_arena.environments.arena_env_builder import ArenaEnvBuilder
303+
from isaaclab_arena.environments.isaaclab_arena_environment import IsaacLabArenaEnvironment
304+
from isaaclab_arena.scene.scene import Scene
305+
306+
background_cls = AssetRegistry().get_asset_by_name("maple_table_robolab")
307+
background = background_cls()
308+
opt_out_background = background_cls(reset_nested_physics=False)
309+
assert background.reset_nested_physics
310+
assert not opt_out_background.reset_nested_physics
311+
scene = Scene(assets=[background])
312+
arena_env = IsaacLabArenaEnvironment(name="maple_table_background_reset", scene=scene)
313+
args = get_isaaclab_arena_cli_parser().parse_args(["--num_envs", "1"])
314+
builder = ArenaEnvBuilder(arena_env, arena_env_builder_cfg_from_argparse(args))
315+
env_cfg, _ = builder.compose_manager_cfg()
316+
317+
table_path = "{ENV_REGEX_NS}/maple_table_robolab/table"
318+
assert scene.get_background_physics_paths()[background.name][table_path] == ObjectType.RIGID
319+
320+
env = builder.make_registered(env_cfg)
321+
try:
322+
env.reset()
323+
base_env = env.unwrapped
324+
reset_term = base_env.event_manager.get_term_cfg("reset_background_physics").func
325+
table_reset = next(
326+
reset
327+
for reset in reset_term._rigid_resets
328+
if reset.asset.cfg.prim_path.endswith("/maple_table_robolab/table")
329+
)
330+
table = table_reset.asset
331+
initial_pose = table.data.root_pose_w.torch.clone()
332+
env_ids = torch.tensor([0], device=base_env.device)
333+
334+
moved_pose = initial_pose.clone()
335+
moved_pose[:, 0] += 1.0
336+
table.write_root_pose_to_sim_index(root_pose=moved_pose, env_ids=env_ids)
337+
table.write_root_velocity_to_sim_index(
338+
root_velocity=torch.ones((1, 6), device=base_env.device),
339+
env_ids=env_ids,
340+
)
341+
assert torch.allclose(table.data.root_pose_w.torch, moved_pose)
342+
343+
env.reset()
344+
345+
assert torch.allclose(table.data.root_pose_w.torch, initial_pose, atol=1.0e-5)
346+
assert torch.count_nonzero(table.data.root_vel_w.torch) == 0
347+
finally:
348+
env.close()
349+
return True
350+
351+
352+
def test_maple_table_pose_restored_on_reset():
353+
assert run_function_with_persistent_simulation_app(_test_maple_table_pose_restored_on_reset)

isaaclab_arena/tests/test_pose.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,29 @@ def test_pose_composition():
4747
assert T_C_A.rotation_xyzw == (0.0, 0.0, 0.0, 1.0)
4848

4949

50+
def test_pose_composition_rotates_inner_translation():
51+
"""Check that the outer rotation is applied to the inner translation."""
52+
square_root_of_half = math.sqrt(0.5)
53+
quarter_turn_about_z_xyzw = (0.0, 0.0, square_root_of_half, square_root_of_half)
54+
T_B_A = Pose(position_xyz=(1.0, 0.0, 0.0), rotation_xyzw=(0.0, 0.0, 0.0, 1.0))
55+
T_C_B = Pose(position_xyz=(2.0, 0.0, 0.0), rotation_xyzw=quarter_turn_about_z_xyzw)
56+
57+
T_C_A = T_C_B.multiply(T_B_A)
58+
59+
torch.testing.assert_close(
60+
torch.tensor(T_C_A.position_xyz),
61+
torch.tensor((2.0, 1.0, 0.0)),
62+
rtol=0.0,
63+
atol=1e-6,
64+
)
65+
torch.testing.assert_close(
66+
torch.tensor(T_C_A.rotation_xyzw),
67+
torch.tensor(quarter_turn_about_z_xyzw),
68+
rtol=0.0,
69+
atol=1e-6,
70+
)
71+
72+
5073
def test_rotate_points_by_yaw_batch_matches_scalar():
5174
"""Batch rotation with per-element yaws produces the same result as scalar rotation per row."""
5275
points = torch.tensor([[1.0, 2.0, 0.5], [3.0, -1.0, 1.0], [0.0, 4.0, -0.3]])

0 commit comments

Comments
 (0)