scene_service.state.data_assoc

Associate per-frame detections with persistent scene objects.

The version 1 algorithm applies these steps:

  1. Gate candidates spatially using a class-specific radius.

  2. Keep candidates with the same class.

  3. Run Hungarian minimum-cost matching.

  4. Allocate objects for unmatched detections.

  5. Leave unmatched-object expiry to mark_stale.

  6. Update matched object poses with an exponential moving average.

Not implemented in v1 (deferred): Kalman filtering, IoU-based gating, appearance embeddings, learned association.

Functions

associate(registry, detections, *[, now])

Resolve detections against the registry.

Classes

Detection(cls, pose, bbox, confidence[, source])

One per-frame perception output.

class scene_service.state.data_assoc.Detection(cls: str, pose: Pose3D, bbox: BBox3D, confidence: float, source: str = 'perception')[source]

Bases: object

One per-frame perception output. Stable id is NOT supplied — it’s this layer’s job to assign / find one. pose is in map frame (ingest does the TF transform before producing Detection).

bbox: BBox3D
cls: str
confidence: float
pose: Pose3D
source: str = 'perception'
scene_service.state.data_assoc.associate(registry: ObjectRegistry, detections: list[Detection], *, now: float | None = None) tuple[list[str], list[str]][source]

Resolve detections against the registry. Caller must hold registry.lock().

Returns (matched_ids, new_ids) for logging / metrics. The registry is mutated in place: matched detections EMA-update existing records, unmatched detections allocate new ones, unmatched objects are NOT touched (mark_stale runs separately on a periodic tick).