simulo-interfaces 0.22.0__tar.gz → 0.24.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.22.0/src/simulo_interfaces.egg-info → simulo_interfaces-0.24.0}/PKG-INFO +1 -1
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/pyproject.toml +1 -1
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/__init__.py +16 -10
- simulo_interfaces-0.24.0/src/simulo/interfaces/authoring/policy_inputs.py +207 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/prop.py +12 -6
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/record.py +35 -2
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/robot.py +96 -28
- simulo_interfaces-0.24.0/src/simulo/interfaces/authoring/robot_config.py +82 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/scene.py +6 -1
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/sensor.py +9 -1
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/terrain.py +2 -4
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/__init__.py +91 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/asset_catalog.py +2 -2
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/enums.py +2 -2
- simulo_interfaces-0.24.0/src/simulo/interfaces/platform/evaluation.py +457 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/manifest.py +7 -6
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/onnx_bundle.py +192 -45
- simulo_interfaces-0.24.0/src/simulo/interfaces/platform/policy_checkpoint.py +89 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/runs.py +20 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/submit.py +13 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/systems.py +6 -7
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/runtime/__init__.py +2 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/runtime/components.py +3 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/runtime/env.py +12 -0
- simulo_interfaces-0.24.0/src/simulo/interfaces/runtime/policy_frames.py +42 -0
- simulo_interfaces-0.24.0/src/simulo/interfaces/runtime/task.py +141 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/runtime/trainer.py +4 -12
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0/src/simulo_interfaces.egg-info}/PKG-INFO +1 -1
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo_interfaces.egg-info/SOURCES.txt +5 -0
- simulo_interfaces-0.22.0/src/simulo/interfaces/runtime/task.py +0 -83
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/MANIFEST.in +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/PYPI.md +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/setup.cfg +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/__init__.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/actuator.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/asset.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/checkpoint.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/entity.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/light.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/materials.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/physics.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/pose.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/primitives.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/visual.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/world.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/exceptions.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/ids.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/_payload.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/app.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/asset.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/callbacks.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/debug.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/domain.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/job_events.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/outputs.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/preview.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/runtime.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/platform/volume.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/py.typed +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/runtime/anomaly.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/runtime/player.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/runtime/policy.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/runtime/tensors.py +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo_interfaces.egg-info/dependency_links.txt +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo_interfaces.egg-info/requires.txt +0 -0
- {simulo_interfaces-0.22.0 → simulo_interfaces-0.24.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.24.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.22.0 → simulo_interfaces-0.24.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``,
|
|
@@ -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
|
|
@@ -99,6 +97,7 @@ from simulo.interfaces.authoring.physics import (
|
|
|
99
97
|
Physics,
|
|
100
98
|
RigidPhysics,
|
|
101
99
|
)
|
|
100
|
+
from simulo.interfaces.authoring.policy_inputs import PolicyInput, PolicyInputSchema
|
|
102
101
|
from simulo.interfaces.authoring.pose import Pose
|
|
103
102
|
from simulo.interfaces.authoring.primitives import (
|
|
104
103
|
Cone,
|
|
@@ -111,6 +110,7 @@ from simulo.interfaces.authoring.primitives import (
|
|
|
111
110
|
from simulo.interfaces.authoring.prop import PropProtocol, PropStateProtocol
|
|
112
111
|
from simulo.interfaces.authoring.record import RecordConfig
|
|
113
112
|
from simulo.interfaces.authoring.robot import RobotProtocol, RobotStateProtocol
|
|
113
|
+
from simulo.interfaces.authoring.robot_config import ActuatorGainsConfig, ArticulationConfig, JointConfig
|
|
114
114
|
from simulo.interfaces.authoring.scene import SceneGroupProtocol, SceneProtocol
|
|
115
115
|
from simulo.interfaces.authoring.sensor import CameraProtocol
|
|
116
116
|
from simulo.interfaces.authoring.terrain import (
|
|
@@ -175,6 +175,8 @@ __all__ = [
|
|
|
175
175
|
"RecordConfig",
|
|
176
176
|
# injected policy input (dependency-free; loading is deferred to execution)
|
|
177
177
|
"PolicyCheckpoint",
|
|
178
|
+
"PolicyInput",
|
|
179
|
+
"PolicyInputSchema",
|
|
178
180
|
# visual
|
|
179
181
|
"VisualMarkerBase",
|
|
180
182
|
"AxesMarker",
|
|
@@ -185,6 +187,10 @@ __all__ = [
|
|
|
185
187
|
# scene (MIRROR — the concrete classes live in the backend package)
|
|
186
188
|
"SceneProtocol",
|
|
187
189
|
"SceneGroupProtocol",
|
|
190
|
+
# robot configuration (dependency-free owning definitions)
|
|
191
|
+
"ActuatorGainsConfig",
|
|
192
|
+
"ArticulationConfig",
|
|
193
|
+
"JointConfig",
|
|
188
194
|
# robot / prop / sensor / actuator (MIRROR — the concrete classes live in
|
|
189
195
|
# the backend package and explicitly inherit these Protocols)
|
|
190
196
|
"RobotProtocol",
|
|
@@ -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.22.0 → simulo_interfaces-0.24.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
|
"""
|
{simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/record.py
RENAMED
|
@@ -44,10 +44,13 @@ fingerprint that overrides the auto-collected one when present).
|
|
|
44
44
|
|
|
45
45
|
from __future__ import annotations
|
|
46
46
|
|
|
47
|
+
import math
|
|
47
48
|
import os
|
|
49
|
+
import sys
|
|
48
50
|
from dataclasses import dataclass, field
|
|
51
|
+
from numbers import Real
|
|
49
52
|
from pathlib import Path
|
|
50
|
-
from typing import Any, Dict, Literal, Mapping, Optional, Sequence, Tuple, Union
|
|
53
|
+
from typing import Any, Dict, Literal, Mapping, Optional, Sequence, SupportsFloat, Tuple, Union
|
|
51
54
|
|
|
52
55
|
# All include_* field names this config exposes. Centralizing avoids drift
|
|
53
56
|
# between the dataclass declaration, _PROFILE_DEFAULTS, and resolve_includes.
|
|
@@ -192,6 +195,11 @@ class RecordConfig:
|
|
|
192
195
|
profile precedence can distinguish "user passed False" from "default
|
|
193
196
|
None". Do not edit defaults to plain ``bool`` — the silent-default
|
|
194
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.
|
|
195
203
|
"""
|
|
196
204
|
|
|
197
205
|
output_path: Optional[Union[str, "os.PathLike[str]", Path]] = None
|
|
@@ -253,7 +261,14 @@ class RecordConfig:
|
|
|
253
261
|
video_fallback: Literal["software", "error"] = "software"
|
|
254
262
|
video_codec: Literal["h264", "hevc", "av1"] = "h264"
|
|
255
263
|
video_bitrate: Optional[str] = "8M"
|
|
256
|
-
|
|
264
|
+
# ``None`` leaves cadence to the frame producer. The encoder then uses its
|
|
265
|
+
# documented 30 fps default, but the recorder omits ``video_fps`` from
|
|
266
|
+
# /run/start because packet timestamps, not an encoder setting, describe
|
|
267
|
+
# that producer-owned cadence. Callers such as evaluation filming set the
|
|
268
|
+
# measured cadence explicitly.
|
|
269
|
+
# Values must also fit a finite IEEE-754 double because both supported
|
|
270
|
+
# encoders and the MCAP JSON record use that representation.
|
|
271
|
+
video_fps: Optional[SupportsFloat] = None
|
|
257
272
|
video_gop: Optional[int] = 30
|
|
258
273
|
video_storage: Literal["embedded", "sidecar"] = "embedded"
|
|
259
274
|
# When True (default), the recorder writes a parallel
|
|
@@ -300,6 +315,24 @@ class RecordConfig:
|
|
|
300
315
|
raise ValueError(f"RecordConfig.profile must be one of {VALID_PROFILES}; got {self.profile!r}")
|
|
301
316
|
if self.record_every_n_steps < 1:
|
|
302
317
|
raise ValueError(f"RecordConfig.record_every_n_steps must be >= 1; got {self.record_every_n_steps}")
|
|
318
|
+
try:
|
|
319
|
+
video_fps = None if self.video_fps is None else float(self.video_fps)
|
|
320
|
+
except (OverflowError, TypeError, ValueError):
|
|
321
|
+
video_fps = None
|
|
322
|
+
if self.video_fps is not None and (
|
|
323
|
+
isinstance(self.video_fps, bool)
|
|
324
|
+
or not isinstance(self.video_fps, Real)
|
|
325
|
+
or video_fps is None
|
|
326
|
+
or not math.isfinite(video_fps)
|
|
327
|
+
# 0.1 fps is a policy floor that keeps rates sensible; ffmpeg's
|
|
328
|
+
# rational conversion only rounds to zero far below it.
|
|
329
|
+
or video_fps < 0.1
|
|
330
|
+
or video_fps > sys.float_info.max
|
|
331
|
+
):
|
|
332
|
+
raise ValueError(
|
|
333
|
+
"RecordConfig.video_fps must be a finite real number representable by the encoder and at least 0.1 "
|
|
334
|
+
f"when set; got {self.video_fps!r}"
|
|
335
|
+
)
|
|
303
336
|
# env_indices: accept any sequence of ints, including a torch tensor —
|
|
304
337
|
# but normalize to a tuple of ints for hashability + reproducibility.
|
|
305
338
|
try:
|
{simulo_interfaces-0.22.0 → simulo_interfaces-0.24.0}/src/simulo/interfaces/authoring/robot.py
RENAMED
|
@@ -7,7 +7,7 @@ and reset paths all reach through a live engine handle), so its
|
|
|
7
7
|
*implementation* cannot live in a zero-dependency package. What CAN live here
|
|
8
8
|
is its declared public surface: this Protocol names exactly what authoring
|
|
9
9
|
code may rely on when it holds a robot — construction-time configuration,
|
|
10
|
-
joint/body queries, command targets, state reads, and reset.
|
|
10
|
+
joint/body queries, command targets, default state, live state reads, and reset.
|
|
11
11
|
|
|
12
12
|
The backend's concrete ``Robot`` class explicitly inherits this Protocol
|
|
13
13
|
(nominal conformance). Enforcement is the standard four layers for a mirror —
|
|
@@ -23,15 +23,16 @@ The Protocol stays structural for any other implementer.
|
|
|
23
23
|
|
|
24
24
|
``state`` is DECLARED, as ``RobotStateProtocol`` — the grouped live-state
|
|
25
25
|
contract. It is the supported, typed way to read a robot's simulation state
|
|
26
|
-
(``robot.state.joint_positions``, ``robot.state.pose``, …):
|
|
26
|
+
(``robot.state.joint_positions``, ``robot.state.pose``, …): eight read-only,
|
|
27
27
|
batched members whose names are plain English rather than engine vocabulary.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
Six members are zero-copy live views of the simulation's buffers. The two
|
|
29
|
+
base-frame velocity members are different: the engine rotates the current
|
|
30
|
+
world-frame velocities into the base frame on every read, returning a new
|
|
31
|
+
tensor. That small computation and allocation are the cost of those two
|
|
32
|
+
accesses, and writing into either result does not reach the simulation.
|
|
33
|
+
Unlike ``internals``, all eight names ARE a compatibility promise: they keep
|
|
34
|
+
working across engine upgrades, because the backend maps each one to whatever
|
|
35
|
+
the engine calls it.
|
|
35
36
|
|
|
36
37
|
Deliberate scoping notes, so they are not mistaken for drift (each is an
|
|
37
38
|
entry in the backend conformance test's documented-omissions allowlist):
|
|
@@ -63,32 +64,37 @@ from typing import Any, Dict, List, Optional, Protocol, Tuple, runtime_checkable
|
|
|
63
64
|
|
|
64
65
|
from simulo.interfaces.authoring.asset import AssetSource
|
|
65
66
|
from simulo.interfaces.authoring.pose import Pose
|
|
67
|
+
from simulo.interfaces.authoring.robot_config import ArticulationConfig, JointConfig
|
|
66
68
|
from simulo.interfaces.runtime.tensors import TensorLike
|
|
67
69
|
|
|
68
70
|
|
|
69
71
|
@runtime_checkable
|
|
70
72
|
class RobotStateProtocol(Protocol):
|
|
71
|
-
"""The live robot-state contract — batched
|
|
73
|
+
"""The live robot-state contract — batched and read-only.
|
|
72
74
|
|
|
73
75
|
Obtained from a robot as ``robot.state``; never constructed directly.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
Six members (``pose``, the two world-frame velocities, and the three
|
|
77
|
+
``joint_*`` members) are zero-copy live views of the simulation's own
|
|
78
|
+
buffers. ``linear_velocity_in_base_frame`` and
|
|
79
|
+
``angular_velocity_in_base_frame`` are computed from the current base
|
|
80
|
+
orientation on every read. Each of those reads returns a new tensor,
|
|
81
|
+
costs a small computation, and cannot modify the simulation when written
|
|
82
|
+
into. All members carry the environment batch as their leading dimension
|
|
83
|
+
(the scene's ``num_envs``).
|
|
78
84
|
|
|
79
85
|
**Read a member each time you need it — do not hold the returned tensor
|
|
80
|
-
across steps, and do not write into it.** The
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
(``robot.state`` itself) may be held freely; the tensors its
|
|
85
|
-
return should be re-read at each use.
|
|
86
|
+
across steps, and do not write into it.** The six live-view tensors share
|
|
87
|
+
simulation memory, and writing into one writes into the simulation. The
|
|
88
|
+
engine can also rebind those buffers, while the base-frame members are
|
|
89
|
+
recomputed, so a tensor held across a step can silently serve stale values.
|
|
90
|
+
The accessor (``robot.state`` itself) may be held freely; the tensors its
|
|
91
|
+
members return should be re-read at each use.
|
|
86
92
|
|
|
87
93
|
Members raise ``RuntimeError`` until the simulation runtime has attached
|
|
88
94
|
the robot — read them from ``on_start`` onward. The ``state`` accessor
|
|
89
95
|
itself is always available and is the same object on every access.
|
|
90
96
|
|
|
91
|
-
These
|
|
97
|
+
These eight names are a compatibility promise: they keep working across
|
|
92
98
|
engine upgrades. For anything not named here, the ``internals`` escape
|
|
93
99
|
hatch remains available — explicitly unstable, at your own risk.
|
|
94
100
|
"""
|
|
@@ -111,6 +117,24 @@ class RobotStateProtocol(Protocol):
|
|
|
111
117
|
``[wx, wy, wz]``."""
|
|
112
118
|
...
|
|
113
119
|
|
|
120
|
+
@property
|
|
121
|
+
def linear_velocity_in_base_frame(self) -> TensorLike:
|
|
122
|
+
"""Base linear velocity expressed in the base frame, shape
|
|
123
|
+
``(num_envs, 3)`` in m/s with columns ``[vx, vy, vz]``. Available
|
|
124
|
+
from ``on_start`` onward. Computed from the current base orientation
|
|
125
|
+
on every read; each read returns a new tensor, costs a small
|
|
126
|
+
computation, and writing into it does not reach the simulation."""
|
|
127
|
+
...
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def angular_velocity_in_base_frame(self) -> TensorLike:
|
|
131
|
+
"""Base angular velocity expressed in the base frame, shape
|
|
132
|
+
``(num_envs, 3)`` in rad/s with columns ``[wx, wy, wz]``. Available
|
|
133
|
+
from ``on_start`` onward. Computed from the current base orientation
|
|
134
|
+
on every read; each read returns a new tensor, costs a small
|
|
135
|
+
computation, and writing into it does not reach the simulation."""
|
|
136
|
+
...
|
|
137
|
+
|
|
114
138
|
@property
|
|
115
139
|
def joint_positions(self) -> TensorLike:
|
|
116
140
|
"""Joint positions (rad or m), shape ``(num_envs, num_joints)``."""
|
|
@@ -146,7 +170,7 @@ class RobotProtocol(Protocol):
|
|
|
146
170
|
"""Model source (USD or URDF) defining the robot."""
|
|
147
171
|
|
|
148
172
|
initial_pose: Pose
|
|
149
|
-
"""Pose the robot spawns at,
|
|
173
|
+
"""Pose the robot spawns at (identity when not given). For a robot copied into each environment, the position is measured from that environment's origin, and default_pose reports it with the origin added. For some catalog robots, such as the cartpole, the identity pose keeps the catalog model's own spawn height; any other pose replaces it. A robot added under a Transform spawns at the Transform's position."""
|
|
150
174
|
|
|
151
175
|
collision_enabled: bool
|
|
152
176
|
"""Whether collision detection is enabled."""
|
|
@@ -160,17 +184,21 @@ class RobotProtocol(Protocol):
|
|
|
160
184
|
scale: Tuple[float, float, float]
|
|
161
185
|
"""Per-axis spawn scale factor."""
|
|
162
186
|
|
|
163
|
-
articulation:
|
|
187
|
+
articulation: ArticulationConfig
|
|
164
188
|
"""Articulation physics and control configuration."""
|
|
165
189
|
|
|
166
|
-
joint_config: Optional[List[
|
|
190
|
+
joint_config: Optional[List[JointConfig]]
|
|
167
191
|
"""Optional per-joint configurations."""
|
|
168
192
|
|
|
169
193
|
disable_gravity: bool
|
|
170
194
|
"""If ``True``, gravity is disabled for this robot."""
|
|
171
195
|
|
|
172
196
|
actuator_gains: Optional[Dict[str, Any]]
|
|
173
|
-
"""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
|
+
"""
|
|
174
202
|
|
|
175
203
|
# -- Identity and attachment ------------------------------------------
|
|
176
204
|
|
|
@@ -203,9 +231,49 @@ class RobotProtocol(Protocol):
|
|
|
203
231
|
"""The grouped live robot state — the supported, typed state surface.
|
|
204
232
|
|
|
205
233
|
Always available (the accessor is the same object on every access);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
234
|
+
six members read straight through to the simulation's state buffers,
|
|
235
|
+
while the two base-frame velocity members are computed into new
|
|
236
|
+
tensors on every read. Members raise ``RuntimeError`` until the
|
|
237
|
+
simulation runtime has attached the robot — read them from
|
|
238
|
+
``on_start`` onward.
|
|
239
|
+
"""
|
|
240
|
+
...
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def default_joint_positions(self) -> TensorLike:
|
|
244
|
+
"""Joint positions that :meth:`reset` writes, shape
|
|
245
|
+
``(num_envs, num_joints)`` in rad for hinge joints and m for slider
|
|
246
|
+
joints. Columns use the same order as ``state.joint_positions`` and
|
|
247
|
+
the indices returned by :meth:`find_joints`. Available from
|
|
248
|
+
``on_start`` onward. Each read returns a new tensor, so writing into
|
|
249
|
+
it cannot change a later reset."""
|
|
250
|
+
...
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def default_joint_velocities(self) -> TensorLike:
|
|
254
|
+
"""Joint velocities that :meth:`reset` writes, shape
|
|
255
|
+
``(num_envs, num_joints)`` in rad/s for hinge joints and m/s for
|
|
256
|
+
slider joints. Columns use the same order as
|
|
257
|
+
``state.joint_positions`` and the indices returned by
|
|
258
|
+
:meth:`find_joints`. Available from ``on_start`` onward. Each read
|
|
259
|
+
returns a new tensor, so writing into it cannot change a later
|
|
260
|
+
reset."""
|
|
261
|
+
...
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def default_pose(self) -> TensorLike:
|
|
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.
|
|
209
277
|
"""
|
|
210
278
|
...
|
|
211
279
|
|