feat(perception): freeze blind detector gates
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
|
||||
import pytest
|
||||
|
||||
from k1link.compute.e45_binding_sensitivity import (
|
||||
E45_ROW_SCHEMA,
|
||||
E45BindingSensitivityError,
|
||||
E45BindingSensitivityProfile,
|
||||
analyze_binding_sensitivity,
|
||||
)
|
||||
|
||||
|
||||
def _row(
|
||||
item_id: str,
|
||||
*,
|
||||
radius: float,
|
||||
speed: float,
|
||||
angular: float,
|
||||
lidar_age: float,
|
||||
pose_age: float,
|
||||
residual: float,
|
||||
points: int,
|
||||
) -> dict[str, object]:
|
||||
age_seconds = (lidar_age + pose_age) / 1000.0
|
||||
return {
|
||||
"schema_version": E45_ROW_SCHEMA,
|
||||
"item_id": item_id,
|
||||
"image_radius_normalized": radius,
|
||||
"translation_speed_mps": speed,
|
||||
"angular_speed_deg_s": angular,
|
||||
"lidar_camera_age_ms": lidar_age,
|
||||
"pose_point_age_ms": pose_age,
|
||||
"motion_exposure_translation_m": speed * age_seconds,
|
||||
"motion_exposure_rotation_deg": angular * age_seconds,
|
||||
"centroid_residual_bbox_diagonal": residual,
|
||||
"occupied_points_in_bbox": points,
|
||||
"supported": points >= 2,
|
||||
}
|
||||
|
||||
|
||||
def test_e45_stratifies_existing_diagnostic_residual_without_target_claim() -> None:
|
||||
rows = [
|
||||
_row(
|
||||
"one",
|
||||
radius=0.2,
|
||||
speed=0.0,
|
||||
angular=0.0,
|
||||
lidar_age=5.0,
|
||||
pose_age=2.0,
|
||||
residual=0.1,
|
||||
points=5,
|
||||
),
|
||||
_row(
|
||||
"two",
|
||||
radius=0.7,
|
||||
speed=0.5,
|
||||
angular=6.0,
|
||||
lidar_age=40.0,
|
||||
pose_age=15.0,
|
||||
residual=0.2,
|
||||
points=8,
|
||||
),
|
||||
_row(
|
||||
"three",
|
||||
radius=1.0,
|
||||
speed=2.0,
|
||||
angular=20.0,
|
||||
lidar_age=80.0,
|
||||
pose_age=35.0,
|
||||
residual=0.4,
|
||||
points=12,
|
||||
),
|
||||
]
|
||||
|
||||
analysis = analyze_binding_sensitivity(rows)
|
||||
|
||||
assert analysis["correspondence_count"] == 3
|
||||
assert analysis["supported_fraction"] == 1.0
|
||||
assert [
|
||||
item["count"] for item in analysis["strata"]["image_radius"]
|
||||
] == [1, 1, 1]
|
||||
assert math.isclose(
|
||||
analysis["spearman_residual_correlation"][
|
||||
"image_radius_normalized"
|
||||
],
|
||||
1.0,
|
||||
)
|
||||
assert (
|
||||
analysis["measured_calibration_target_residual_available"] is False
|
||||
)
|
||||
assert analysis["physical_mount_inferred"] is False
|
||||
|
||||
|
||||
def test_e45_rejects_missing_support_in_accepted_correspondence() -> None:
|
||||
rows = [
|
||||
_row(
|
||||
"one",
|
||||
radius=0.2,
|
||||
speed=0.0,
|
||||
angular=0.0,
|
||||
lidar_age=5.0,
|
||||
pose_age=2.0,
|
||||
residual=0.1,
|
||||
points=1,
|
||||
)
|
||||
]
|
||||
|
||||
with pytest.raises(E45BindingSensitivityError, match="supported"):
|
||||
analyze_binding_sensitivity(rows)
|
||||
|
||||
|
||||
def test_e45_profile_rejects_overlapping_or_reversed_edges() -> None:
|
||||
with pytest.raises(E45BindingSensitivityError):
|
||||
E45BindingSensitivityProfile(image_radius_edges=(0.8, 0.4))
|
||||
Reference in New Issue
Block a user