simulo-interfaces 0.24.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.
Files changed (69) hide show
  1. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/PKG-INFO +1 -1
  2. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/pyproject.toml +1 -1
  3. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/__init__.py +29 -4
  4. simulo_interfaces-0.25.0/src/simulo/interfaces/authoring/action_channel.py +22 -0
  5. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/prop.py +18 -0
  6. simulo_interfaces-0.25.0/src/simulo/interfaces/authoring/properties.py +92 -0
  7. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/scene.py +22 -0
  8. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/terrain.py +43 -0
  9. simulo_interfaces-0.25.0/src/simulo/interfaces/authoring/variants.py +48 -0
  10. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/visual.py +90 -1
  11. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/__init__.py +47 -2
  12. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/evaluation.py +18 -1
  13. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/manifest.py +6 -5
  14. simulo_interfaces-0.25.0/src/simulo/interfaces/platform/preview.py +197 -0
  15. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/runs.py +35 -8
  16. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/submit.py +24 -6
  17. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/__init__.py +3 -1
  18. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/env.py +5 -0
  19. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/task.py +56 -1
  20. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/trainer.py +3 -1
  21. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo_interfaces.egg-info/PKG-INFO +1 -1
  22. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo_interfaces.egg-info/SOURCES.txt +3 -0
  23. simulo_interfaces-0.24.0/src/simulo/interfaces/platform/preview.py +0 -85
  24. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/MANIFEST.in +0 -0
  25. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/PYPI.md +0 -0
  26. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/setup.cfg +0 -0
  27. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/__init__.py +0 -0
  28. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/actuator.py +0 -0
  29. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/asset.py +0 -0
  30. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/checkpoint.py +0 -0
  31. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/entity.py +0 -0
  32. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/light.py +0 -0
  33. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/materials.py +0 -0
  34. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/physics.py +0 -0
  35. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/policy_inputs.py +0 -0
  36. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/pose.py +0 -0
  37. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/primitives.py +0 -0
  38. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/record.py +0 -0
  39. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/robot.py +0 -0
  40. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/robot_config.py +0 -0
  41. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/sensor.py +0 -0
  42. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/authoring/world.py +0 -0
  43. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/exceptions.py +0 -0
  44. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/ids.py +0 -0
  45. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/_payload.py +0 -0
  46. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/app.py +0 -0
  47. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/asset.py +0 -0
  48. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/asset_catalog.py +0 -0
  49. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/callbacks.py +0 -0
  50. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/debug.py +0 -0
  51. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/domain.py +0 -0
  52. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/enums.py +0 -0
  53. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/job_events.py +0 -0
  54. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/onnx_bundle.py +0 -0
  55. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/outputs.py +0 -0
  56. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/policy_checkpoint.py +0 -0
  57. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/runtime.py +0 -0
  58. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/systems.py +0 -0
  59. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/platform/volume.py +0 -0
  60. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/py.typed +0 -0
  61. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/anomaly.py +0 -0
  62. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/components.py +0 -0
  63. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/player.py +0 -0
  64. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/policy.py +0 -0
  65. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/policy_frames.py +0 -0
  66. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo/interfaces/runtime/tensors.py +0 -0
  67. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo_interfaces.egg-info/dependency_links.txt +0 -0
  68. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo_interfaces.egg-info/requires.txt +0 -0
  69. {simulo_interfaces-0.24.0 → simulo_interfaces-0.25.0}/src/simulo_interfaces.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simulo-interfaces
3
- Version: 0.24.0
3
+ Version: 0.25.0
4
4
  Summary: Typed Python contracts for the Simulo cloud robotics SDK and managed simulation runtime.
5
5
  Author-email: Simulo Team <team@simulo.ai>
6
6
  License: BSD-3-Clause
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simulo-interfaces"
7
- version = "0.24.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"
@@ -17,7 +17,7 @@ decision — to resolve that exact name collision), and ``light`` (``Light``,
17
17
  ``RoughTerrain``), ``primitives`` (``PrimitiveShape``, ``Cone``,
18
18
  ``Cuboid``, ``Sphere``, ``Cylinder``, ``Transform``), ``entity`` (``Entity``,
19
19
  ``AssetEntity``), and ``visual`` (``Visual``, ``VisualMarkerBase``,
20
- ``AxesMarker``, ``SphereMarker``, ``ArrowMarker``, ``PointMarker``).
20
+ ``AxesMarker``, ``SphereMarker``, ``ArrowMarker``, ``PointMarker``, ``LineMarker``).
21
21
  Wave C4: ``record`` (``RecordConfig`` — the MCAP recording configuration;
22
22
  stdlib-only, so it classifies RELOCATE, and the backend recorder imports it
23
23
  from here), and ``world`` (``World``, ``USDWorld``): ``Terrain`` is the
@@ -84,11 +84,17 @@ and cross-references for cryptic engine-style names) — that table, not this
84
84
  docstring, is the source of truth for what lands here and when.
85
85
  """
86
86
 
87
+ from simulo.interfaces.authoring.action_channel import ActionChannel
87
88
  from simulo.interfaces.authoring.actuator import ActuatorProtocol
88
89
  from simulo.interfaces.authoring.asset import AssetSource, URDFAsset, USDAsset
89
90
  from simulo.interfaces.authoring.checkpoint import PolicyCheckpoint
90
91
  from simulo.interfaces.authoring.entity import AssetEntity, Entity
91
- from simulo.interfaces.authoring.light import DistantLight, DomeLight, Light, LightConfig
92
+ from simulo.interfaces.authoring.light import (
93
+ DistantLight,
94
+ DomeLight,
95
+ Light,
96
+ LightConfig,
97
+ )
92
98
  from simulo.interfaces.authoring.materials import Material, SurfaceMaterial
93
99
  from simulo.interfaces.authoring.physics import (
94
100
  Collision,
@@ -107,11 +113,17 @@ from simulo.interfaces.authoring.primitives import (
107
113
  Sphere,
108
114
  Transform,
109
115
  )
110
- 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
+ )
111
123
  from simulo.interfaces.authoring.record import RecordConfig
112
124
  from simulo.interfaces.authoring.robot import RobotProtocol, RobotStateProtocol
113
125
  from simulo.interfaces.authoring.robot_config import ActuatorGainsConfig, ArticulationConfig, JointConfig
114
- from simulo.interfaces.authoring.scene import SceneGroupProtocol, SceneProtocol
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,
@@ -183,20 +197,31 @@ __all__ = [
183
197
  "SphereMarker",
184
198
  "ArrowMarker",
185
199
  "PointMarker",
200
+ "LineMarker",
186
201
  "Visual",
187
202
  # scene (MIRROR — the concrete classes live in the backend package)
203
+ "SceneGravityProtocol",
188
204
  "SceneProtocol",
189
205
  "SceneGroupProtocol",
206
+ "SceneVariantsProtocol",
207
+ # scene variants (dependency-free authoring data)
208
+ "ObjectVariants",
190
209
  # robot configuration (dependency-free owning definitions)
191
210
  "ActuatorGainsConfig",
192
211
  "ArticulationConfig",
193
212
  "JointConfig",
194
213
  # robot / prop / sensor / actuator (MIRROR — the concrete classes live in
195
214
  # the backend package and explicitly inherit these Protocols)
215
+ "ExternalForceProtocol",
216
+ "RobotPropertiesProtocol",
217
+ "PropPropertiesProtocol",
196
218
  "RobotProtocol",
197
219
  "RobotStateProtocol",
198
220
  "PropProtocol",
199
221
  "PropStateProtocol",
222
+ "VariantPropProtocol",
200
223
  "CameraProtocol",
201
224
  "ActuatorProtocol",
225
+ # action-to-joint metadata
226
+ "ActionChannel",
202
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
@@ -214,3 +214,21 @@ class PropProtocol(Protocol):
214
214
  environment.
215
215
  """
216
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
+ ...
@@ -34,6 +34,7 @@ Two deliberate scoping notes, so they are not mistaken for drift:
34
34
 
35
35
  from typing import Any, Dict, List, Optional, Protocol, runtime_checkable
36
36
 
37
+ from simulo.interfaces.authoring.variants import ObjectVariants
37
38
  from simulo.interfaces.runtime.tensors import TensorLike
38
39
 
39
40
 
@@ -156,3 +157,24 @@ class SceneProtocol(Protocol):
156
157
  authoring code does not need to call it directly.
157
158
  """
158
159
  ...
160
+
161
+
162
+ @runtime_checkable
163
+ class SceneVariantsProtocol(SceneProtocol, Protocol):
164
+ """The opt-in scene surface that can author different prop objects per copy.
165
+
166
+ The baseline :class:`SceneProtocol` intentionally keeps its legacy
167
+ ``add`` signature. A task that uses ``variants=`` declares this narrower
168
+ capability, so frozen legacy scene implementations remain valid baseline
169
+ scenes without claiming they can build variants.
170
+ """
171
+
172
+ def add(
173
+ self,
174
+ item: Any,
175
+ at: str = ...,
176
+ per_environment: bool = ...,
177
+ variants: ObjectVariants | None = ...,
178
+ ) -> None:
179
+ """Add a prop with opt-in per-copy object variants."""
180
+ ...
@@ -15,6 +15,7 @@ a factory floor — is a :class:`~simulo.interfaces.authoring.world.World`
15
15
  (``World.usd(...)``).
16
16
  """
17
17
 
18
+ import math
18
19
  from dataclasses import dataclass, field
19
20
  from typing import Any, Dict, List, Optional, Tuple
20
21
 
@@ -38,6 +39,25 @@ class GroundPlane(TerrainConfig):
38
39
  """
39
40
 
40
41
  size: float = 100.0
42
+ friction: Optional[float] = None
43
+ contact_stiffness: Optional[float] = None
44
+ contact_damping: Optional[float] = None
45
+ contact_gap: Optional[float] = None
46
+
47
+ def __post_init__(self) -> None:
48
+ for name in ("friction", "contact_stiffness", "contact_damping", "contact_gap"):
49
+ value = getattr(self, name)
50
+ if value is None:
51
+ continue
52
+ if (
53
+ isinstance(value, bool)
54
+ or not isinstance(value, (int, float))
55
+ or not math.isfinite(value)
56
+ or value < 0
57
+ or (name in ("contact_stiffness", "contact_damping") and value == 0)
58
+ ):
59
+ bound = "positive" if name in ("contact_stiffness", "contact_damping") else "nonnegative"
60
+ raise ValueError(f"{name} must be a finite {bound} number")
41
61
 
42
62
 
43
63
  @dataclass
@@ -62,6 +82,11 @@ class SubTerrainConfig:
62
82
  Defaults to 1.0, which uses the whole grid for this single
63
83
  sub-terrain.
64
84
  params: Type-specific parameters (e.g., height, step_height, etc.)
85
+ material: Visual material: "default", "concrete", "dirt", or "none".
86
+ "default" uses concrete for discrete obstacles, slopes, and waves;
87
+ dirt for stairs and random rough terrain (including holes); and
88
+ white for flat terrain. "none" explicitly selects white. This
89
+ setting does not change collision geometry or physical friction.
65
90
 
66
91
  Example:
67
92
  ```python
@@ -77,6 +102,11 @@ class SubTerrainConfig:
77
102
  type: str
78
103
  proportion: float = 1.0
79
104
  params: Dict[str, Any] = field(default_factory=dict)
105
+ material: str = "default"
106
+
107
+ def __post_init__(self) -> None:
108
+ if self.material not in ("default", "concrete", "dirt", "none"):
109
+ raise ValueError("material must be one of: default, concrete, dirt, none")
80
110
 
81
111
 
82
112
  @dataclass
@@ -215,6 +245,11 @@ class Terrain:
215
245
  name: str = "groundPlane",
216
246
  pose: Optional[Pose] = None,
217
247
  size: float = 100.0,
248
+ *,
249
+ friction: Optional[float] = None,
250
+ contact_stiffness: Optional[float] = None,
251
+ contact_damping: Optional[float] = None,
252
+ contact_gap: Optional[float] = None,
218
253
  ) -> GroundPlane:
219
254
  """Create a ground plane (infinite flat surface).
220
255
 
@@ -225,6 +260,10 @@ class Terrain:
225
260
  name: Terrain name
226
261
  pose: Pose in space
227
262
  size: Visual size (actual collision is infinite)
263
+ friction: Contact friction coefficient, or the backend default
264
+ contact_stiffness: Normal contact stiffness in N/m, or the backend default
265
+ contact_damping: Normal contact damping in N s/m, or the backend default
266
+ contact_gap: Additional contact detection gap in meters, or the backend default
228
267
 
229
268
  Returns:
230
269
  GroundPlane instance
@@ -233,6 +272,10 @@ class Terrain:
233
272
  name=name,
234
273
  pose=pose or Pose.identity(),
235
274
  size=size,
275
+ friction=friction,
276
+ contact_stiffness=contact_stiffness,
277
+ contact_damping=contact_damping,
278
+ contact_gap=contact_gap,
236
279
  )
237
280
 
238
281
  @staticmethod
@@ -0,0 +1,48 @@
1
+ """Dependency-free authoring data for opt-in per-copy scene variants."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from dataclasses import dataclass
7
+ from typing import Literal, Sequence
8
+
9
+ from simulo.interfaces.authoring.asset import AssetSource
10
+ from simulo.interfaces.authoring.pose import Pose
11
+ from simulo.interfaces.authoring.primitives import PrimitiveShape
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class ObjectVariants:
16
+ """The objects one authored prop can represent across environment copies.
17
+
18
+ ``groups`` assigns each option to a contiguous group of copies. ``pool``
19
+ builds every option in each copy so a task can switch the active object at
20
+ reset. The engine validates geometry-dependent parking distances at build.
21
+ """
22
+
23
+ options: Sequence[PrimitiveShape | AssetSource]
24
+ mode: Literal["groups", "pool"] = "groups"
25
+ parking_origin: Pose | None = None
26
+ parking_spacing: float | None = None
27
+
28
+ def __post_init__(self) -> None:
29
+ options = tuple(self.options)
30
+ if not 2 <= len(options) <= 8:
31
+ raise ValueError("ObjectVariants.options must contain between 2 and 8 options")
32
+ if not all(isinstance(option, (PrimitiveShape, AssetSource)) for option in options):
33
+ raise TypeError("ObjectVariants.options must contain PrimitiveShape or AssetSource values")
34
+ if self.mode not in {"groups", "pool"}:
35
+ raise ValueError("ObjectVariants.mode must be 'groups' or 'pool'")
36
+ if self.mode != "pool" and (self.parking_origin is not None or self.parking_spacing is not None):
37
+ raise ValueError("parking_origin and parking_spacing are supported only for pool variants")
38
+ if self.parking_spacing is not None:
39
+ if (
40
+ isinstance(self.parking_spacing, bool)
41
+ or not math.isfinite(self.parking_spacing)
42
+ or self.parking_spacing <= 0
43
+ ):
44
+ raise ValueError("parking_spacing must be a finite positive number")
45
+ object.__setattr__(self, "options", options)
46
+
47
+
48
+ __all__ = ["ObjectVariants"]
@@ -53,7 +53,9 @@ class VisualMarkerBase:
53
53
  """Update marker pose(s).
54
54
 
55
55
  Args:
56
- position: Position(s) as (3,) or (num_instances, 3) tensor/array
56
+ position: Position(s) as (3,) or (num_instances, 3) tensor/array.
57
+ On the engine, pass float32 tensors on the simulation device;
58
+ other inputs may allocate or synchronize the host.
57
59
  quaternion: Optional quaternion(s) as (4,) or (num_instances, 4)
58
60
  in wxyz format. If None, uses identity rotation.
59
61
  """
@@ -143,6 +145,57 @@ class PointMarker(VisualMarkerBase):
143
145
  color: Color = (1.0, 1.0, 0.0) # Yellow by default
144
146
 
145
147
 
148
+ @dataclass
149
+ class LineMarker(VisualMarkerBase):
150
+ """Line or polyline marker for paths and trails.
151
+
152
+ Draws a line strip through ``points``. The number of points is fixed when
153
+ the marker is created; ``set_points`` moves them. Unlike the pose
154
+ markers a line has no pose, so ``set_pose`` raises.
155
+
156
+ Attributes:
157
+ points: Initial polyline as a ``(P, 3)`` sequence or array, at least
158
+ two points, in world coordinates. Every scene copy starts with
159
+ these points.
160
+ width: Line width in meters
161
+ color: Line color as RGB or RGBA tuple
162
+ """
163
+
164
+ points: Any = None
165
+ width: float = 0.01
166
+ color: Color = (0.0, 0.8, 1.0) # Cyan by default
167
+ _num_points: int = field(default=0, init=False, repr=False)
168
+
169
+ def __post_init__(self) -> None:
170
+ if self.points is None or len(self.points) < 2:
171
+ raise ValueError("LineMarker needs at least two points")
172
+ self._num_points = int(len(self.points))
173
+
174
+ @property
175
+ def num_points(self) -> int:
176
+ """Number of points on the line, fixed at creation."""
177
+ return self._num_points
178
+
179
+ def set_points(self, points: Any) -> None:
180
+ """Move the line's points.
181
+
182
+ Args:
183
+ points: ``(num_points, 3)`` to give every scene copy the same line,
184
+ or ``(num_envs, num_points, 3)`` for one line per copy, in
185
+ world coordinates. Pass float32 tensors on the simulation
186
+ device; other inputs (lists, CPU or float64 tensors) may
187
+ allocate or synchronize the host.
188
+ """
189
+ if self._backend_handle is None:
190
+ return
191
+
192
+ self._backend_handle.set_points(points)
193
+
194
+ def set_pose(self, position: Any, quaternion: Optional[Any] = None) -> None:
195
+ """Always raises: a line has points, not a pose. Use ``set_points``."""
196
+ raise TypeError("LineMarker has no pose; use set_points")
197
+
198
+
146
199
  class Visual:
147
200
  """Factory for creating visual markers.
148
201
 
@@ -164,6 +217,7 @@ class Visual:
164
217
  - sphere: Sphere marker for points/targets
165
218
  - arrow: Directional arrow for forces/velocities
166
219
  - point: Simple point marker (lightweight)
220
+ - line: Line or polyline through points, for paths and trails
167
221
  """
168
222
 
169
223
  @staticmethod
@@ -300,3 +354,38 @@ class Visual:
300
354
  color=color,
301
355
  num_instances=num_instances,
302
356
  )
357
+
358
+ @staticmethod
359
+ def line(
360
+ name: str,
361
+ points: Any,
362
+ width: float = 0.01,
363
+ color: Color = (0.0, 0.8, 1.0),
364
+ num_instances: int = 1,
365
+ ) -> LineMarker:
366
+ """Create a line or polyline marker.
367
+
368
+ Args:
369
+ name: Unique marker name
370
+ points: Initial ``(P, 3)`` points, at least two, in world coordinates
371
+ width: Line width in meters
372
+ color: RGB or RGBA color tuple
373
+ num_instances: Number of instances for multi-env
374
+
375
+ Returns:
376
+ LineMarker instance
377
+
378
+ Example:
379
+ ```python
380
+ path = simulo.Visual.line("planned_path", points=waypoints, width=0.02)
381
+ scene.add(path)
382
+ path.set_points(new_waypoints) # same number of points
383
+ ```
384
+ """
385
+ return LineMarker(
386
+ name=name,
387
+ points=points,
388
+ width=width,
389
+ color=color,
390
+ num_instances=num_instances,
391
+ )
@@ -68,8 +68,19 @@ from simulo.interfaces.platform.asset_catalog import (
68
68
  parse_asset_ref,
69
69
  )
70
70
  from simulo.interfaces.platform.callbacks import JobCallbackProtocol
71
- from simulo.interfaces.platform.debug import DebugSessionProtocol, ReplaySessionProtocol, VisualizationSessionProtocol
72
- from simulo.interfaces.platform.domain import Checkpoint, Job, Package, Project, Resource, Tag
71
+ from simulo.interfaces.platform.debug import (
72
+ DebugSessionProtocol,
73
+ ReplaySessionProtocol,
74
+ VisualizationSessionProtocol,
75
+ )
76
+ from simulo.interfaces.platform.domain import (
77
+ Checkpoint,
78
+ Job,
79
+ Package,
80
+ Project,
81
+ Resource,
82
+ Tag,
83
+ )
73
84
  from simulo.interfaces.platform.enums import (
74
85
  INPUT_CHECKPOINT_JOB_TYPES,
75
86
  INPUT_CHECKPOINT_REQUIRED_JOB_TYPES,
@@ -85,6 +96,8 @@ from simulo.interfaces.platform.evaluation import (
85
96
  EPISODES_CSV_CHECK_PREFIX,
86
97
  EPISODES_CSV_COLUMNS_AFTER_CHECKS,
87
98
  EPISODES_CSV_COLUMNS_BEFORE_CHECKS,
99
+ EPISODES_CSV_FIRST_FAILURE_AT_S_PREFIX,
100
+ EPISODES_CSV_FIRST_FAILURE_STEP_PREFIX,
88
101
  EVALUATION_ACTIONS,
89
102
  EVALUATION_REPORT_SCHEMA_VERSION,
90
103
  MAX_CHECK_NAME_CHARS,
@@ -241,14 +254,28 @@ from simulo.interfaces.platform.outputs import (
241
254
  tier0_declarations,
242
255
  )
243
256
  from simulo.interfaces.platform.preview import (
257
+ ENGINE_SETUP_SCHEMA_VERSION,
258
+ LIMITS_REPORT_SCHEMA_VERSION,
244
259
  PREVIEW_CHECKS,
245
260
  PREVIEW_REPORT_SCHEMA_VERSION,
261
+ PREVIEW_REPORT_SCHEMA_VERSION_V2,
262
+ PREVIEW_REPORT_SCHEMA_VERSIONS,
246
263
  PREVIEW_SELECTABLE_CHECKS,
264
+ EngineMotorSpec,
265
+ EngineSetupMotorSpec,
266
+ EngineSetupReport,
267
+ EngineSetupScope,
268
+ EngineSetupValue,
269
+ LimitsReport,
270
+ LimitsReportComparisonRating,
271
+ LimitsReportJoint,
247
272
  PreviewCheck,
248
273
  PreviewOwner,
249
274
  PreviewReport,
275
+ PreviewReportAny,
250
276
  PreviewReportRow,
251
277
  PreviewReportStage,
278
+ PreviewReportV2,
252
279
  PreviewRowStatus,
253
280
  PreviewStageStatus,
254
281
  PreviewVerdict,
@@ -290,6 +317,7 @@ from simulo.interfaces.platform.submit import (
290
317
  JOB_RECORDING_DOWNLOAD_ROUTE_TEMPLATE,
291
318
  JOB_RECORDINGS_ROUTE_TEMPLATE,
292
319
  JOB_TYPE_MISMATCH_CODE,
320
+ LIVE_VIEW_UNAVAILABLE_WARNING_CODE,
293
321
  MAX_COMPLETE_MESSAGE_CHARS,
294
322
  MAX_COMPLETE_REASON_CODE_CHARS,
295
323
  MAX_LOG_CHUNK_BYTES,
@@ -472,6 +500,7 @@ __all__ = [
472
500
  "MAX_RESULT_BYTES",
473
501
  "MAX_COMPLETE_MESSAGE_CHARS",
474
502
  "MAX_COMPLETE_REASON_CODE_CHARS",
503
+ "LIVE_VIEW_UNAVAILABLE_WARNING_CODE",
475
504
  "MAX_WORKER_RETIREMENT_DETAIL_CHARS",
476
505
  "CHECKPOINT_KINDS",
477
506
  "RESERVED_RUNTIME_ENV_KEYS",
@@ -615,6 +644,10 @@ __all__ = [
615
644
  # preview report wire contract — schema version, check names, and the
616
645
  # row/stage/report shapes the preview job returns
617
646
  "PREVIEW_REPORT_SCHEMA_VERSION",
647
+ "PREVIEW_REPORT_SCHEMA_VERSION_V2",
648
+ "PREVIEW_REPORT_SCHEMA_VERSIONS",
649
+ "ENGINE_SETUP_SCHEMA_VERSION",
650
+ "LIMITS_REPORT_SCHEMA_VERSION",
618
651
  "PREVIEW_CHECKS",
619
652
  "PREVIEW_SELECTABLE_CHECKS",
620
653
  "PreviewCheck",
@@ -625,6 +658,16 @@ __all__ = [
625
658
  "PreviewReportRow",
626
659
  "PreviewReportStage",
627
660
  "PreviewReport",
661
+ "PreviewReportV2",
662
+ "PreviewReportAny",
663
+ "EngineSetupScope",
664
+ "EngineMotorSpec",
665
+ "EngineSetupValue",
666
+ "EngineSetupMotorSpec",
667
+ "EngineSetupReport",
668
+ "LimitsReportComparisonRating",
669
+ "LimitsReportJoint",
670
+ "LimitsReport",
628
671
  # evaluation report wire contract — schema version, how an episode ended,
629
672
  # the bounds that keep the report under the result cap, the report shapes,
630
673
  # the replay trace filming reads, and episodes.csv
@@ -669,6 +712,8 @@ __all__ = [
669
712
  "EPISODES_CSV_COLUMNS_BEFORE_CHECKS",
670
713
  "EPISODES_CSV_CHECK_PREFIX",
671
714
  "EPISODES_CSV_COLUMNS_AFTER_CHECKS",
715
+ "EPISODES_CSV_FIRST_FAILURE_STEP_PREFIX",
716
+ "EPISODES_CSV_FIRST_FAILURE_AT_S_PREFIX",
672
717
  "episodes_csv_columns",
673
718
  # protocols
674
719
  "RuntimeProtocol",