Spatial Model And Frame Graph
R2Kernel treats spatial composition as part of the robot architecture, not as an application-specific Event topic. A subsystem can participate in a frame graph without exposing transforms as a public communication contract.
The model has four layers.
component spec = which spatial ports a compatible component must expose
subsystem manifest = which local frames implement those ports
compound manifest = how bound subsystem ports are mounted together
runtime frame graph = live static and dynamic transforms for this deployment
Spatial Ports Are Not Communication Interfaces
provides and requires declare communication contracts. Spatial ports declare
physical attachment and reference-frame boundaries. A spatial port does not
create an Event, Control, Procedure, or Operation endpoint.
Every public component spec explicitly declares whether it participates.
spatial:
participation: required
ports:
base:
direction: import
semantic: mounting_frame
tool:
direction: export
semantic: tool_frame
Use participation: none when spatial topology is not meaningful for that
component. This is explicit so a provider cannot silently omit required frame
semantics.
Concrete Frame Mapping
A subsystem maps the component ports to names local to its own process.
spatial:
participation: required
ports:
base:
direction: import
frame: arm_base
tool:
direction: export
frame: tool0
Local names do not need to be globally unique. The runtime qualifies them as
<subsystem>/<frame>, for example component.arm/tool0.
An alias can also resolve a port through a required binding. This is useful when a compound subsystem re-exports the frame of a bound component.
spatial:
participation: required
ports:
body:
direction: export
source:
requirement: mobility
port: body
Compound Mounts
A compound subsystem owns the static topology between the subsystems that form its body. Mount endpoints refer to logical requirements, so the topology remains valid when deployment chooses a different compatible provider.
spatial:
participation: required
mounts:
- name: arm_on_body
parent:
requirement: mobility
port: body
child:
requirement: manipulation
port: base
transform:
translation: [0.18, 0.0, 0.12]
rotation_xyzw: [0.0, 0.0, 0.0, 1.0]
The parent port must export a frame and the child port must import one. The deployment validator resolves both through current bindings and rejects missing ports, invalid directions, cycles, and multiple parents.
Dynamic Transform Ownership
The subsystem that computes a changing relationship owns that transform. A
localizer can report world -> base, while a manipulator component can report
arm_base -> tool0. The compound declares only the static mount between the
mobile body and the arm base.
Python example.
self.set_frame_transform(
parent_frame="world",
child_frame="base_link",
translation=(x, y, 0.0),
rotation_xyzw=(0.0, 0.0, qz, qw),
observed_at_sync_ns=self.now_ns(),
stale_after_ms=500,
)
C++ uses r2kernel::FrameTransform with Subsystem::set_frame_transform().
Updates use a lightweight runtime path. The regular heartbeat still carries a
complete snapshot so the graph recovers after a control-backend restart.
Runtime Lookup And Diagnostics
Inspect the current graph.
r2kernel frame graph
Resolve one frame into another.
r2kernel frame lookup component.mobile/world component.manipulation/tool0
The same data is available through Host Control REST.
GET /api/v1/control/frames
GET /api/v1/control/frames/lookup?source=<frame>&target=<frame>
Dynamic edges have a stale deadline. Stale edges remain visible for diagnosis but are excluded from lookup. The graph also reports unresolved ports, invalid mounts, cycles, and frames with multiple parents.
What The Robot Manifest Owns
The robot manifest still names the reusable platform, slots, and application-facing capabilities. It does not duplicate concrete frame topology. Runtime topology is owned by subsystem spatial declarations and the compound subsystem that connects them.