feat(perception): integrate calibrated operator pipeline
Add calibrated K1 projection, recorded and near-live perception qualification, unified Rerun operator layers, bounded replay admission, audited viewer controls, worker experiments, and lab evidence.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from k1link.device_plugins.xgrids_k1.analyze.calibrated_projection import (
|
||||
CalibratedProjectionError,
|
||||
Kb4ProjectionProfile,
|
||||
depth_colors,
|
||||
map_points_to_lidar,
|
||||
project_map_points_kb4,
|
||||
quaternion_xyzw_to_rotation_matrix,
|
||||
)
|
||||
from k1link.device_plugins.xgrids_k1.calibration_schema import (
|
||||
parse_k1_factory_calibration,
|
||||
)
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures" / "k1" / "calibration"
|
||||
|
||||
|
||||
def _identity_profile() -> Kb4ProjectionProfile:
|
||||
transform = np.eye(4, dtype=np.float64)
|
||||
transform.setflags(write=False)
|
||||
return Kb4ProjectionProfile(
|
||||
source_id="sensor.camera.right",
|
||||
calibration_slot="camera_1",
|
||||
width=800,
|
||||
height=600,
|
||||
intrinsic_fx_fy_cx_cy=(100.0, 100.0, 400.0, 300.0),
|
||||
distortion_kb4=(0.0, 0.0, 0.0, 0.0),
|
||||
t_camera_from_lidar=transform,
|
||||
)
|
||||
|
||||
|
||||
def test_pose_inverse_maps_world_points_back_into_lidar_frame() -> None:
|
||||
half = math.sqrt(0.5)
|
||||
points_lidar = map_points_to_lidar(
|
||||
[[10.0, 21.0, 30.0]],
|
||||
position_map_xyz=(10.0, 20.0, 30.0),
|
||||
orientation_map_from_lidar_xyzw=(0.0, 0.0, half, half),
|
||||
)
|
||||
np.testing.assert_allclose(points_lidar, [[1.0, 0.0, 0.0]], atol=1e-12)
|
||||
|
||||
|
||||
def test_quaternion_rotation_is_orthonormal_after_normalization() -> None:
|
||||
rotation = quaternion_xyzw_to_rotation_matrix((0.0, 0.0, 2.0, 2.0))
|
||||
assert rotation @ rotation.T == pytest.approx(np.eye(3), abs=1e-12)
|
||||
assert np.linalg.det(rotation) == pytest.approx(1.0, abs=1e-12)
|
||||
|
||||
|
||||
def test_kb4_projection_uses_theta_polynomial_and_rejects_behind_camera() -> None:
|
||||
projected = project_map_points_kb4(
|
||||
[[0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [0.0, 0.0, -1.0]],
|
||||
position_map_xyz=(0.0, 0.0, 0.0),
|
||||
orientation_map_from_lidar_xyzw=(0.0, 0.0, 0.0, 1.0),
|
||||
profile=_identity_profile(),
|
||||
)
|
||||
assert projected.source_point_count == 3
|
||||
assert projected.camera_front_point_count == 2
|
||||
assert projected.projected_point_count == 2
|
||||
assert projected.pixels_xy[0] == pytest.approx([400.0, 300.0])
|
||||
assert projected.pixels_xy[1] == pytest.approx(
|
||||
[400.0 + 100.0 * math.pi / 4.0, 300.0]
|
||||
)
|
||||
assert projected.source_indices.tolist() == [0, 1]
|
||||
|
||||
|
||||
def test_factory_profile_binds_right_main_camera_and_scales_intrinsics() -> None:
|
||||
calibration = parse_k1_factory_calibration(
|
||||
(FIXTURES / "camera.yaml").read_bytes(),
|
||||
(FIXTURES / "extrinsic_camera_lidar.yaml").read_bytes(),
|
||||
)
|
||||
profile = Kb4ProjectionProfile.from_factory_calibration(
|
||||
calibration,
|
||||
"sensor.camera.right",
|
||||
)
|
||||
camera = calibration.camera("camera_1")
|
||||
assert profile.calibration_slot == "camera_1"
|
||||
assert (profile.width, profile.height) == (800, 600)
|
||||
assert profile.intrinsic_fx_fy_cx_cy == pytest.approx(
|
||||
tuple(value * 0.2 for value in camera.intrinsic)
|
||||
)
|
||||
assert profile.t_camera_from_lidar == pytest.approx(
|
||||
np.asarray(calibration.t_camera_from_lidar("camera_1"))
|
||||
)
|
||||
|
||||
|
||||
def test_projection_rejects_unknown_camera_and_invalid_quaternion() -> None:
|
||||
calibration = parse_k1_factory_calibration(
|
||||
(FIXTURES / "camera.yaml").read_bytes(),
|
||||
(FIXTURES / "extrinsic_camera_lidar.yaml").read_bytes(),
|
||||
)
|
||||
with pytest.raises(CalibratedProjectionError, match="admitted K1 main camera"):
|
||||
Kb4ProjectionProfile.from_factory_calibration(calibration, "sensor.camera.unknown")
|
||||
with pytest.raises(CalibratedProjectionError, match="no usable norm"):
|
||||
quaternion_xyzw_to_rotation_matrix((0.0, 0.0, 0.0, 0.0))
|
||||
|
||||
|
||||
def test_depth_colors_are_bounded_and_repeatable() -> None:
|
||||
first = depth_colors([1.0, 2.0, 3.0])
|
||||
second = depth_colors([1.0, 2.0, 3.0])
|
||||
assert first.dtype == np.uint8
|
||||
assert first.shape == (3, 3)
|
||||
assert np.array_equal(first, second)
|
||||
assert first[0, 0] > first[0, 2]
|
||||
assert first[-1, 2] > first[-1, 0]
|
||||
Reference in New Issue
Block a user