feat(perception): qualify lossless lidar observations
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, cast
|
||||
|
||||
from k1link.compute import derive_motion_semantic_signal
|
||||
|
||||
PROFILE: dict[str, float | int] = {
|
||||
"minimum_motion_observations": 3,
|
||||
"minimum_motion_span_seconds": 0.2,
|
||||
"minimum_motion_displacement_m": 0.25,
|
||||
"minimum_motion_speed_mps": 0.4,
|
||||
"maximum_motion_speed_mps": 20.0,
|
||||
"proximity_threshold_m": 2.0,
|
||||
}
|
||||
|
||||
|
||||
def _component(*, state: str = "current") -> dict[str, Any]:
|
||||
return {
|
||||
"state": state,
|
||||
"temporal_id": 7,
|
||||
"source_owner_key": "track:42",
|
||||
"owner_kind": "camera-track",
|
||||
"last_observed_age_seconds": 0.0 if state == "current" else 0.25,
|
||||
"history_tail": [
|
||||
{
|
||||
"session_seconds": 1.0,
|
||||
"centroid_map_xyz_m": [0.0, 0.0, 0.0],
|
||||
},
|
||||
{
|
||||
"session_seconds": 1.5,
|
||||
"centroid_map_xyz_m": [0.5, 0.0, 0.0],
|
||||
},
|
||||
{
|
||||
"session_seconds": 2.0,
|
||||
"centroid_map_xyz_m": [1.0, 0.0, 0.0],
|
||||
},
|
||||
],
|
||||
"semantic_provenance": {
|
||||
"owner": "camera",
|
||||
"labels": ["person"],
|
||||
"track_ids": [42],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _geometry() -> dict[str, Any]:
|
||||
return {
|
||||
"evidence_state": "agree",
|
||||
"range_m": 1.5,
|
||||
"reason_codes": [
|
||||
"camera-semantic-with-connected-occupied-lidar-support"
|
||||
],
|
||||
"semantic": {
|
||||
"owner": "camera",
|
||||
"label": "person",
|
||||
"track_id": 42,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_e51_derives_bounded_motion_proximity_and_semantic_evidence() -> None:
|
||||
signal = derive_motion_semantic_signal(
|
||||
_component(),
|
||||
_geometry(),
|
||||
map_frame_jump_candidate=False,
|
||||
profile=PROFILE,
|
||||
)
|
||||
assert signal is not None
|
||||
motion = cast(dict[str, Any], signal["motion"])
|
||||
proximity = cast(dict[str, Any], signal["proximity"])
|
||||
semantic = cast(dict[str, Any], signal["semantic"])
|
||||
collision = cast(dict[str, Any], signal["collision"])
|
||||
assert motion["candidate"] is True
|
||||
assert motion["speed_mps"] == 1.0
|
||||
assert motion["dynamic_class_available"] is False
|
||||
assert proximity["candidate"] is True
|
||||
assert semantic["available"] is True
|
||||
assert semantic["confidence"]["available"] is False
|
||||
assert collision["state"] == "unavailable"
|
||||
assert signal["authority"]["navigation_or_safety_accepted"] is False
|
||||
|
||||
|
||||
def test_e51_rejects_motion_on_map_frame_jump_without_losing_semantics() -> None:
|
||||
signal = derive_motion_semantic_signal(
|
||||
_component(),
|
||||
_geometry(),
|
||||
map_frame_jump_candidate=True,
|
||||
profile=PROFILE,
|
||||
)
|
||||
assert signal is not None
|
||||
motion = cast(dict[str, Any], signal["motion"])
|
||||
semantic = cast(dict[str, Any], signal["semantic"])
|
||||
assert motion["candidate"] is False
|
||||
assert motion["map_frame_jump_rejected"] is True
|
||||
assert semantic["available"] is True
|
||||
|
||||
|
||||
def test_e51_held_component_cannot_publish_current_proximity() -> None:
|
||||
signal = derive_motion_semantic_signal(
|
||||
_component(state="held"),
|
||||
None,
|
||||
map_frame_jump_candidate=False,
|
||||
profile=PROFILE,
|
||||
)
|
||||
assert signal is not None
|
||||
freshness = cast(dict[str, Any], signal["freshness"])
|
||||
proximity = cast(dict[str, Any], signal["proximity"])
|
||||
semantic = cast(dict[str, Any], signal["semantic"])
|
||||
assert freshness["state"] == "held"
|
||||
assert freshness["current_hit_backed"] is False
|
||||
assert proximity["candidate"] is False
|
||||
assert semantic["available"] is True
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import json
|
||||
import struct
|
||||
from pathlib import Path
|
||||
from typing import Any, cast
|
||||
|
||||
import lz4.block
|
||||
import pytest
|
||||
@@ -11,11 +12,13 @@ from fastapi.routing import APIRoute
|
||||
|
||||
from k1link.compute import (
|
||||
K1_LIDAR_PACK_V2_PROFILE,
|
||||
K1LocalSurfaceV1,
|
||||
LidarPipelineStage,
|
||||
LidarReadiness,
|
||||
LidarReplayError,
|
||||
LidarReplayPackV2,
|
||||
assess_lidar_profile,
|
||||
build_k1_local_surface,
|
||||
build_lidar_replay_pack_v2,
|
||||
verify_lidar_replay_equivalence,
|
||||
)
|
||||
@@ -163,7 +166,11 @@ def _capture(tmp_path: Path) -> Path:
|
||||
|
||||
def _endpoint(router: APIRouter, path: str) -> object:
|
||||
for route in router.routes:
|
||||
if isinstance(route, APIRoute) and route.path == path and "GET" in route.methods:
|
||||
if (
|
||||
isinstance(route, APIRoute)
|
||||
and route.path == path
|
||||
and "GET" in (route.methods or set())
|
||||
):
|
||||
return route.endpoint
|
||||
raise AssertionError(f"GET {path} route is missing")
|
||||
|
||||
@@ -208,6 +215,38 @@ def test_lidar_replay_v2_retains_fields_and_passes_equivalence(tmp_path: Path) -
|
||||
assert pack.equivalence["array_mismatches"] == 0
|
||||
rerun = verify_lidar_replay_equivalence(capture, pack)
|
||||
assert rerun["status"] == "passed"
|
||||
|
||||
surface_output = build_k1_local_surface(
|
||||
pack,
|
||||
tmp_path / "local-surfaces",
|
||||
display_name="synthetic replay v2 local surface",
|
||||
)
|
||||
surface = K1LocalSurfaceV1(surface_output)
|
||||
try:
|
||||
assert surface.identity["source_pack_id"] == pack.pack_id
|
||||
assert (
|
||||
surface.identity["source_pack_identity_sha256"]
|
||||
== pack.manifest["identity_sha256"]
|
||||
)
|
||||
assert (
|
||||
surface.identity["source_schema_version"]
|
||||
== "missioncore.lidar-replay-pack/v2"
|
||||
)
|
||||
assert (
|
||||
surface.identity["source_representation"]
|
||||
== "lossless-lidar-replay-v2"
|
||||
)
|
||||
assert surface.identity["frame_count"] == pack.point_frame_count
|
||||
assert surface.identity["point_count"] == pack.point_count
|
||||
surface_source = cast(dict[str, Any], surface.report["source"])
|
||||
assert surface_source["intensity_available"] is True
|
||||
detail = surface.frame_detail(pack, 0)
|
||||
assert detail["source_pack_id"] == pack.pack_id
|
||||
assert detail["source_frame_index"] == 1
|
||||
detail_pose = cast(dict[str, Any], detail["pose"])
|
||||
assert detail_pose["binding_age_ms"] == pytest.approx(10.0)
|
||||
finally:
|
||||
surface.close()
|
||||
finally:
|
||||
pack.close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user