simulo-interfaces 0.21.0__tar.gz → 0.23.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 (61) hide show
  1. {simulo_interfaces-0.21.0/src/simulo_interfaces.egg-info → simulo_interfaces-0.23.0}/PKG-INFO +1 -1
  2. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/pyproject.toml +1 -1
  3. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/__init__.py +4 -0
  4. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/__init__.py +11 -7
  5. simulo_interfaces-0.23.0/src/simulo/interfaces/authoring/checkpoint.py +84 -0
  6. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/record.py +31 -3
  7. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/robot.py +81 -25
  8. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/terrain.py +2 -4
  9. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/__init__.py +169 -72
  10. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/enums.py +9 -3
  11. simulo_interfaces-0.23.0/src/simulo/interfaces/platform/evaluation.py +451 -0
  12. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/manifest.py +7 -6
  13. simulo_interfaces-0.21.0/src/simulo/interfaces/platform/policy_bundle.py → simulo_interfaces-0.23.0/src/simulo/interfaces/platform/onnx_bundle.py +115 -117
  14. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/outputs.py +18 -3
  15. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/runs.py +11 -0
  16. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/submit.py +11 -0
  17. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/runtime/__init__.py +2 -2
  18. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/runtime/components.py +3 -0
  19. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/runtime/env.py +12 -0
  20. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/runtime/policy.py +3 -3
  21. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/runtime/task.py +35 -6
  22. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/runtime/trainer.py +4 -12
  23. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0/src/simulo_interfaces.egg-info}/PKG-INFO +1 -1
  24. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo_interfaces.egg-info/SOURCES.txt +3 -1
  25. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/MANIFEST.in +0 -0
  26. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/PYPI.md +0 -0
  27. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/setup.cfg +0 -0
  28. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/actuator.py +0 -0
  29. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/asset.py +0 -0
  30. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/entity.py +0 -0
  31. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/light.py +0 -0
  32. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/materials.py +0 -0
  33. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/physics.py +0 -0
  34. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/pose.py +0 -0
  35. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/primitives.py +0 -0
  36. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/prop.py +0 -0
  37. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/scene.py +0 -0
  38. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/sensor.py +0 -0
  39. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/visual.py +0 -0
  40. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/authoring/world.py +0 -0
  41. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/exceptions.py +0 -0
  42. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/ids.py +0 -0
  43. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/_payload.py +0 -0
  44. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/app.py +0 -0
  45. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/asset.py +0 -0
  46. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/asset_catalog.py +0 -0
  47. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/callbacks.py +0 -0
  48. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/debug.py +0 -0
  49. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/domain.py +0 -0
  50. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/job_events.py +0 -0
  51. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/preview.py +0 -0
  52. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/runtime.py +0 -0
  53. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/systems.py +0 -0
  54. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/platform/volume.py +0 -0
  55. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/py.typed +0 -0
  56. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/runtime/anomaly.py +0 -0
  57. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/runtime/player.py +0 -0
  58. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo/interfaces/runtime/tensors.py +0 -0
  59. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo_interfaces.egg-info/dependency_links.txt +0 -0
  60. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.0}/src/simulo_interfaces.egg-info/requires.txt +0 -0
  61. {simulo_interfaces-0.21.0 → simulo_interfaces-0.23.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.21.0
3
+ Version: 0.23.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.21.0"
7
+ version = "0.23.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"
@@ -19,6 +19,7 @@ namespace that ``simulo-backend`` also contributes to; it ships NO
19
19
  """
20
20
 
21
21
  from simulo.interfaces import platform, runtime
22
+ from simulo.interfaces.authoring import PolicyCheckpoint
22
23
  from simulo.interfaces.exceptions import (
23
24
  ContractViolationError,
24
25
  ImportResolutionError,
@@ -50,12 +51,15 @@ from simulo.interfaces.ids import (
50
51
  policy_id_for_job,
51
52
  )
52
53
  from simulo.interfaces.platform.enums import JobType
54
+ from simulo.interfaces.runtime import PolicyNetworkProtocol
53
55
 
54
56
  __all__ = [
55
57
  # subpackages
56
58
  "platform",
57
59
  "runtime",
58
60
  "JobType",
61
+ "PolicyCheckpoint",
62
+ "PolicyNetworkProtocol",
59
63
  # exception hierarchy
60
64
  "SimuloError",
61
65
  "ContractViolationError",
@@ -41,13 +41,14 @@ waves add the remaining RELOCATE-classified types and MIRROR Protocols.
41
41
  **R-C3 is the hard constraint on everything that lands here:** no module in
42
42
  this package — or anywhere else in ``simulo-interfaces`` — may import
43
43
  ``simulo.core``, ``simulo.backend``, or the thin client's private internal
44
- package, at import time or at call time (the ``Scene`` trap: zero torch
45
- imports, but call-time imports of torch-coupled ``core`` modules deep inside a
46
- method body). Guarded by ``tests/test_no_core_dependency.py`` in this package —
47
- both a static import-statement guard and a runtime ``sys.modules`` check that
48
- imports every submodule AND instantiates/calls the relocated API, because the
49
- static guard alone cannot see ``from simulo import core``, dynamic access, or a
50
- call-time-only import buried in a method body.
44
+ package at import time. Calls must remain dependency-free too (the ``Scene``
45
+ trap), with one explicit execution-only bridge: ``PolicyCheckpoint.load()``
46
+ lazily imports ``simulo.core.policy.load_policy_network`` inside a job and
47
+ raises the documented submit-time error when the backend is absent. Guarded by
48
+ ``tests/test_no_core_dependency.py`` in this package — both a static
49
+ import-statement guard and isolated runtime checks that exercise the relocated
50
+ API, prove ordinary imports remain backend-free, and prove the loader fails
51
+ clearly in a submit-only installation.
51
52
 
52
53
  Two relocation mechanisms select which types land here and how (see the derived
53
54
  plan for the full worked example and rejected alternatives):
@@ -87,6 +88,7 @@ docstring, is the source of truth for what lands here and when.
87
88
 
88
89
  from simulo.interfaces.authoring.actuator import ActuatorProtocol
89
90
  from simulo.interfaces.authoring.asset import AssetSource, URDFAsset, USDAsset
91
+ from simulo.interfaces.authoring.checkpoint import PolicyCheckpoint
90
92
  from simulo.interfaces.authoring.entity import AssetEntity, Entity
91
93
  from simulo.interfaces.authoring.light import DistantLight, DomeLight, Light, LightConfig
92
94
  from simulo.interfaces.authoring.materials import Material, SurfaceMaterial
@@ -171,6 +173,8 @@ __all__ = [
171
173
  # record (RELOCATED Wave C4 — the MCAP recording config; its helper
172
174
  # functions stay importable from ``simulo.interfaces.authoring.record``)
173
175
  "RecordConfig",
176
+ # injected policy input (dependency-free; loading is deferred to execution)
177
+ "PolicyCheckpoint",
174
178
  # visual
175
179
  "VisualMarkerBase",
176
180
  "AxesMarker",
@@ -0,0 +1,84 @@
1
+ """Dependency-free handle for a policy checkpoint injected into a job."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from dataclasses import dataclass
7
+ from importlib import import_module
8
+ from pathlib import Path
9
+ from typing import Any, cast
10
+
11
+ from simulo.interfaces.runtime.policy import PolicyNetworkProtocol
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class PolicyCheckpoint:
16
+ """A resolved policy checkpoint supplied to a play or evaluation job.
17
+
18
+ The contract contains only standard-library values. Loading the policy is
19
+ deliberately deferred until execution, where ``simulo-backend`` provides
20
+ the concrete policy-network loader.
21
+ """
22
+
23
+ checkpoint: str
24
+ policy_id: str
25
+ kind: str
26
+ path: Path
27
+ meta: Mapping[str, Any]
28
+
29
+ @property
30
+ def iteration(self) -> int:
31
+ """Training iteration stored in the checkpoint sidecar."""
32
+
33
+ return cast(int, self.meta["completed_iterations"])
34
+
35
+ @property
36
+ def max_iterations(self) -> int:
37
+ """Configured training limit stored in the checkpoint sidecar."""
38
+
39
+ return cast(int, self.meta["max_iterations"])
40
+
41
+ @property
42
+ def reward(self) -> float | None:
43
+ """Best-checkpoint reward, or ``None`` when the sidecar has none."""
44
+
45
+ return cast(float | None, self.meta.get("reward"))
46
+
47
+ @property
48
+ def obs_dim(self) -> int:
49
+ """Observation width used to train the checkpoint."""
50
+
51
+ return cast(int, self.meta["obs_dim"])
52
+
53
+ @property
54
+ def act_dim(self) -> int:
55
+ """Action width produced by the checkpoint."""
56
+
57
+ return cast(int, self.meta["act_dim"])
58
+
59
+ @property
60
+ def task_name(self) -> str:
61
+ """Name of the task class used for training, if the sidecar has it."""
62
+
63
+ return cast(str, self.meta.get("task_name", "unknown"))
64
+
65
+ @property
66
+ def task_digest(self) -> str:
67
+ """Digest of the file that defined the training task, if available."""
68
+
69
+ return cast(str, self.meta.get("task_digest", "unknown"))
70
+
71
+ def load(self, device: str = "cuda") -> PolicyNetworkProtocol:
72
+ """Load the checkpoint as a policy network inside a Simulo job."""
73
+
74
+ try:
75
+ policy_module = import_module("simulo.core.policy")
76
+ except ImportError as exc:
77
+ raise RuntimeError(
78
+ "PolicyCheckpoint.load() runs inside a job on Simulo; it is not available at submit."
79
+ ) from exc
80
+ loader = getattr(policy_module, "load_policy_network")
81
+ return cast(PolicyNetworkProtocol, loader(self, device=device))
82
+
83
+
84
+ __all__ = ["PolicyCheckpoint"]
@@ -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.
@@ -194,7 +197,7 @@ class RecordConfig:
194
197
  mitigation depends on the tri-state.
195
198
  """
196
199
 
197
- output_path: Union[str, "os.PathLike[str]", Path]
200
+ output_path: Optional[Union[str, "os.PathLike[str]", Path]] = None
198
201
 
199
202
  # Identity
200
203
  run_id: Optional[str] = None
@@ -253,7 +256,14 @@ class RecordConfig:
253
256
  video_fallback: Literal["software", "error"] = "software"
254
257
  video_codec: Literal["h264", "hevc", "av1"] = "h264"
255
258
  video_bitrate: Optional[str] = "8M"
256
- video_fps: Optional[int] = 30
259
+ # ``None`` leaves cadence to the frame producer. The encoder then uses its
260
+ # documented 30 fps default, but the recorder omits ``video_fps`` from
261
+ # /run/start because packet timestamps, not an encoder setting, describe
262
+ # that producer-owned cadence. Callers such as evaluation filming set the
263
+ # measured cadence explicitly.
264
+ # Values must also fit a finite IEEE-754 double because both supported
265
+ # encoders and the MCAP JSON record use that representation.
266
+ video_fps: Optional[SupportsFloat] = None
257
267
  video_gop: Optional[int] = 30
258
268
  video_storage: Literal["embedded", "sidecar"] = "embedded"
259
269
  # When True (default), the recorder writes a parallel
@@ -300,6 +310,24 @@ class RecordConfig:
300
310
  raise ValueError(f"RecordConfig.profile must be one of {VALID_PROFILES}; got {self.profile!r}")
301
311
  if self.record_every_n_steps < 1:
302
312
  raise ValueError(f"RecordConfig.record_every_n_steps must be >= 1; got {self.record_every_n_steps}")
313
+ try:
314
+ video_fps = None if self.video_fps is None else float(self.video_fps)
315
+ except (OverflowError, TypeError, ValueError):
316
+ video_fps = None
317
+ if self.video_fps is not None and (
318
+ isinstance(self.video_fps, bool)
319
+ or not isinstance(self.video_fps, Real)
320
+ or video_fps is None
321
+ or not math.isfinite(video_fps)
322
+ # 0.1 fps is a policy floor that keeps rates sensible; ffmpeg's
323
+ # rational conversion only rounds to zero far below it.
324
+ or video_fps < 0.1
325
+ or video_fps > sys.float_info.max
326
+ ):
327
+ raise ValueError(
328
+ "RecordConfig.video_fps must be a finite real number representable by the encoder and at least 0.1 "
329
+ f"when set; got {self.video_fps!r}"
330
+ )
303
331
  # env_indices: accept any sequence of ints, including a torch tensor —
304
332
  # but normalize to a tuple of ints for hashability + reproducibility.
305
333
  try:
@@ -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``, …): six read-only,
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
- The members are LIVE VIEWS — each read goes straight through to the
29
- simulation's own buffers, at the same cost as reaching through the
30
- ``internals`` escape hatch, with no copy and no per-access container
31
- allocation — so reading them inside a training loop is free of hidden
32
- overhead. Unlike ``internals``, these six names ARE a compatibility promise:
33
- they keep working across engine upgrades, because the backend maps each one
34
- to whatever the engine calls it.
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):
@@ -68,27 +69,31 @@ from simulo.interfaces.runtime.tensors import TensorLike
68
69
 
69
70
  @runtime_checkable
70
71
  class RobotStateProtocol(Protocol):
71
- """The live robot-state contract — batched, read-only, zero-copy.
72
+ """The live robot-state contract — batched and read-only.
72
73
 
73
74
  Obtained from a robot as ``robot.state``; never constructed directly.
74
- Each member is a live view of the simulation's own state buffers: reading
75
- one returns the current values without copying, and the same access works
76
- identically inside a training loop's hot path. All members carry the
77
- environment batch as their leading dimension (the scene's ``num_envs``).
75
+ Six members (``pose``, the two world-frame velocities, and the three
76
+ ``joint_*`` members) are zero-copy live views of the simulation's own
77
+ buffers. ``linear_velocity_in_base_frame`` and
78
+ ``angular_velocity_in_base_frame`` are computed from the current base
79
+ orientation on every read. Each of those reads returns a new tensor,
80
+ costs a small computation, and cannot modify the simulation when written
81
+ into. All members carry the environment batch as their leading dimension
82
+ (the scene's ``num_envs``).
78
83
 
79
84
  **Read a member each time you need it — do not hold the returned tensor
80
- across steps, and do not write into it.** The tensor IS the simulation's
81
- own memory: the engine refreshes most of these buffers by rebinding them
82
- to new tensors, so a tensor held across a step can silently serve stale
83
- values, and writing into one writes into the simulation. The accessor
84
- (``robot.state`` itself) may be held freely; the tensors its members
85
- return should be re-read at each use.
85
+ across steps, and do not write into it.** The six live-view tensors share
86
+ simulation memory, and writing into one writes into the simulation. The
87
+ engine can also rebind those buffers, while the base-frame members are
88
+ recomputed, so a tensor held across a step can silently serve stale values.
89
+ The accessor (``robot.state`` itself) may be held freely; the tensors its
90
+ members return should be re-read at each use.
86
91
 
87
92
  Members raise ``RuntimeError`` until the simulation runtime has attached
88
93
  the robot — read them from ``on_start`` onward. The ``state`` accessor
89
94
  itself is always available and is the same object on every access.
90
95
 
91
- These six names are a compatibility promise: they keep working across
96
+ These eight names are a compatibility promise: they keep working across
92
97
  engine upgrades. For anything not named here, the ``internals`` escape
93
98
  hatch remains available — explicitly unstable, at your own risk.
94
99
  """
@@ -111,6 +116,24 @@ class RobotStateProtocol(Protocol):
111
116
  ``[wx, wy, wz]``."""
112
117
  ...
113
118
 
119
+ @property
120
+ def linear_velocity_in_base_frame(self) -> TensorLike:
121
+ """Base linear velocity expressed in the base frame, shape
122
+ ``(num_envs, 3)`` in m/s with columns ``[vx, vy, vz]``. Available
123
+ from ``on_start`` onward. Computed from the current base orientation
124
+ on every read; each read returns a new tensor, costs a small
125
+ computation, and writing into it does not reach the simulation."""
126
+ ...
127
+
128
+ @property
129
+ def angular_velocity_in_base_frame(self) -> TensorLike:
130
+ """Base angular velocity expressed in the base frame, shape
131
+ ``(num_envs, 3)`` in rad/s with columns ``[wx, wy, wz]``. Available
132
+ from ``on_start`` onward. Computed from the current base orientation
133
+ on every read; each read returns a new tensor, costs a small
134
+ computation, and writing into it does not reach the simulation."""
135
+ ...
136
+
114
137
  @property
115
138
  def joint_positions(self) -> TensorLike:
116
139
  """Joint positions (rad or m), shape ``(num_envs, num_joints)``."""
@@ -146,7 +169,7 @@ class RobotProtocol(Protocol):
146
169
  """Model source (USD or URDF) defining the robot."""
147
170
 
148
171
  initial_pose: Pose
149
- """Pose the robot spawns at, in the world frame (identity when not given)."""
172
+ """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
173
 
151
174
  collision_enabled: bool
152
175
  """Whether collision detection is enabled."""
@@ -203,12 +226,45 @@ class RobotProtocol(Protocol):
203
226
  """The grouped live robot state — the supported, typed state surface.
204
227
 
205
228
  Always available (the accessor is the same object on every access);
206
- its members read straight through to the simulation's state buffers
207
- and raise ``RuntimeError`` until the simulation runtime has attached
208
- the robot — read them from ``on_start`` onward.
229
+ six members read straight through to the simulation's state buffers,
230
+ while the two base-frame velocity members are computed into new
231
+ tensors on every read. Members raise ``RuntimeError`` until the
232
+ simulation runtime has attached the robot — read them from
233
+ ``on_start`` onward.
209
234
  """
210
235
  ...
211
236
 
237
+ @property
238
+ def default_joint_positions(self) -> TensorLike:
239
+ """Joint positions that :meth:`reset` writes, shape
240
+ ``(num_envs, num_joints)`` in rad for hinge joints and m for slider
241
+ joints. Columns use the same order as ``state.joint_positions`` and
242
+ the indices returned by :meth:`find_joints`. Available from
243
+ ``on_start`` onward. Each read returns a new tensor, so writing into
244
+ it cannot change a later reset."""
245
+ ...
246
+
247
+ @property
248
+ def default_joint_velocities(self) -> TensorLike:
249
+ """Joint velocities that :meth:`reset` writes, shape
250
+ ``(num_envs, num_joints)`` in rad/s for hinge joints and m/s for
251
+ slider joints. Columns use the same order as
252
+ ``state.joint_positions`` and the indices returned by
253
+ :meth:`find_joints`. Available from ``on_start`` onward. Each read
254
+ returns a new tensor, so writing into it cannot change a later
255
+ reset."""
256
+ ...
257
+
258
+ @property
259
+ def default_pose(self) -> TensorLike:
260
+ """Base pose that :meth:`reset` writes in the world frame, shape
261
+ ``(num_envs, 7)`` with columns ``[x, y, z, qw, qx, qy, qz]``:
262
+ position in m followed by a w-first unit quaternion. Each
263
+ environment's origin is included. Available from ``on_start``
264
+ onward. Each read returns a new tensor, so writing into it cannot
265
+ change a later reset."""
266
+ ...
267
+
212
268
  @property
213
269
  def sensors(self) -> List[Any]:
214
270
  """Sensors attached to this robot via :meth:`add_sensor`."""
@@ -59,10 +59,8 @@ class SubTerrainConfig:
59
59
  - "hf_pyramid_slope": Height-field pyramid slope
60
60
  - "hf_pyramid_slope_inv": Inverted height-field pyramid slope
61
61
  proportion: Proportion of terrain grid to use this type (0.0-1.0).
62
- Defaults to 1.0 — "use 100% of the grid for this single
63
- sub-terrain". Every in-tree caller passes ``proportion=``
64
- explicitly (Wave 0 audit verified), so the default-value
65
- change from the pre-Wave-B local default of 0.2 is safe.
62
+ Defaults to 1.0, which uses the whole grid for this single
63
+ sub-terrain.
66
64
  params: Type-specific parameters (e.g., height, step_height, etc.)
67
65
 
68
66
  Example: