""" End-to-End Tests für die Pipeline gegen echte Daten. Diese Tests rufen die Pipeline direkt auf und validieren, dass sinnvolle Ergebnisse erzeugt werden. """ import json from pathlib import Path import pytest from scripts import estimate_from_dir DATA = Path(__file__).parent / "data" SCENE_9A = DATA / "Scene9a" ROBOT_JSON = DATA / "robot.json" REFERENCE_POSE = SCENE_9A / "pose.json" def _normalize_angle(angle: float) -> float: """Normalisiere Winkel auf [-180, 180].""" while angle > 180: angle -= 360 while angle <= -180: angle += 360 return angle def _angle_distance(a1: float, a2: float) -> float: """Berechne die kürzeste Winkel-Differenz in Grad.""" a1_norm = _normalize_angle(a1) a2_norm = _normalize_angle(a2) diff = abs(a1_norm - a2_norm) if diff > 180: diff = 360 - diff return diff @pytest.mark.skipif( not SCENE_9A.exists(), reason="Scene9a Testdaten nicht vorhanden" ) def test_estimate_from_scene9a(): """ E2E-Test: Scene9a-Bilder mit allen Kameras a-g, dazugehörige Intrinsiken und robot.json auswerten. Validiert, dass: - Pipeline ohne Fehler läuft - Alle 7 Gelenke Werte erhalten - Confidence-Werte für alle Gelenke vorhanden - Plausible numerische Ergebnisse erzeugt werden - Ergebnisse mit Referenzhaltung übereinstimmen (±5mm, ±5°) """ result = estimate_from_dir( SCENE_9A, robot_json=ROBOT_JSON, ) # [*] Basis-Validierungen assert result is not None assert isinstance(result.joints, dict) assert isinstance(result.confidence, dict) # [*] Gelenke prüfen expected_joints = {"x", "y", "z", "a", "b", "c", "e"} assert set(result.joints.keys()) == expected_joints, \ f"Erwartet {expected_joints}, aber erhalten {set(result.joints.keys())}" # [*] Alle Werte sollten numerisch sein und nicht NaN for joint, value in result.joints.items(): assert isinstance(value, (int, float)), \ f"Gelenk {joint} sollte numerisch sein, ist aber {type(value)}" assert not (isinstance(value, float) and value != value), \ f"Gelenk {joint} ist NaN" # [*] Confidence-Werte prüfen confidence_levels = {"high", "medium", "low", "none"} for joint, conf in result.confidence.items(): assert conf in confidence_levels, \ f"Gelenk {joint} hat ungültiges Confidence {conf}" # [*] Marker und Residuum assert result.n_markers > 0, "Sollte mindestens einen Marker haben" assert result.residual_rms >= 0, "RMS-Residuum sollte nicht negativ sein" assert result.processing_ms > 0, "Verarbeitung sollte Zeit brauchen" # [*] Fehlerbehandlung assert isinstance(result.errors, list) # [*] Vergleich mit Referenzwerten aus pose.json if REFERENCE_POSE.exists(): ref_data = json.loads(REFERENCE_POSE.read_text()) ref_position = ref_data.get("position", {}) # Toleranzen: ±2mm für Linear, ±2° für Rotation LINEAR_TOLERANCE = 2.0 # mm ANGULAR_TOLERANCE = 2.0 # Grad for joint in expected_joints: estimated = result.joints[joint] reference = ref_position.get(joint) if reference is not None: if joint in {"x", "y", "z"}: # Linear-Achsen: Toleranz ±5mm diff = abs(estimated - reference) assert diff <= LINEAR_TOLERANCE, \ f"Gelenk {joint}: Abweichung {diff:.2f}mm > {LINEAR_TOLERANCE}mm " \ f"(Referenz: {reference}, Gemessen: {estimated:.2f})" else: # Rotations-Achsen: Toleranz ±5° angle_diff = _angle_distance(estimated, reference) assert angle_diff <= ANGULAR_TOLERANCE, \ f"Gelenk {joint}: Abweichung {angle_diff:.2f}° > {ANGULAR_TOLERANCE}° " \ f"(Referenz: {reference}°, Gemessen: {estimated:.2f}°)" # [OK] Ausgeben zur manuellen Validierung print(f"\n[OK] Scene9a erfolgreich verarbeitet") print(f" Gelenke: {result.joints}") print(f" Confidence: {result.confidence}") print(f" Marker: {result.n_markers}, RMS: {result.residual_rms:.3f} mm") print(f" Zeit: {result.processing_ms} ms")