feat(perception): run PointPillars on RAVNOVES00

This commit is contained in:
DCCONSTRUCTIONS
2026-07-31 14:35:34 +03:00
parent b44c4b3265
commit 55b7821a5c
11 changed files with 1760 additions and 9 deletions
+30
View File
@@ -53,6 +53,36 @@ def test_advanced_index_is_empty_when_not_configured() -> None:
}
def test_advanced_index_publishes_l31_ravnoves_identity(
tmp_path: Path,
monkeypatch: MonkeyPatch,
) -> None:
result_id = f"l31-pointpillars-ravnoves-{'b' * 64}"
def fake_latest(provider: object) -> dict[str, str]:
assert callable(provider)
assert provider() == tmp_path # type: ignore[operator]
return {
"result_id": result_id,
"created_at_utc": "2026-07-31T10:56:45.861Z",
}
monkeypatch.setattr(advanced_api, "latest_l31_identity", fake_latest)
router = build_advanced_laboratory_router(
l31_ravnoves_root_provider=lambda: tmp_path,
)
route = _endpoint(router, "/api/v1/laboratory/advanced-index")
assert route()["items"] == [ # type: ignore[index,operator]
{
"work_id": "l31-pointpillars-ravnoves",
"result_id": result_id,
"created_at_utc": "2026-07-31T10:56:45.861Z",
"access": "read-only",
}
]
def test_advanced_index_reads_only_bounded_identity_documents(
tmp_path: Path,
) -> None:
+49
View File
@@ -0,0 +1,49 @@
from __future__ import annotations
import numpy as np
import pytest
from k1link.compute.l31_pointpillars_ravnoves import (
L31PointPillarsRavnovesError,
nearest_pose_indices,
select_visual_frame_indices,
sensor_frame_xyzi,
)
def test_nearest_pose_binding_prefers_earlier_pose_on_tie() -> None:
indices, age_ms = nearest_pose_indices(
np.asarray([10, 20, 31], dtype=np.int64) * 1_000_000,
np.asarray([5, 15, 30], dtype=np.int64) * 1_000_000,
)
assert indices.tolist() == [0, 1, 2]
assert age_ms.tolist() == [5.0, 5.0, 1.0]
def test_sensor_frame_xyzi_inverts_map_pose() -> None:
points = np.asarray([[11.0, 20.0, 30.0], [10.0, 22.0, 30.0]])
intensity = np.asarray([255, 0], dtype=np.uint8)
result = sensor_frame_xyzi(
points,
intensity,
position_map_xyz=np.asarray([10.0, 20.0, 30.0]),
orientation_map_from_lidar_xyzw=np.asarray([0.0, 0.0, 0.0, 1.0]),
)
np.testing.assert_allclose(
result,
np.asarray([[1.0, 0.0, 0.0, 1.0], [0.0, 2.0, 0.0, 0.0]]),
)
def test_visual_selection_prefers_vehicles_with_route_coverage() -> None:
selected = select_visual_frame_indices(
[0, 3, 1, 0, 0, 2],
[5, 3, 9, 0, 4, 2],
maximum_frames=3,
)
assert selected == (1, 2, 5)
def test_visual_selection_rejects_impossible_class_count() -> None:
with pytest.raises(L31PointPillarsRavnovesError):
select_visual_frame_indices([2], [1])
+202
View File
@@ -0,0 +1,202 @@
from __future__ import annotations
import hashlib
import json
from pathlib import Path
from fastapi import APIRouter
from fastapi.routing import APIRoute
from pytest import raises
from k1link.web.l31_pointpillars_ravnoves_api import (
build_l31_pointpillars_ravnoves_router,
)
def _endpoint(router: APIRouter, path: str) -> object:
for route in router.routes:
if (
isinstance(route, APIRoute)
and route.path == path
and route.methods is not None
and "GET" in route.methods
):
return route.endpoint
raise AssertionError(f"GET {path} route is missing")
def _canonical(payload: object) -> bytes:
return json.dumps(
payload,
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
).encode("utf-8")
def _write(path: Path, payload: object) -> dict[str, object]:
content = _canonical(payload)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(content)
return {
"sha256": hashlib.sha256(content).hexdigest(),
"byte_length": len(content),
}
def _result(root: Path) -> tuple[str, str]:
frame_id = "000075"
identity = {
"source_session_id": "20260720T065719Z_viewer_live",
"source_pack_id": f"lidar-replay-pack-{'1' * 64}",
"source_logical_content_sha256": "2" * 64,
"model": {"name": "pointpillars", "source_model_sha256": "3" * 64},
"execution": {
"worker_host_id": "worker-006",
"sequential": True,
"parallel_workers": 1,
},
"authority": {
"shadow_only": True,
"commands_enabled": False,
"navigation_or_safety_accepted": False,
"accuracy_accepted": False,
},
}
identity_sha = hashlib.sha256(_canonical(identity)).hexdigest()
result_id = f"l31-pointpillars-ravnoves-{identity_sha}"
candidate = root / result_id
detail = {
"schema_version": "missioncore.l31-pointpillars-ravnoves-visual-frame/v1",
"frame_id": frame_id,
"points": {
"layout": "flat-xyzi",
"sampled_point_count": 1,
"values": [1.0, 2.0, 3.0, 0.5],
},
"prediction_boxes": [
{
"x_m": 1.0,
"y_m": 2.0,
"z_m": 0.5,
"length_m": 4.0,
"width_m": 2.0,
"height_m": 1.5,
"yaw_rad": 0.1,
"class_id": 0,
"model_class": "Vehicle",
"score": 0.8,
}
],
"interpretation": {
"ground_truth_available": False,
"boxes_are_model_hypotheses": True,
"accuracy_claim_allowed": False,
},
}
detail_descriptor = _write(
candidate / "visual-frames" / f"{frame_id}.json",
detail,
)
frame = {
"frame_id": frame_id,
"frame_index": 75,
"session_seconds": 7.5,
"source_point_count": 2143,
"prediction_count": 1,
"class_counts": {"Vehicle": 1, "Pedestrian": 0, "Cyclist": 0},
"inference_ms": 26.3,
"detail_path": f"visual-frames/{frame_id}.json",
"detail_sha256": detail_descriptor["sha256"],
"detail_byte_length": detail_descriptor["byte_length"],
}
catalog = {
"schema_version": "missioncore.l31-pointpillars-ravnoves-catalog/v1",
"result_id": result_id,
"source_session_id": identity["source_session_id"],
"frame_count": 1,
"frames": [frame],
}
catalog_descriptor = _write(candidate / "catalog.json", catalog)
manifest = {
"schema_version": "missioncore.l31-pointpillars-ravnoves/v1",
"result_id": result_id,
"identity_sha256": identity_sha,
"identity": identity,
"created_at_utc": "2026-07-31T10:00:00Z",
"status": "k1-cross-domain-transfer-measured-visual-review-required",
"metrics": {
"frame_count": 4570,
"input_admission_fraction": 1.0,
"output_schema_valid_fraction": 1.0,
},
"catalog": {
"path": "catalog.json",
"kind": "visual-frame-catalog",
**catalog_descriptor,
},
"limitations": ["Independent 3D ground truth is unavailable."],
"authority": identity["authority"],
}
_write(candidate / "manifest.json", manifest)
return result_id, frame_id
def test_l31_catalog_and_visual_frame_are_read_only(tmp_path: Path) -> None:
result_id, frame_id = _result(tmp_path)
router = build_l31_pointpillars_ravnoves_router(
root_provider=lambda: tmp_path,
)
catalog_route = _endpoint(
router,
"/api/v1/laboratory/l31/pointpillars-ravnoves/results",
)
frame_route = _endpoint(
router,
(
"/api/v1/laboratory/l31/pointpillars-ravnoves/"
"{result_id}/frames/{frame_id}"
),
)
catalog = catalog_route(limit=1) # type: ignore[operator]
assert catalog["configured"] is True
assert catalog["invalid_total"] == 0
assert catalog["items"][0]["result_id"] == result_id
assert catalog["items"][0]["status"] == (
"cross-domain-transfer-measured-visual-review-required"
)
assert catalog["items"][0]["frames"][0]["frame_id"] == frame_id
frame = frame_route( # type: ignore[operator]
result_id=result_id,
frame_id=frame_id,
)
assert frame["prediction_boxes"][0]["model_class"] == "Vehicle"
assert frame["interpretation"]["ground_truth_available"] is False
assert frame["access"] == "read-only"
def test_l31_visual_frame_fails_closed_after_mutation(tmp_path: Path) -> None:
result_id, frame_id = _result(tmp_path)
router = build_l31_pointpillars_ravnoves_router(
root_provider=lambda: tmp_path,
)
frame_route = _endpoint(
router,
(
"/api/v1/laboratory/l31/pointpillars-ravnoves/"
"{result_id}/frames/{frame_id}"
),
)
(tmp_path / result_id / "visual-frames" / f"{frame_id}.json").write_text(
"{}",
encoding="utf-8",
)
with raises(Exception) as caught:
frame_route( # type: ignore[operator]
result_id=result_id,
frame_id=frame_id,
)
assert getattr(caught.value, "status_code", None) == 404