- Canonical
DistanceMatrixlayout is(azimuth, elevation). - Environment azimuth bin
0is the forward ray along local-Z. - Dashboard and replay tools must roll the azimuth axis before center-cropping a forward FOV; otherwise the forward seam is split across the panorama edges and front-view panels appear distorted.
- Canonical training/runtime datasets come only from the downloaded corpus compiled into
artifacts/gmdag/corpus; no generated or procedural scene source is allowed in the canonical path.
Package: navi-contracts
Status: Active canonical specification
Policy: See AGENTS.md for implementation rules and non-negotiables
The following canonical dataclasses are the only models permitted on the inter-process wire. No additional models may be added without explicit approval. Visualization types (RGB frames, camera images) are never part of these contracts.
6-DOF robot pose with timestamp.
@dataclass(frozen=True, slots=True)
class RobotPose:
x: float
y: float
z: float
roll: float
pitch: float
yaw: float
timestamp: floatCanonical observation contract for training and inference.
@dataclass(frozen=True, slots=True)
class DistanceMatrix:
episode_id: int
env_ids: NDArray[int32] # (batch,)
matrix_shape: tuple[int, int] # (azimuth_bins, elevation_bins)
depth: NDArray[float32] # (n_envs, Az, El), normalized [0, 1]
delta_depth: NDArray[float32] # (n_envs, Az, El), frame difference
semantic: NDArray[int32] # (n_envs, Az, El), class IDs [0, 10]
valid_mask: NDArray[bool_] # (n_envs, Az, El), ray hit validity
overhead: NDArray[float32] # (H, W, 3), BGR minimap
robot_pose: RobotPose
step_id: int
timestamp: floatShape convention:
matrix_shape[0]= azimuth bins (rows),matrix_shape[1]= elevation bins (columns).- Default resolution:
(256, 48). - Single-env backends produce
n_envs = 1→ array shapes(1, Az, El). depthis normalized to[0, 1]by dividing bymax_distance.delta_depthis the per-bin change since the previous step (temporal velocity awareness).semanticuses integer IDs in[0, 10](see SIMULATION.md §3.3 for the full semantic table).valid_maskisTruefor bins that received at least one ray hit.overheadis a 256×256 BGR minimap centered on the robot. This field is consumed only by the Gallery Layer — it never enters the training engine. It is the sole visualization-adjacent field permitted in the wire contract, included for diagnostic convenience. It carries no semantic training data.- viewer layers are consumers, not contract authors: dashboard layout,
half-sphere extraction, axis rolling for display, or palette choice must not
change
DistanceMatrixshape, normalization, semantic meaning, or wire fields.
Movement command produced by the Brain policy.
@dataclass(frozen=True, slots=True)
class Action:
env_ids: NDArray[int32] # (batch,)
linear_velocity: NDArray[float32] # (batch, 3) — [forward, vertical, lateral]
angular_velocity: NDArray[float32] # (batch, 3) — [roll_rate, pitch_rate, yaw_rate]
policy_id: str # identifies which policy produced this action
step_id: int
timestamp: float4-DOF mapping: The actor internally produces [fwd, vert, lat, yaw].
On the wire this is packed as:
linear_velocity[:, 0]= forward,[:, 1]= vertical,[:, 2]= lateral.angular_velocity[:, 2]= yaw rate (roll and pitch rates are typically zero).
Discrete step request from Brain to Simulation Layer (REQ/REP).
@dataclass(frozen=True, slots=True)
class StepRequest:
action: Action
step_id: int
timestamp: floatStep acknowledgement from Simulation Layer to Brain (REQ/REP).
@dataclass(frozen=True, slots=True)
class StepResult:
step_id: int
env_id: int
episode_id: int
done: bool
truncated: bool
reward: float
episode_return: float
timestamp: floatAsynchronous telemetry event for logging, dashboarding, and replay.
@dataclass(frozen=True, slots=True)
class TelemetryEvent:
event_type: str # e.g., "ppo.update", "environment.step"
episode_id: int
env_id: int
step_id: int
payload: NDArray[float32] # generic numeric payload
timestamp: floatBatched step request from Brain to Simulation Layer (REQ/REP).
@dataclass(frozen=True, slots=True)
class BatchStepRequest:
actions: tuple[Action, ...]
step_id: int
timestamp: floatBatched step reply from Simulation Layer to Brain (REQ/REP).
@dataclass(frozen=True, slots=True)
class BatchStepResult:
results: tuple[StepResult, ...]
observations: tuple[DistanceMatrix, ...]Defined in navi_contracts.types for static analysis and documentation:
| Alias | Underlying Type | Shape | Description |
|---|---|---|---|
MatrixShape |
tuple[int, int] |
— | (azimuth_bins, elevation_bins) |
DepthMatrix |
NDArray[float32] |
(B, Az, El) |
Normalized distance values [0, 1] |
DeltaDepthMatrix |
NDArray[float32] |
(B, Az, El) |
Temporal depth deltas |
SemanticMatrix |
NDArray[int32] |
(B, Az, El) |
Semantic identifiers per cell |
ValidMask |
NDArray[bool_] |
(B, Az, El) |
True where a ray hit is valid |
EnvIdVector |
NDArray[int32] |
(B,) |
Active environment IDs |
VelocityMatrix |
NDArray[float32] |
(B, 3) |
Linear or angular velocity commands |
TelemetryPayload |
NDArray[float32] |
(N, M) |
Generic numeric telemetry payload |
All inter-service communication uses versioned topic strings for PUB/SUB routing and REQ/REP message discrimination.
| Constant | Value | Transport | Direction |
|---|---|---|---|
TOPIC_DISTANCE_MATRIX |
distance_matrix_v2 |
PUB/SUB | Simulation → Brain, Gallery |
TOPIC_ACTION |
action_v2 |
PUB/SUB | Brain → Simulation, Gallery |
TOPIC_STEP_REQUEST |
step_request_v2 |
REQ/REP | Brain → Simulation |
TOPIC_STEP_RESULT |
step_result_v2 |
REQ/REP | Simulation → Brain |
TOPIC_TELEMETRY_EVENT |
telemetry_event_v2 |
PUB/SUB | Any → Gallery |
No other topics may be added without explicit approval.
The imported project documented low-level tensor contracts explicitly. Navi now does the same, while keeping them clearly separate from the public wire format.
These seams are normative for performance work but are not additional wire models.
Canonical raycasting tensors:
| Tensor | Shape | Dtype | Device | Notes |
|---|---|---|---|---|
origins |
[B, R, 3] |
float32 |
CUDA | contiguous |
dirs |
[B, R, 3] |
float32 |
CUDA | contiguous, direction vectors |
out_distances |
[B, R] |
float32 |
CUDA | preallocated output |
out_semantics |
[B, R] |
int32 |
CUDA | preallocated output |
Scalar parameters:
| Parameter | Type | Default | Notes |
|---|---|---|---|
sdf_max_steps |
int |
config | max sphere-tracing iterations |
max_distance |
float |
config | horizon clamp distance |
bbox_min / bbox_max |
float |
asset | DAG world bounds |
resolution |
int |
asset | DAG voxel resolution |
skip_direction_validation |
bool |
False |
bypass direction-norm validation |
Boundary rules:
- device placement, rank, and contiguity are validated before kernel launch
- direction vectors must be normalized within an explicit tolerance; exact
floating-point equality with
1.0is not a valid contract rule - when
skip_direction_validation=True, the caller guarantees normalization and the four GPU→CPU synchronization barriers for norm checking are skipped; canonicalSdfDagBackendhot-path calls use this mode because yaw-rotated unit vectors are mathematically guaranteed normalized - probe, inspection, and diagnostic calls should keep
skip_direction_validation=False - canonical runtime is CUDA-only; CPU fallback is not part of this seam
- long CUDA execution should release the Python GIL
The canonical trainer may consume tensor-native observations directly when the runtime provides them.
Current canonical observation tensor:
- shape:
(B, 3, Az, El) - channel
0: normalized depth - channel
1: semantic ids asfloat32 - channel
2: valid mask asfloat32
This seam exists to keep the hot path on CUDA.
It does not replace the external DistanceMatrix contract.
Observer tools may materialize, crop, or colorize tensors after publication, but they do not change this seam's channel meaning, normalization, or axis ordering.
DistanceMatrix and Action remain the canonical service and diagnostic
contracts. On the production training hot path, they may be materialized only
when needed for:
- passive dashboard publication
- coarse telemetry
- service mode
- tests and explicit diagnostics
Rebuilding them unconditionally in the rollout loop is a performance bug.
If browser-facing transport is introduced for dashboards or dataset QA, it is an observer-side proxy layered on top of the canonical ZMQ surfaces.
That proxy does not widen the public wire contract and must never be used in hot-path throughput reasoning.
All messages are serialized via msgpack with custom extension types for numpy arrays. The encoding pipeline:
- Dataclass fields are converted to a msgpack-compatible dict.
- numpy arrays are packed as msgpack ext types preserving dtype and shape.
RobotPoseis serialized as a nested dict viadataclasses.asdict().
Messages are sent as multipart ZMQ frames:
Frame 0: topic_bytes (UTF-8 encoded topic string)
Frame 1: payload_bytes (msgpack-serialized message)
from navi_contracts import serialize, deserialize
# Encode
payload: bytes = serialize(distance_matrix)
# Decode (auto-detects type)
msg = deserialize(payload)
assert isinstance(msg, DistanceMatrix)- v2 only. Legacy wire contracts and topics are not permitted in new code.
- No new models may be added to this package without explicit approval.
- No visualization types. RGB frames, camera images, and rendered outputs are never canonical contracts. They belong exclusively to the Gallery Layer.
- Immutable dataclasses. All models use
frozen=True, slots=Truefor safety and performance. - Service sovereignty. No service may import another service's package. All integration is via serialized messages over ZMQ.
- Internal seams do not widen the wire. Tensor-native training paths may exist internally, but they do not authorize new public wire models.