simulo-interfaces 0.23.0__tar.gz → 0.25.0__tar.gz
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.
- {simulo_interfaces-0.23.0/src/simulo_interfaces.egg-info → simulo_interfaces-0.25.0}/PKG-INFO +1 -1
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/pyproject.toml +1 -1
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/__init__.py +45 -14
- simulo_interfaces-0.25.0/src/simulo/interfaces/authoring/action_channel.py +22 -0
- simulo_interfaces-0.25.0/src/simulo/interfaces/authoring/policy_inputs.py +207 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/prop.py +30 -6
- simulo_interfaces-0.25.0/src/simulo/interfaces/authoring/properties.py +92 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/record.py +5 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/robot.py +21 -9
- simulo_interfaces-0.25.0/src/simulo/interfaces/authoring/robot_config.py +82 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/scene.py +28 -1
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/sensor.py +9 -1
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/terrain.py +43 -0
- simulo_interfaces-0.25.0/src/simulo/interfaces/authoring/variants.py +48 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/visual.py +90 -1
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/__init__.py +47 -2
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/asset_catalog.py +2 -2
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/evaluation.py +27 -4
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/manifest.py +6 -5
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/onnx_bundle.py +192 -45
- simulo_interfaces-0.25.0/src/simulo/interfaces/platform/policy_checkpoint.py +89 -0
- simulo_interfaces-0.25.0/src/simulo/interfaces/platform/preview.py +197 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/runs.py +44 -8
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/submit.py +25 -4
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/systems.py +6 -7
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/__init__.py +5 -1
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/env.py +5 -0
- simulo_interfaces-0.25.0/src/simulo/interfaces/runtime/policy_frames.py +42 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/task.py +86 -2
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/trainer.py +3 -1
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0/src/simulo_interfaces.egg-info}/PKG-INFO +1 -1
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo_interfaces.egg-info/SOURCES.txt +7 -0
- simulo_interfaces-0.23.0/src/simulo/interfaces/platform/preview.py +0 -85
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/MANIFEST.in +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/PYPI.md +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/setup.cfg +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/__init__.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/actuator.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/asset.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/checkpoint.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/entity.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/light.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/materials.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/physics.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/pose.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/primitives.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/world.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/exceptions.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/ids.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/_payload.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/app.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/asset.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/callbacks.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/debug.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/domain.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/enums.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/job_events.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/outputs.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/runtime.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/volume.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/py.typed +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/anomaly.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/components.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/player.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/policy.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/tensors.py +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo_interfaces.egg-info/dependency_links.txt +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo_interfaces.egg-info/requires.txt +0 -0
- {simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo_interfaces.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simulo-interfaces"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.25.0"
|
|
8
8
|
description = "Typed Python contracts for the Simulo cloud robotics SDK and managed simulation runtime."
|
|
9
9
|
readme = "PYPI.md"
|
|
10
10
|
requires-python = ">=3.11"
|
{simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/__init__.py
RENAMED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
"""Part C — the typed authoring surface.
|
|
2
2
|
|
|
3
|
-
The
|
|
4
|
-
Each type
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
go", per the owner's 2026-07-26 directive recorded in the plan): the former
|
|
9
|
-
backend ``core`` modules for these types are deleted, so ``isinstance`` keeps
|
|
10
|
-
working because only this definition exists.
|
|
3
|
+
The relocation and mirror destination for shared authoring value types.
|
|
4
|
+
Each value type has one definition here, used by the torch-free client when
|
|
5
|
+
submitting a job and by the backend when executing it. Robot configuration
|
|
6
|
+
records retain their existing backend import paths as re-exports of these
|
|
7
|
+
same objects.
|
|
11
8
|
|
|
12
9
|
Relocated so far — Wave C1: ``pose`` (``Pose``), ``materials`` (``Material``,
|
|
13
10
|
``SurfaceMaterial``), ``physics`` (``Physics``, ``Collision``, ``RigidPhysics``,
|
|
@@ -20,7 +17,7 @@ decision — to resolve that exact name collision), and ``light`` (``Light``,
|
|
|
20
17
|
``RoughTerrain``), ``primitives`` (``PrimitiveShape``, ``Cone``,
|
|
21
18
|
``Cuboid``, ``Sphere``, ``Cylinder``, ``Transform``), ``entity`` (``Entity``,
|
|
22
19
|
``AssetEntity``), and ``visual`` (``Visual``, ``VisualMarkerBase``,
|
|
23
|
-
``AxesMarker``, ``SphereMarker``, ``ArrowMarker``, ``PointMarker``).
|
|
20
|
+
``AxesMarker``, ``SphereMarker``, ``ArrowMarker``, ``PointMarker``, ``LineMarker``).
|
|
24
21
|
Wave C4: ``record`` (``RecordConfig`` — the MCAP recording configuration;
|
|
25
22
|
stdlib-only, so it classifies RELOCATE, and the backend recorder imports it
|
|
26
23
|
from here), and ``world`` (``World``, ``USDWorld``): ``Terrain`` is the
|
|
@@ -53,8 +50,9 @@ clearly in a submit-only installation.
|
|
|
53
50
|
Two relocation mechanisms select which types land here and how (see the derived
|
|
54
51
|
plan for the full worked example and rejected alternatives):
|
|
55
52
|
|
|
56
|
-
* **RELOCATE** — the class definition MOVES here
|
|
57
|
-
|
|
53
|
+
* **RELOCATE** — the class definition MOVES here. Dependency-bearing modules
|
|
54
|
+
that also exposed a shared value type can re-export its one definition for
|
|
55
|
+
existing callers; dependency-free modules move with their call sites.
|
|
58
56
|
Reserved for types with zero import-time *or* call-time dependency on
|
|
59
57
|
anything that cannot live in a zero-dependency package.
|
|
60
58
|
* **MIRROR** — a ``typing.Protocol`` (named ``*Protocol``, matching the
|
|
@@ -86,11 +84,17 @@ and cross-references for cryptic engine-style names) — that table, not this
|
|
|
86
84
|
docstring, is the source of truth for what lands here and when.
|
|
87
85
|
"""
|
|
88
86
|
|
|
87
|
+
from simulo.interfaces.authoring.action_channel import ActionChannel
|
|
89
88
|
from simulo.interfaces.authoring.actuator import ActuatorProtocol
|
|
90
89
|
from simulo.interfaces.authoring.asset import AssetSource, URDFAsset, USDAsset
|
|
91
90
|
from simulo.interfaces.authoring.checkpoint import PolicyCheckpoint
|
|
92
91
|
from simulo.interfaces.authoring.entity import AssetEntity, Entity
|
|
93
|
-
from simulo.interfaces.authoring.light import
|
|
92
|
+
from simulo.interfaces.authoring.light import (
|
|
93
|
+
DistantLight,
|
|
94
|
+
DomeLight,
|
|
95
|
+
Light,
|
|
96
|
+
LightConfig,
|
|
97
|
+
)
|
|
94
98
|
from simulo.interfaces.authoring.materials import Material, SurfaceMaterial
|
|
95
99
|
from simulo.interfaces.authoring.physics import (
|
|
96
100
|
Collision,
|
|
@@ -99,6 +103,7 @@ from simulo.interfaces.authoring.physics import (
|
|
|
99
103
|
Physics,
|
|
100
104
|
RigidPhysics,
|
|
101
105
|
)
|
|
106
|
+
from simulo.interfaces.authoring.policy_inputs import PolicyInput, PolicyInputSchema
|
|
102
107
|
from simulo.interfaces.authoring.pose import Pose
|
|
103
108
|
from simulo.interfaces.authoring.primitives import (
|
|
104
109
|
Cone,
|
|
@@ -108,10 +113,17 @@ from simulo.interfaces.authoring.primitives import (
|
|
|
108
113
|
Sphere,
|
|
109
114
|
Transform,
|
|
110
115
|
)
|
|
111
|
-
from simulo.interfaces.authoring.prop import PropProtocol, PropStateProtocol
|
|
116
|
+
from simulo.interfaces.authoring.prop import PropProtocol, PropStateProtocol, VariantPropProtocol
|
|
117
|
+
from simulo.interfaces.authoring.properties import (
|
|
118
|
+
ExternalForceProtocol,
|
|
119
|
+
PropPropertiesProtocol,
|
|
120
|
+
RobotPropertiesProtocol,
|
|
121
|
+
SceneGravityProtocol,
|
|
122
|
+
)
|
|
112
123
|
from simulo.interfaces.authoring.record import RecordConfig
|
|
113
124
|
from simulo.interfaces.authoring.robot import RobotProtocol, RobotStateProtocol
|
|
114
|
-
from simulo.interfaces.authoring.
|
|
125
|
+
from simulo.interfaces.authoring.robot_config import ActuatorGainsConfig, ArticulationConfig, JointConfig
|
|
126
|
+
from simulo.interfaces.authoring.scene import SceneGroupProtocol, SceneProtocol, SceneVariantsProtocol
|
|
115
127
|
from simulo.interfaces.authoring.sensor import CameraProtocol
|
|
116
128
|
from simulo.interfaces.authoring.terrain import (
|
|
117
129
|
GroundPlane,
|
|
@@ -120,9 +132,11 @@ from simulo.interfaces.authoring.terrain import (
|
|
|
120
132
|
Terrain,
|
|
121
133
|
TerrainConfig,
|
|
122
134
|
)
|
|
135
|
+
from simulo.interfaces.authoring.variants import ObjectVariants
|
|
123
136
|
from simulo.interfaces.authoring.visual import (
|
|
124
137
|
ArrowMarker,
|
|
125
138
|
AxesMarker,
|
|
139
|
+
LineMarker,
|
|
126
140
|
PointMarker,
|
|
127
141
|
SphereMarker,
|
|
128
142
|
Visual,
|
|
@@ -175,22 +189,39 @@ __all__ = [
|
|
|
175
189
|
"RecordConfig",
|
|
176
190
|
# injected policy input (dependency-free; loading is deferred to execution)
|
|
177
191
|
"PolicyCheckpoint",
|
|
192
|
+
"PolicyInput",
|
|
193
|
+
"PolicyInputSchema",
|
|
178
194
|
# visual
|
|
179
195
|
"VisualMarkerBase",
|
|
180
196
|
"AxesMarker",
|
|
181
197
|
"SphereMarker",
|
|
182
198
|
"ArrowMarker",
|
|
183
199
|
"PointMarker",
|
|
200
|
+
"LineMarker",
|
|
184
201
|
"Visual",
|
|
185
202
|
# scene (MIRROR — the concrete classes live in the backend package)
|
|
203
|
+
"SceneGravityProtocol",
|
|
186
204
|
"SceneProtocol",
|
|
187
205
|
"SceneGroupProtocol",
|
|
206
|
+
"SceneVariantsProtocol",
|
|
207
|
+
# scene variants (dependency-free authoring data)
|
|
208
|
+
"ObjectVariants",
|
|
209
|
+
# robot configuration (dependency-free owning definitions)
|
|
210
|
+
"ActuatorGainsConfig",
|
|
211
|
+
"ArticulationConfig",
|
|
212
|
+
"JointConfig",
|
|
188
213
|
# robot / prop / sensor / actuator (MIRROR — the concrete classes live in
|
|
189
214
|
# the backend package and explicitly inherit these Protocols)
|
|
215
|
+
"ExternalForceProtocol",
|
|
216
|
+
"RobotPropertiesProtocol",
|
|
217
|
+
"PropPropertiesProtocol",
|
|
190
218
|
"RobotProtocol",
|
|
191
219
|
"RobotStateProtocol",
|
|
192
220
|
"PropProtocol",
|
|
193
221
|
"PropStateProtocol",
|
|
222
|
+
"VariantPropProtocol",
|
|
194
223
|
"CameraProtocol",
|
|
195
224
|
"ActuatorProtocol",
|
|
225
|
+
# action-to-joint metadata
|
|
226
|
+
"ActionChannel",
|
|
196
227
|
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Dependency-free metadata for a policy action column."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
ActionKind = Literal["position", "velocity", "effort", "command"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class ActionChannel:
|
|
11
|
+
"""One named policy action and the command it represents.
|
|
12
|
+
|
|
13
|
+
``joint`` is ``None`` for a task-level command. Resolver code validates
|
|
14
|
+
the value against a task's action width and the engine's available joints.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
name: str
|
|
18
|
+
joint: str | None
|
|
19
|
+
kind: ActionKind
|
|
20
|
+
scale: float = 1.0
|
|
21
|
+
offset: float = 0.0
|
|
22
|
+
hold_steps: int = 1
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""Dependency-free declarations for raw actor and critic inputs.
|
|
2
|
+
|
|
3
|
+
Images must be batched RGB uint8 NHWC. The contract requires future trainer
|
|
4
|
+
and export consumers to convert the current minibatch to NCHW float and divide
|
|
5
|
+
by 255; ``center`` also subtracts 0.5. These transforms belong in the actor's
|
|
6
|
+
graph. This module checks declarations and raw values, not graph behavior.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import re
|
|
14
|
+
from collections.abc import Mapping
|
|
15
|
+
from dataclasses import asdict, dataclass
|
|
16
|
+
from typing import Any, Literal
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
20
|
+
class PolicyInput:
|
|
21
|
+
"""One named input, with ``shape`` excluding the environment batch.
|
|
22
|
+
|
|
23
|
+
Names match ``[A-Za-z][A-Za-z0-9_]*``; shape dimensions are positive.
|
|
24
|
+
Tuple and list shapes are accepted and stored as tuples. Defaults are
|
|
25
|
+
``dtype="float32"``, ``kind="numeric"`` and ``preprocessing="none"``.
|
|
26
|
+
Numeric inputs require float32, no camera and no preprocessing. Image
|
|
27
|
+
inputs require uint8, shape ``(H, W, 3)`` and a nonblank camera scene name:
|
|
28
|
+
the last segment of ``scene.add(camera, at="/World/<name>")``. Full paths
|
|
29
|
+
are rejected. LearningEnv setup requires that camera's purpose to be
|
|
30
|
+
``"policy"``. Two policy cameras cannot share a scene name.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
name: str
|
|
34
|
+
shape: tuple[int, ...]
|
|
35
|
+
dtype: str = "float32"
|
|
36
|
+
kind: Literal["numeric", "image"] = "numeric"
|
|
37
|
+
camera: str | None = None
|
|
38
|
+
preprocessing: Literal["none", "center"] = "none"
|
|
39
|
+
|
|
40
|
+
def __post_init__(self) -> None:
|
|
41
|
+
label = f"Input {self.name!r}"
|
|
42
|
+
if isinstance(self.shape, list):
|
|
43
|
+
object.__setattr__(self, "shape", tuple(self.shape))
|
|
44
|
+
if not isinstance(self.name, str) or not re.fullmatch(r"[A-Za-z][A-Za-z0-9_]*", self.name):
|
|
45
|
+
raise ValueError(f"{label}: name must start with a letter and contain only letters, digits or underscores.")
|
|
46
|
+
if (
|
|
47
|
+
not isinstance(self.shape, tuple)
|
|
48
|
+
or not self.shape
|
|
49
|
+
or any(type(dim) is not int or dim <= 0 for dim in self.shape)
|
|
50
|
+
):
|
|
51
|
+
raise ValueError(f"{label}: shape must be a nonempty tuple of positive dimensions, excluding batch.")
|
|
52
|
+
if self.kind not in ("numeric", "image"):
|
|
53
|
+
raise ValueError(f"{label}: kind must be 'numeric' or 'image'.")
|
|
54
|
+
if self.preprocessing not in ("none", "center"):
|
|
55
|
+
raise ValueError(f"{label}: preprocessing must be 'none' or 'center'.")
|
|
56
|
+
if self.kind == "image":
|
|
57
|
+
if self.dtype != "uint8":
|
|
58
|
+
raise ValueError(f"{label}: image dtype must be uint8 RGB NHWC, got {self.dtype!r}.")
|
|
59
|
+
if len(self.shape) != 3 or self.shape[-1] != 3:
|
|
60
|
+
raise ValueError(f"{label}: image shape must be (H, W, 3) RGB NHWC, excluding batch, got {self.shape}.")
|
|
61
|
+
if not isinstance(self.camera, str) or not self.camera.strip():
|
|
62
|
+
raise ValueError(f"{label}: image inputs require a camera identity.")
|
|
63
|
+
if "/" in self.camera or self.camera != self.camera.strip():
|
|
64
|
+
raise ValueError(f"{label}: camera must be a scene name, the last segment of scene.add(..., at=...).")
|
|
65
|
+
elif self.dtype != "float32" or self.camera is not None or self.preprocessing != "none":
|
|
66
|
+
raise ValueError(f"{label}: numeric inputs require float32, no camera, and preprocessing='none'.")
|
|
67
|
+
|
|
68
|
+
def to_dict(self) -> dict[str, Any]:
|
|
69
|
+
"""Return JSON metadata, converting the stored tuple shape to a list."""
|
|
70
|
+
return {**asdict(self), "shape": list(self.shape)}
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def from_dict(cls, value: Mapping[str, Any]) -> PolicyInput:
|
|
74
|
+
"""Parse JSON, converting list shapes to tuples; reject unknown fields."""
|
|
75
|
+
if not isinstance(value, Mapping):
|
|
76
|
+
raise ValueError("Input declaration must be an object.")
|
|
77
|
+
allowed = {"name", "shape", "dtype", "kind", "camera", "preprocessing"}
|
|
78
|
+
if set(value) - allowed or not {"name", "shape"} <= set(value):
|
|
79
|
+
raise ValueError(f"Input {value.get('name')!r}: declaration has missing or unknown fields.")
|
|
80
|
+
shape = value["shape"]
|
|
81
|
+
if not isinstance(shape, (list, tuple)):
|
|
82
|
+
raise ValueError(f"Input {value['name']!r}: shape must be an array of positive dimensions.")
|
|
83
|
+
return cls(**{**value, "shape": tuple(shape)})
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
87
|
+
class PolicyInputSchema:
|
|
88
|
+
"""Ordered actor inputs and optional separate training-only critic inputs.
|
|
89
|
+
|
|
90
|
+
``critic=None`` means the critic uses exactly the actor declarations.
|
|
91
|
+
Future export and playback consumers must use only ``actor``. A critic
|
|
92
|
+
declaration sharing an actor name must be identical. Actor inputs omitted
|
|
93
|
+
from an explicit critic are not supplied to that critic. Order is part of
|
|
94
|
+
checkpoint compatibility, including numeric concatenation order.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
actor: tuple[PolicyInput, ...]
|
|
98
|
+
critic: tuple[PolicyInput, ...] | None = None
|
|
99
|
+
|
|
100
|
+
def __post_init__(self) -> None:
|
|
101
|
+
for role, inputs in (("actor", self.actor), ("critic", self.critic_inputs)):
|
|
102
|
+
if not isinstance(inputs, tuple) or not inputs:
|
|
103
|
+
raise ValueError(f"{role} inputs must be a nonempty tuple of PolicyInput declarations.")
|
|
104
|
+
seen: set[str] = set()
|
|
105
|
+
for spec in inputs:
|
|
106
|
+
if not isinstance(spec, PolicyInput):
|
|
107
|
+
raise ValueError(f"{role} inputs must contain PolicyInput declarations.")
|
|
108
|
+
if spec.name in seen:
|
|
109
|
+
raise ValueError(f"{role} input {spec.name!r}: duplicate input name.")
|
|
110
|
+
seen.add(spec.name)
|
|
111
|
+
actor = {spec.name: spec for spec in self.actor}
|
|
112
|
+
for spec in self.critic_inputs:
|
|
113
|
+
if spec.name in actor and spec != actor[spec.name]:
|
|
114
|
+
raise ValueError(
|
|
115
|
+
f"critic input {spec.name!r}: declaration contradicts the actor input of the same name."
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def critic_inputs(self) -> tuple[PolicyInput, ...]:
|
|
120
|
+
"""Effective critic declarations, defaulting to actor inputs."""
|
|
121
|
+
return self.actor if self.critic is None else self.critic
|
|
122
|
+
|
|
123
|
+
def to_dict(self) -> dict[str, Any]:
|
|
124
|
+
"""Return the actor/critic schema for a checkpoint sidecar."""
|
|
125
|
+
return {
|
|
126
|
+
"actor": [spec.to_dict() for spec in self.actor],
|
|
127
|
+
"critic": None if self.critic is None else [spec.to_dict() for spec in self.critic],
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def from_dict(cls, value: Mapping[str, Any]) -> PolicyInputSchema:
|
|
132
|
+
"""Parse a schema without permitting undeclared metadata."""
|
|
133
|
+
if not isinstance(value, Mapping) or set(value) != {"actor", "critic"}:
|
|
134
|
+
raise ValueError("Policy input schema requires exactly actor and critic fields.")
|
|
135
|
+
actor, critic = value["actor"], value["critic"]
|
|
136
|
+
if not isinstance(actor, (list, tuple)) or (critic is not None and not isinstance(critic, (list, tuple))):
|
|
137
|
+
raise ValueError("Policy input schema actor and critic must be arrays (critic may be null).")
|
|
138
|
+
return cls(
|
|
139
|
+
actor=tuple(PolicyInput.from_dict(spec) for spec in actor),
|
|
140
|
+
critic=None if critic is None else tuple(PolicyInput.from_dict(spec) for spec in critic),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def fingerprint(self) -> str:
|
|
145
|
+
"""SHA-256 of ordered inputs, including camera and preprocessing.
|
|
146
|
+
|
|
147
|
+
An omitted critic and an explicitly identical critic are equivalent.
|
|
148
|
+
Batch size is absent, so a checkpoint can change parallelism.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
def canonical(spec: PolicyInput) -> dict[str, Any]:
|
|
152
|
+
# Pin v1's subset: future defaulted fields do not invalidate hashes.
|
|
153
|
+
return {name: getattr(spec, name) for name in ("name", "shape", "dtype", "kind", "camera", "preprocessing")}
|
|
154
|
+
|
|
155
|
+
metadata = {
|
|
156
|
+
"version": "simulo.policy-input-fingerprint.v1",
|
|
157
|
+
"actor": [canonical(spec) for spec in self.actor],
|
|
158
|
+
"critic": [canonical(spec) for spec in self.critic_inputs],
|
|
159
|
+
}
|
|
160
|
+
return hashlib.sha256(json.dumps(metadata, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
|
|
161
|
+
|
|
162
|
+
def validate_values(
|
|
163
|
+
self,
|
|
164
|
+
actor: Any,
|
|
165
|
+
critic: Any,
|
|
166
|
+
*,
|
|
167
|
+
batch_size: int | None = None,
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Check raw names, shapes, dtypes and batch size at LearningEnv._init_spaces.
|
|
170
|
+
|
|
171
|
+
Values need only ``shape`` and ``dtype`` attributes. No engine or tensor
|
|
172
|
+
package is imported. One declared numeric input may use the legacy
|
|
173
|
+
tensor return; multiple or image inputs require a named mapping. Values
|
|
174
|
+
must have a leading num_envs batch. Failures raise ValueError naming the
|
|
175
|
+
role and input. Legacy tasks with no schema are not checked by setup.
|
|
176
|
+
"""
|
|
177
|
+
expected_batch = batch_size
|
|
178
|
+
for role, specs, values in (("actor", self.actor, actor), ("critic", self.critic_inputs, critic)):
|
|
179
|
+
if not isinstance(values, Mapping):
|
|
180
|
+
if len(specs) != 1 or specs[0].kind != "numeric":
|
|
181
|
+
raise ValueError(f"{role} input {specs[0].name!r}: named inputs require a mapping.")
|
|
182
|
+
values = {specs[0].name: values}
|
|
183
|
+
declared = {spec.name for spec in specs}
|
|
184
|
+
for name in values:
|
|
185
|
+
if name not in declared:
|
|
186
|
+
raise ValueError(f"{role} input {name!r}: unknown input name.")
|
|
187
|
+
for spec in specs:
|
|
188
|
+
if spec.name not in values:
|
|
189
|
+
raise ValueError(f"{role} input {spec.name!r}: missing input.")
|
|
190
|
+
value = values[spec.name]
|
|
191
|
+
shape = tuple(getattr(value, "shape", ()))
|
|
192
|
+
if (
|
|
193
|
+
len(shape) != len(spec.shape) + 1
|
|
194
|
+
or shape[1:] != spec.shape
|
|
195
|
+
or type(shape[0]) is not int
|
|
196
|
+
or shape[0] <= 0
|
|
197
|
+
):
|
|
198
|
+
raise ValueError(
|
|
199
|
+
f"{role} input {spec.name!r}: expected batched shape (N, {spec.shape}), got {shape}."
|
|
200
|
+
)
|
|
201
|
+
if expected_batch is None:
|
|
202
|
+
expected_batch = shape[0]
|
|
203
|
+
if shape[0] != expected_batch:
|
|
204
|
+
raise ValueError(f"{role} input {spec.name!r}: expected batch {expected_batch}, got {shape[0]}.")
|
|
205
|
+
dtype = str(getattr(value, "dtype", "")).removeprefix("torch.")
|
|
206
|
+
if dtype != spec.dtype:
|
|
207
|
+
raise ValueError(f"{role} input {spec.name!r}: expected dtype {spec.dtype}, got {dtype!r}.")
|
{simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/prop.py
RENAMED
|
@@ -154,15 +154,21 @@ class PropProtocol(Protocol):
|
|
|
154
154
|
|
|
155
155
|
@property
|
|
156
156
|
def default_pose(self) -> TensorLike:
|
|
157
|
-
"""The prop's spawn pose in the **world** frame
|
|
157
|
+
"""The prop's spawn pose in the **world** frame.
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
``state.pose`` and directly usable as
|
|
162
|
-
reset. Index it with the environments being
|
|
163
|
-
(``prop.default_pose[env_ids]``), perturb, and write it back with
|
|
159
|
+
For a prop added with ``per_environment=True`` (the default), the pose
|
|
160
|
+
has shape ``(num_envs, 7)`` and includes each environment's origin, so
|
|
161
|
+
it is directly comparable with ``state.pose`` and directly usable as
|
|
162
|
+
the base for a randomised reset. Index it with the environments being
|
|
163
|
+
reset (``prop.default_pose[env_ids]``), perturb, and write it back with
|
|
164
164
|
:meth:`set_pose`.
|
|
165
165
|
|
|
166
|
+
For a prop added with ``per_environment=False``, this is one row
|
|
167
|
+
containing its authored world pose with no environment origin. A
|
|
168
|
+
shared prop is supported only when the scene has one environment;
|
|
169
|
+
scene building refuses ``per_environment=False`` for a prop when
|
|
170
|
+
``num_envs > 1``.
|
|
171
|
+
|
|
166
172
|
Raises ``RuntimeError`` until the simulation runtime has attached the
|
|
167
173
|
prop.
|
|
168
174
|
"""
|
|
@@ -208,3 +214,21 @@ class PropProtocol(Protocol):
|
|
|
208
214
|
environment.
|
|
209
215
|
"""
|
|
210
216
|
...
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@runtime_checkable
|
|
220
|
+
class VariantPropProtocol(PropProtocol, Protocol):
|
|
221
|
+
"""The opt-in prop surface for a scene item with per-copy variants.
|
|
222
|
+
|
|
223
|
+
:class:`PropProtocol` remains the legacy-compatible pose contract. A task
|
|
224
|
+
that chooses scene variants depends on this narrower capability instead.
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def variant_index(self) -> TensorLike:
|
|
229
|
+
"""Active option index for every environment copy, shape ``(num_envs,)``."""
|
|
230
|
+
...
|
|
231
|
+
|
|
232
|
+
def set_variant(self, index: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
233
|
+
"""Switch selected copies to an option index. Supported for pool variants."""
|
|
234
|
+
...
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Optional per-copy physics capabilities, independent of the base authoring protocols.
|
|
2
|
+
|
|
3
|
+
Backends opt into each capability they implement. The frozen legacy backend
|
|
4
|
+
continues to satisfy RobotProtocol, PropProtocol and SceneProtocol without them.
|
|
5
|
+
K is the number of selected copies; B, S and J are body, shape and joint counts.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional, Protocol, runtime_checkable
|
|
9
|
+
|
|
10
|
+
from simulo.interfaces.runtime.tensors import TensorLike
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@runtime_checkable
|
|
14
|
+
class RobotPropertiesProtocol(Protocol):
|
|
15
|
+
"""Per-copy articulation properties supported by the engine backend."""
|
|
16
|
+
|
|
17
|
+
def set_mass(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
18
|
+
"""Set link mass, shape ``(K, B)``, for selected copies."""
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
def set_center_of_mass(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
22
|
+
"""Set link centres of mass in body frames, shape ``(K, B, 3)``."""
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
def set_friction(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
26
|
+
"""Set shape friction, shape ``(K, S)``, for selected copies."""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
def set_restitution(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
30
|
+
"""Set shape restitution, shape ``(K, S)``, for selected copies."""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
def set_joint_gains(
|
|
34
|
+
self,
|
|
35
|
+
stiffness: TensorLike,
|
|
36
|
+
damping: TensorLike,
|
|
37
|
+
env_ids: Optional[TensorLike] = ...,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Set joint stiffness and damping, each shape ``(K, J)``."""
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
def set_joint_armature(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
43
|
+
"""Set output-side joint armature, shape ``(K, J)``, for selected copies."""
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@runtime_checkable
|
|
48
|
+
class PropPropertiesProtocol(Protocol):
|
|
49
|
+
"""Per-copy rigid-prop properties supported by the engine backend."""
|
|
50
|
+
|
|
51
|
+
def set_mass(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
52
|
+
"""Set the prop body mass, shape ``(K, 1)``, for selected copies."""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
def set_center_of_mass(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
56
|
+
"""Set the prop centre of mass in its body frame, shape ``(K, 1, 3)``."""
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
def set_friction(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
60
|
+
"""Set prop-shape friction, shape ``(K, S)``, for selected copies."""
|
|
61
|
+
...
|
|
62
|
+
|
|
63
|
+
def set_restitution(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
64
|
+
"""Set prop-shape restitution, shape ``(K, S)``, for selected copies."""
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
def set_scale(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
68
|
+
"""Set primitive-shape scale, shape ``(K, S, 3)``, for selected copies."""
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@runtime_checkable
|
|
73
|
+
class ExternalForceProtocol(Protocol):
|
|
74
|
+
"""World-frame forces and torques for selected copies of an entity."""
|
|
75
|
+
|
|
76
|
+
def apply_external_force(
|
|
77
|
+
self,
|
|
78
|
+
forces: TensorLike,
|
|
79
|
+
torques: TensorLike,
|
|
80
|
+
env_ids: Optional[TensorLike] = ...,
|
|
81
|
+
) -> None:
|
|
82
|
+
"""Apply forces and torques, each shape ``(K, B, 3)``; props have B=1."""
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@runtime_checkable
|
|
87
|
+
class SceneGravityProtocol(Protocol):
|
|
88
|
+
"""Per-copy gravity supported by the engine backend."""
|
|
89
|
+
|
|
90
|
+
def set_gravity(self, values: TensorLike, env_ids: Optional[TensorLike] = ...) -> None:
|
|
91
|
+
"""Set world-frame gravity, shape ``(K, 3)``, for selected copies."""
|
|
92
|
+
...
|
{simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/record.py
RENAMED
|
@@ -195,6 +195,11 @@ class RecordConfig:
|
|
|
195
195
|
profile precedence can distinguish "user passed False" from "default
|
|
196
196
|
None". Do not edit defaults to plain ``bool`` — the silent-default
|
|
197
197
|
mitigation depends on the tri-state.
|
|
198
|
+
|
|
199
|
+
``record_all_envs`` expands numeric recording data only. Policy camera
|
|
200
|
+
video always uses ``env_indices`` (default: environment 0), so host frame
|
|
201
|
+
buffers and encoder count are bounded by the selected environments and
|
|
202
|
+
declared cameras, independently of the total environment count.
|
|
198
203
|
"""
|
|
199
204
|
|
|
200
205
|
output_path: Optional[Union[str, "os.PathLike[str]", Path]] = None
|
{simulo_interfaces-0.23.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/robot.py
RENAMED
|
@@ -64,6 +64,7 @@ from typing import Any, Dict, List, Optional, Protocol, Tuple, runtime_checkable
|
|
|
64
64
|
|
|
65
65
|
from simulo.interfaces.authoring.asset import AssetSource
|
|
66
66
|
from simulo.interfaces.authoring.pose import Pose
|
|
67
|
+
from simulo.interfaces.authoring.robot_config import ArticulationConfig, JointConfig
|
|
67
68
|
from simulo.interfaces.runtime.tensors import TensorLike
|
|
68
69
|
|
|
69
70
|
|
|
@@ -183,17 +184,21 @@ class RobotProtocol(Protocol):
|
|
|
183
184
|
scale: Tuple[float, float, float]
|
|
184
185
|
"""Per-axis spawn scale factor."""
|
|
185
186
|
|
|
186
|
-
articulation:
|
|
187
|
+
articulation: ArticulationConfig
|
|
187
188
|
"""Articulation physics and control configuration."""
|
|
188
189
|
|
|
189
|
-
joint_config: Optional[List[
|
|
190
|
+
joint_config: Optional[List[JointConfig]]
|
|
190
191
|
"""Optional per-joint configurations."""
|
|
191
192
|
|
|
192
193
|
disable_gravity: bool
|
|
193
194
|
"""If ``True``, gravity is disabled for this robot."""
|
|
194
195
|
|
|
195
196
|
actuator_gains: Optional[Dict[str, Any]]
|
|
196
|
-
"""Optional mapping of actuator-group names to gain configurations.
|
|
197
|
+
"""Optional mapping of actuator-group names to gain configurations.
|
|
198
|
+
|
|
199
|
+
Catalog robots support only the ``"all_joints"`` key; other keys are ignored
|
|
200
|
+
with a warning.
|
|
201
|
+
"""
|
|
197
202
|
|
|
198
203
|
# -- Identity and attachment ------------------------------------------
|
|
199
204
|
|
|
@@ -257,12 +262,19 @@ class RobotProtocol(Protocol):
|
|
|
257
262
|
|
|
258
263
|
@property
|
|
259
264
|
def default_pose(self) -> TensorLike:
|
|
260
|
-
"""Base pose that :meth:`reset` writes in the world frame
|
|
261
|
-
|
|
262
|
-
position in m followed by a
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
265
|
+
"""Base pose that :meth:`reset` writes in the world frame.
|
|
266
|
+
|
|
267
|
+
Columns are ``[x, y, z, qw, qx, qy, qz]``: position in m followed by a
|
|
268
|
+
w-first unit quaternion. For a robot added with
|
|
269
|
+
``per_environment=True`` (the default), the shape is ``(num_envs, 7)``
|
|
270
|
+
and each environment's origin is included. For a robot added with
|
|
271
|
+
``per_environment=False``, this is one row containing its authored
|
|
272
|
+
world pose with no environment origin. A shared robot is supported
|
|
273
|
+
only when the scene has one environment; scene building refuses
|
|
274
|
+
``per_environment=False`` for a robot when ``num_envs > 1``. Available
|
|
275
|
+
from ``on_start`` onward. Each read returns a new tensor, so writing
|
|
276
|
+
into it cannot change a later reset.
|
|
277
|
+
"""
|
|
266
278
|
...
|
|
267
279
|
|
|
268
280
|
@property
|