react-manip 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. assets/materials/realistic/common/ceramic.png +0 -0
  2. assets/materials/realistic/common/cream-plaster.png +0 -0
  3. assets/materials/realistic/common/gray-felt.png +0 -0
  4. assets/materials/realistic/common/gray-plaster.png +0 -0
  5. assets/materials/realistic/common/gray-woodgrain.png +0 -0
  6. assets/materials/realistic/common/light-gray-floor-tile.png +0 -0
  7. assets/materials/realistic/common/light-gray-plaster.png +0 -0
  8. assets/materials/realistic/common/metal.png +0 -0
  9. assets/materials/realistic/common/oak-wood.png +0 -0
  10. assets/materials/realistic/common/steel-brushed.png +0 -0
  11. assets/materials/realistic/common/steel-scratched.png +0 -0
  12. assets/materials/realistic/common/varnished-wood-panels.png +0 -0
  13. assets/materials/realistic/common/walnut-wood.png +0 -0
  14. assets/materials/realistic/common/white-bricks.png +0 -0
  15. assets/materials/realistic/common/wood-tiles.png +0 -0
  16. assets/materials/realistic/tiles074/Tiles074_2K_Color.png +0 -0
  17. assets/materials/realistic/woodfloor049/WoodFloor049_2K_Color.png +0 -0
  18. configs/box_insertion/profile_default.json +204 -0
  19. react_manip/__init__.py +12 -0
  20. react_manip/asset_cli.py +18 -0
  21. react_manip/builtin.py +168 -0
  22. react_manip/cameras.py +301 -0
  23. react_manip/category_objects.py +248 -0
  24. react_manip/core/__init__.py +60 -0
  25. react_manip/core/aggregate.py +59 -0
  26. react_manip/core/compatibility.py +64 -0
  27. react_manip/core/contracts.py +42 -0
  28. react_manip/core/geometry.py +269 -0
  29. react_manip/core/language.py +116 -0
  30. react_manip/core/metrics.py +196 -0
  31. react_manip/core/observation.py +59 -0
  32. react_manip/core/protocol.py +91 -0
  33. react_manip/core/registry.py +94 -0
  34. react_manip/core/runner.py +498 -0
  35. react_manip/core/scenarios.py +110 -0
  36. react_manip/core/specs.py +203 -0
  37. react_manip/core/timeouts.py +44 -0
  38. react_manip/core/visual_randomization.py +261 -0
  39. react_manip/evaluation/__init__.py +4 -0
  40. react_manip/evaluation/metrics.py +17 -0
  41. react_manip/evaluation/protocols.py +326 -0
  42. react_manip/evaluation/viewer.py +240 -0
  43. react_manip/instances/__init__.py +6 -0
  44. react_manip/instances/candidate_bank.py +109 -0
  45. react_manip/instances/public.py +95 -0
  46. react_manip/instances/records.py +345 -0
  47. react_manip/instances/runtime.py +306 -0
  48. react_manip/instances/suite.py +231 -0
  49. react_manip/instances/trajectory.py +59 -0
  50. react_manip/instances/variation.py +118 -0
  51. react_manip/legacy.py +318 -0
  52. react_manip/policies/__init__.py +5 -0
  53. react_manip/policies/base.py +79 -0
  54. react_manip/rendering.py +58 -0
  55. react_manip/robot_registry.py +31 -0
  56. react_manip/robots/__init__.py +33 -0
  57. react_manip/robots/adapters.py +135 -0
  58. react_manip/robots/agilex_adapter.py +35 -0
  59. react_manip/robots/agilex_description.py +74 -0
  60. react_manip/robots/agilex_robosuite.py +246 -0
  61. react_manip/robots/arm_actions.py +17 -0
  62. react_manip/robots/base.py +119 -0
  63. react_manip/robots/catalog.py +18 -0
  64. react_manip/robots/legacy_agilex_robosuite.py +169 -0
  65. react_manip/robots/mobile_base.py +33 -0
  66. react_manip/robots/mounts.py +80 -0
  67. react_manip/robots/resolution.py +103 -0
  68. react_manip/robots/sawyer_robosuite.py +147 -0
  69. react_manip/robots/sensors.py +137 -0
  70. react_manip/utils/__init__.py +1 -0
  71. react_manip/visual_task.py +50 -0
  72. react_manip-0.1.0.dist-info/METADATA +446 -0
  73. react_manip-0.1.0.dist-info/RECORD +99 -0
  74. react_manip-0.1.0.dist-info/WHEEL +5 -0
  75. react_manip-0.1.0.dist-info/entry_points.txt +2 -0
  76. react_manip-0.1.0.dist-info/licenses/LICENSE +131 -0
  77. react_manip-0.1.0.dist-info/top_level.txt +5 -0
  78. scripts/add_phone_lean_to_bank.py +242 -0
  79. scripts/agilex_wheeled_builder.py +133 -0
  80. scripts/audit_task_solvability.py +150 -0
  81. scripts/build_agilex_assets.py +317 -0
  82. scripts/build_legacy_agilex_assets.py +244 -0
  83. scripts/evaluate_all_tasks.py +309 -0
  84. scripts/evaluate_policy.py +281 -0
  85. scripts/piperx_assets.lock.json +181 -0
  86. scripts/pygame_demo.py +360 -0
  87. scripts/pygame_task_debug.py +242 -0
  88. scripts/regenerate_instance_bank.py +192 -0
  89. scripts/remove_task_from_bank.py +103 -0
  90. scripts/select_task_instances.py +64 -0
  91. scripts/verify_curling_reachability.py +206 -0
  92. scripts/verify_piperx_tasks.py +81 -0
  93. tasks/box_insertion/__init__.py +3 -0
  94. tasks/box_insertion/assets/scene.xml +50 -0
  95. tasks/box_insertion/env.py +642 -0
  96. tasks/box_insertion/evaluation.json +16 -0
  97. tasks/box_insertion/instance_contract.json +321 -0
  98. tasks/box_insertion/scoring.py +23 -0
  99. tasks/box_insertion/task_spec.json +333 -0
@@ -0,0 +1,204 @@
1
+ {
2
+ "schema_version": "1.0",
3
+ "profile_id": "box_insertion_final_position_v3",
4
+ "task_id": "box_insertion",
5
+ "fixed": {
6
+ "table": {
7
+ "top_z": 0.78,
8
+ "size": [
9
+ 0.9,
10
+ 1.6,
11
+ 0.05
12
+ ]
13
+ },
14
+ "box": {
15
+ "fixed": true,
16
+ "mounted_on": "table",
17
+ "has_bottom": false,
18
+ "center": [
19
+ 0.26,
20
+ 0.0,
21
+ 0.815
22
+ ],
23
+ "clearance_m": 0.05,
24
+ "inner_depth": 0.3,
25
+ "outer_depth": 0.32,
26
+ "wall_height": 0.07,
27
+ "wall": 0.02
28
+ },
29
+ "wood_block": {
30
+ "size": [
31
+ 0.13175,
32
+ 1.0,
33
+ 0.04421
34
+ ],
35
+ "initial_clearance_to_slot_m": 0.06,
36
+ "target_insertion_depth_m": 0.36,
37
+ "bottom_tolerance_m": 0.005
38
+ },
39
+ "physics": {
40
+ "gravity_m_s2": [
41
+ 0.0,
42
+ 0.0,
43
+ -9.81
44
+ ],
45
+ "table_friction": 0.005
46
+ },
47
+ "task": {
48
+ "time_score_max": 50.0,
49
+ "distance_score_max": 50.0,
50
+ "score_horizon_sec": 60.0,
51
+ "time_full_until_sec": 10.0,
52
+ "time_zero_from_sec": 40.0
53
+ }
54
+ },
55
+ "geometry": {
56
+ "wood_block.width_m": {
57
+ "distribution": "uniform",
58
+ "low": 0.6,
59
+ "high": 1.0
60
+ },
61
+ "block_friction.split_ratio": {
62
+ "distribution": "uniform",
63
+ "low": 0.2,
64
+ "high": 0.8
65
+ }
66
+ },
67
+ "physics": {
68
+ "block_friction.left": {
69
+ "distribution": "exponential",
70
+ "low": 0.01,
71
+ "high": 0.6,
72
+ "scale": 0.1
73
+ },
74
+ "block_friction.right": {
75
+ "distribution": "exponential",
76
+ "low": 0.01,
77
+ "high": 0.6,
78
+ "scale": 0.1
79
+ },
80
+ "block_density.light_kg_m3": {
81
+ "distribution": "uniform",
82
+ "low": 350.0,
83
+ "high": 550.0
84
+ },
85
+ "block_density.heavy_kg_m3": {
86
+ "distribution": "uniform",
87
+ "low": 850.0,
88
+ "high": 1150.0
89
+ },
90
+ "block_density.heavy_side": {
91
+ "distribution": "choice",
92
+ "values": [
93
+ "left",
94
+ "right"
95
+ ]
96
+ },
97
+ "physics.slot_left_wall_friction": {
98
+ "distribution": "uniform",
99
+ "low": 3.5,
100
+ "high": 5.0
101
+ },
102
+ "physics.slot_right_wall_friction": {
103
+ "distribution": "uniform",
104
+ "low": 3.5,
105
+ "high": 5.0
106
+ },
107
+ "physics.slot_back_wall_friction": {
108
+ "distribution": "uniform",
109
+ "low": 1.0,
110
+ "high": 2.0
111
+ }
112
+ },
113
+ "appearance": {
114
+ "room.floor_texture": {
115
+ "distribution": "choice",
116
+ "values": [
117
+ "light-gray-floor-tile",
118
+ "oak-wood-realistic",
119
+ "ceramic-realistic",
120
+ "gray-carpet-realistic",
121
+ "walnut-wood-realistic"
122
+ ]
123
+ },
124
+ "room.wall_texture": {
125
+ "distribution": "choice",
126
+ "values": [
127
+ "white-plaster",
128
+ "cool-white-paint",
129
+ "gray-plaster",
130
+ "brick-accent",
131
+ "cream-plaster-realistic"
132
+ ]
133
+ },
134
+ "desk.texture": {
135
+ "distribution": "choice",
136
+ "values": [
137
+ "oak-wood-realistic",
138
+ "walnut-wood-realistic",
139
+ "gray-woodgrain-realistic",
140
+ "varnished-wood-realistic",
141
+ "white-laminate",
142
+ "brushed-steel-realistic"
143
+ ]
144
+ },
145
+ "slot.texture": {
146
+ "distribution": "choice",
147
+ "values": [
148
+ "brushed-steel-realistic",
149
+ "scratched-steel-realistic",
150
+ "drawer-light-metal",
151
+ "drawer-dark-metal",
152
+ "drawer-white-laminate",
153
+ "drawer-blue-metal"
154
+ ]
155
+ },
156
+ "block.texture": {
157
+ "distribution": "choice",
158
+ "values": [
159
+ "oak-wood-realistic",
160
+ "walnut-wood-realistic",
161
+ "gray-woodgrain-realistic",
162
+ "varnished-wood-realistic",
163
+ "white-laminate",
164
+ "black-laminate"
165
+ ]
166
+ }
167
+ },
168
+ "runtime": {
169
+ "lighting.layout": {
170
+ "distribution": "choice",
171
+ "values": [
172
+ "overcast",
173
+ "warm_indoor",
174
+ "daylight_side",
175
+ "cool_office",
176
+ "backlit",
177
+ "mixed_daylight"
178
+ ]
179
+ },
180
+ "lighting.intensity_scale": {
181
+ "distribution": "uniform",
182
+ "low": 0.8,
183
+ "high": 1.35
184
+ },
185
+ "lighting.azimuth_deg": {
186
+ "distribution": "uniform",
187
+ "low": -180,
188
+ "high": 180
189
+ }
190
+ },
191
+ "constraints": [
192
+ {
193
+ "type": "not_equal",
194
+ "paths": [
195
+ "desk.texture",
196
+ "block.texture"
197
+ ]
198
+ }
199
+ ],
200
+ "assets": {
201
+ "scene": "tasks/box_insertion/assets/scene.xml",
202
+ "asset_version": "box_insertion_robot_v1"
203
+ }
204
+ }
@@ -0,0 +1,12 @@
1
+ """CL-Manip external-policy evaluation runtime."""
2
+ # Set this before any task imports robosuite/Numba. Some historical task
3
+ # modules use setdefault('NUMBA_DISABLE_JIT', '1') after robosuite is loaded;
4
+ # switching modes mid-process breaks controller compilation and replay IDs.
5
+ import os
6
+ os.environ.setdefault('NUMBA_DISABLE_JIT', '0')
7
+ __version__ = '0.1.0'
8
+ from .core import BenchmarkRunner, SuccessRule, TaskSpec
9
+ from .builtin import discover_task_definitions, register_builtin_tasks
10
+ from .instances.public import InstanceBundle, create_instance, open_instance, task_score
11
+ __all__ = ['BenchmarkRunner', 'SuccessRule', 'TaskSpec', 'discover_task_definitions',
12
+ 'register_builtin_tasks', 'InstanceBundle', 'create_instance', 'open_instance', 'task_score']
@@ -0,0 +1,18 @@
1
+ """Prepare the checksum-locked PiPER-X assets outside the installed package."""
2
+ import argparse
3
+
4
+
5
+ def main():
6
+ parser = argparse.ArgumentParser(description=__doc__)
7
+ parser.add_argument('command', choices=('prepare',))
8
+ parser.add_argument('--offline', action='store_true')
9
+ args = parser.parse_args()
10
+ from react_manip.robots.agilex_robosuite import asset_dir, validate_bundle
11
+ from scripts.build_agilex_assets import Builder
12
+ Builder(asset_dir(), args.offline).build()
13
+ validate_bundle()
14
+ print(f'PiPER-X assets ready: {asset_dir()}')
15
+
16
+
17
+ if __name__ == '__main__':
18
+ main()
react_manip/builtin.py ADDED
@@ -0,0 +1,168 @@
1
+ """Lazy environment registration for the sixteen release tasks."""
2
+ from __future__ import annotations
3
+ from pathlib import Path
4
+ from typing import Any, Callable
5
+ import inspect
6
+ from .core.registry import get_task_registry
7
+ from .core.specs import TaskSpec, load_task_spec
8
+ from .robots.resolution import resolve_robosuite_robot, robot_id_from_model
9
+ from .instances.records import SUPPORTED_TASKS
10
+
11
+ def _lazy_constructor(module_name: str, class_name: str, default_robot: str, *, native_v2: bool = False) -> Callable[..., Any]:
12
+ def factory(*, seed: int | None = None, robot_id: str | None = None, **kwargs: Any) -> Any:
13
+ from importlib import import_module
14
+ from .legacy import LegacyTaskAdapter, attach_robot_context
15
+
16
+ scenario = kwargs.pop("scenario", None)
17
+ cls = getattr(import_module(module_name), class_name)
18
+ robot = robot_id or default_robot
19
+ if native_v2:
20
+ # Native v2 tasks are not robosuite environments. Construct them
21
+ # from the parameters they actually advertise (CurlingSweep, for
22
+ # example, accepts only ``seed``), while forwarding optional
23
+ # arguments to richer native adapters.
24
+ params = dict(kwargs)
25
+ try:
26
+ signature = inspect.signature(cls)
27
+ accepts_kwargs = any(
28
+ p.kind == inspect.Parameter.VAR_KEYWORD
29
+ for p in signature.parameters.values()
30
+ )
31
+ accepted = set(signature.parameters)
32
+ except (TypeError, ValueError):
33
+ accepts_kwargs, accepted = True, set()
34
+ if not accepts_kwargs:
35
+ params = {key: value for key, value in params.items() if key in accepted}
36
+ if accepts_kwargs or "seed" in accepted:
37
+ params.setdefault("seed", seed)
38
+ if "scenario" in accepted:
39
+ params.setdefault("scenario", scenario)
40
+ if "robot_id" in accepted:
41
+ params.setdefault("robot_id", robot_id)
42
+ if "robots" in accepted:
43
+ mapped_robot = robot
44
+ if isinstance(robot, str):
45
+ resolved = resolve_robosuite_robot(robot_id_from_model(robot))
46
+ mapped_robot = resolved.robot_name
47
+ if resolved.controller_configs is not None:
48
+ params.setdefault("controller_configs", resolved.controller_configs)
49
+ params.setdefault("gripper_types", resolved.gripper_types)
50
+ params.setdefault("initialization_noise", resolved.initialization_noise)
51
+ params.setdefault("robots", mapped_robot)
52
+ env = cls(**params)
53
+ attach_robot_context(env, robot_id)
54
+ env.scenario = scenario
55
+ adapted = LegacyTaskAdapter(env)
56
+ adapted.scenario = scenario
57
+ return adapted
58
+ if isinstance(robot, str):
59
+ robot_key = robot_id_from_model(robot)
60
+ resolved = resolve_robosuite_robot(robot_key)
61
+ robot = resolved.robot_name
62
+ # Preserve legacy task-tuned Panda/Sawyer defaults until their
63
+ # controller profiles are migrated; new providers own defaults.
64
+ if robot_key not in {"panda", "sawyer"}:
65
+ kwargs.setdefault("controller_configs", resolved.controller_configs)
66
+ kwargs.setdefault("gripper_types", resolved.gripper_types)
67
+ kwargs.setdefault("initialization_noise", resolved.initialization_noise)
68
+ env = LegacyTaskAdapter(cls(robots=robot, seed=seed, scenario=scenario, **kwargs))
69
+ if isinstance(robot, str):
70
+ env.env.robot_id = robot_key
71
+ # Tasks may resolve a Profile themselves when no Scenario was supplied.
72
+ # Preserve that concrete instance for replay IDs and benchmark logging.
73
+ env.scenario = getattr(env.env, "scenario", None) or scenario
74
+ return env
75
+
76
+ return factory
77
+
78
+ def _laptop_factory(*, seed: int | None = None, robot_id: str | None = None, **kwargs: Any) -> Any:
79
+ """Use the same physical env construction for v2 and task recordings."""
80
+
81
+ from tasks.laptop_into_drawer.construction import make_env
82
+ from .legacy import LegacyTaskAdapter
83
+
84
+ scenario = kwargs.get("scenario")
85
+ # Headless benchmark runs do not need an OpenGL context. Recorders opt in
86
+ # explicitly so macOS CGL is never initialized on a non-GUI evaluation.
87
+ kwargs.setdefault("has_offscreen_renderer", False)
88
+ env = LegacyTaskAdapter(make_env(seed=0 if seed is None else seed, robot_id=robot_id or "dual_piperx", **kwargs))
89
+ return env
90
+
91
+ def _maze_factory(*, seed=None, robot_id=None, **kwargs):
92
+ from tasks.tilte_maze.env import TilteMazePhysical
93
+ from .legacy import LegacyTaskAdapter
94
+ env = TilteMazePhysical(robots=robot_id or 'dual_piperx', seed=seed, **kwargs)
95
+ adapted = LegacyTaskAdapter(env)
96
+ adapted.scenario = env.scenario
97
+ return adapted
98
+
99
+ def _phone_lean_factory(**kwargs):
100
+ from tasks.phone_lean.benchmark_adapter import make_benchmark_env
101
+ return make_benchmark_env(**kwargs)
102
+
103
+ def _leveling_board1_factory(**kwargs):
104
+ from tasks.leveling_board1.benchmark_adapter import make_benchmark_env
105
+ return make_benchmark_env(**kwargs)
106
+
107
+ def _turntable_gamble1_factory(**kwargs):
108
+ from tasks.turntable_gamble1.benchmark_adapter import make_benchmark_env
109
+ return make_benchmark_env(**kwargs)
110
+
111
+ def _car_drive2_factory(**kwargs):
112
+ from tasks.car_drive2.benchmark_adapter import make_benchmark_env
113
+ return make_benchmark_env(**kwargs)
114
+
115
+ def _ring_toss1_factory(**kwargs):
116
+ from tasks.ring_toss1.benchmark_adapter import make_benchmark_env
117
+ return make_benchmark_env(**kwargs)
118
+
119
+ _SPECIAL_FACTORIES = {'laptop_into_drawer': _laptop_factory, 'tilte_maze': _maze_factory, 'phone_lean': _phone_lean_factory, 'leveling_board1': _leveling_board1_factory, 'turntable_gamble1': _turntable_gamble1_factory, 'car_drive2': _car_drive2_factory, 'ring_toss1': _ring_toss1_factory}
120
+
121
+ def discover_task_definitions(root: str | Path | None = None, *, task_ids=None):
122
+ root_path = Path(root) if root is not None else Path(__file__).resolve().parents[1] / 'tasks'
123
+ definitions = []
124
+ available = (task_id for task_id in SUPPORTED_TASKS
125
+ if (root_path / task_id / 'task_spec.json').is_file())
126
+ for task_id in available if task_ids is None else task_ids:
127
+ spec = load_task_spec(root_path / task_id / 'task_spec.json')
128
+ class_name = ''.join(part.capitalize() for part in task_id.split('_'))
129
+ factory = _SPECIAL_FACTORIES.get(task_id, _lazy_constructor(
130
+ f'tasks.{task_id}.env', class_name,
131
+ spec.supported_robots[0] if spec.supported_robots else 'dual_piperx',
132
+ native_v2=bool(spec.metadata.get('native_v2', False))))
133
+ definitions.append((spec, _camera_factory(factory, spec)))
134
+ return tuple(definitions)
135
+
136
+ def _camera_factory(factory, spec=None):
137
+ """Uniform opt-in RGB, independent of legacy use_camera_obs support."""
138
+ from functools import wraps
139
+
140
+ @wraps(factory)
141
+ def create(*args, camera_observation_ids=(), camera_width=640, camera_height=480, **kwargs):
142
+ if spec is not None:
143
+ from .core.compatibility import resolve_task_robot
144
+ kwargs['robot_id'] = resolve_task_robot(spec, kwargs.get('robot_id'))
145
+ elif kwargs.get('robot_id') is not None:
146
+ from .robots.catalog import normalize_robot_id
147
+ kwargs['robot_id'] = normalize_robot_id(kwargs['robot_id'])
148
+ env = factory(*args, **kwargs)
149
+ try:
150
+ from .cameras import configure_fixed_cameras
151
+ configure_fixed_cameras(getattr(env, 'env', env))
152
+ from .robots.agilex_robosuite import validate_compiled_grippers
153
+ validate_compiled_grippers(env)
154
+ env.set_camera_observations(camera_observation_ids, width=camera_width, height=camera_height)
155
+ return env
156
+ except Exception:
157
+ env.close()
158
+ raise
159
+ return create
160
+
161
+ def register_builtin_tasks(*, replace: bool = False) -> None:
162
+ """Register legacy tasks exactly once; safe to call repeatedly."""
163
+
164
+ registry = get_task_registry()
165
+ for spec, factory in discover_task_definitions():
166
+ if spec.task_id in {definition.spec.task_id for definition in registry.list()} and not replace:
167
+ continue
168
+ registry.register(spec, factory, replace=replace)