from __future__ import annotations from dataclasses import dataclass, field from typing import Any, Dict, Optional import numpy as np @dataclass class LocalizationFrameState: odom_to_map: np.ndarray = field(default_factory=lambda: np.eye(4, dtype=np.float64)) odom_to_ref: np.ndarray = field(default_factory=lambda: np.eye(4, dtype=np.float64)) last_alignment: np.ndarray = field(default_factory=lambda: np.eye(4, dtype=np.float64)) confidence: float = 0.0 ref_valid: bool = False def reset(self) -> None: self.odom_to_map = np.eye(4, dtype=np.float64) self.odom_to_ref = np.eye(4, dtype=np.float64) self.last_alignment = np.eye(4, dtype=np.float64) self.confidence = 0.0 self.ref_valid = False def reset_reference(self) -> None: self.odom_to_ref = np.eye(4, dtype=np.float64) self.last_alignment = np.eye(4, dtype=np.float64) self.confidence = 0.0 self.ref_valid = False def set_reference_alignment(self, odom_to_ref: np.ndarray, confidence: float) -> None: tf = np.asarray(odom_to_ref, dtype=np.float64) if tf.shape != (4, 4): return self.odom_to_ref = np.array(tf, dtype=np.float64, copy=True) self.last_alignment = np.array(tf, dtype=np.float64, copy=True) self.confidence = float(np.clip(confidence, 0.0, 1.0)) self.ref_valid = True def update_confidence(self, confidence: float) -> None: self.confidence = float(np.clip(confidence, 0.0, 1.0)) def invalidate_reference(self) -> None: self.ref_valid = False self.confidence = 0.0 def apply_map_correction(self, correction: np.ndarray) -> None: corr = np.asarray(correction, dtype=np.float64) if corr.shape != (4, 4): return self.odom_to_map = corr @ np.asarray(self.odom_to_map, dtype=np.float64) def apply_loop_correction(self, correction: np.ndarray, update_reference: bool = True) -> None: corr = np.asarray(correction, dtype=np.float64) if corr.shape != (4, 4): return self.odom_to_map = corr @ np.asarray(self.odom_to_map, dtype=np.float64) if update_reference and self.ref_valid: try: inv_corr = np.linalg.inv(corr) self.odom_to_ref = np.asarray(self.odom_to_ref, dtype=np.float64) @ inv_corr self.last_alignment = np.array(self.odom_to_ref, dtype=np.float64, copy=True) except Exception: pass def map_pose(self, odom_pose: Optional[np.ndarray]) -> Optional[np.ndarray]: if odom_pose is None: return None pose = np.asarray(odom_pose, dtype=np.float64) if pose.shape != (4, 4): return None return np.asarray(self.odom_to_map, dtype=np.float64) @ pose def ref_pose(self, odom_pose: Optional[np.ndarray]) -> Optional[np.ndarray]: if odom_pose is None or not self.ref_valid: return None pose = np.asarray(odom_pose, dtype=np.float64) if pose.shape != (4, 4): return None return np.asarray(self.odom_to_ref, dtype=np.float64) @ pose def snapshot(self) -> Dict[str, Any]: return { "ref_valid": bool(self.ref_valid), "confidence": float(self.confidence), "odom_to_map_t": np.asarray(self.odom_to_map[:3, 3], dtype=np.float64).tolist(), "odom_to_ref_t": np.asarray(self.odom_to_ref[:3, 3], dtype=np.float64).tolist(), }