AURON: Veraltete Flaschentreffer bei fehlgeschlagener Erkennung verwerfen

This commit is contained in:
2026-10-17 10:57:08 +00:00
parent 8f3b47ff5c
commit ae222b441d
2 changed files with 101 additions and 1 deletions

View File

@@ -0,0 +1,97 @@
"""Regressionstests für Erkennungszustand ohne Hardware oder externe Pakete."""
import importlib.util
import math
from pathlib import Path
import sys
import types
import unittest
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
def load_config():
spec = importlib.util.spec_from_file_location("_detection_test_config", ROOT / "config.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def load_vision():
config = load_config()
cv2 = types.ModuleType("cv2")
numpy = types.ModuleType("numpy")
numpy.sqrt = math.sqrt
calibrator_module = types.ModuleType("calibrator")
class Calibrator:
is_calibrated = True
def pixel_to_robot(self, x, y):
return (float(x), float(y))
calibrator_module.CameraCalibrator = Calibrator
ui_module = types.ModuleType("user_interface")
ui_module.ModernUI = type("ModernUI", (), {})
ultra = types.ModuleType("ultralytics")
ultra.YOLO = lambda path: None
modules = {
"cv2": cv2, "numpy": numpy, "config": config,
"calibrator": calibrator_module, "user_interface": ui_module,
"ultralytics": ultra,
}
with patch.dict(sys.modules, modules):
spec = importlib.util.spec_from_file_location("_detection_test_vision", ROOT / "vision.py")
vision = importlib.util.module_from_spec(spec)
spec.loader.exec_module(vision)
return vision.BeerBotVision
class DetectionStateTests(unittest.TestCase):
def setUp(self):
self.BeerBotVision = load_vision()
self.bot = self.BeerBotVision()
def seed(self):
self.bot.detected_bottles = [{"bbox": (1, 2, 3, 4), "center_pixel": (2, 3),
"robot_coords": (2, 3), "confidence": 0.9}]
def test_missing_model_clears_cache_and_nearest(self):
self.seed()
self.bot.model = None
self.assertEqual(self.bot.detect_objects(), {"bottles": []})
self.assertEqual(self.bot.detected_bottles, [])
self.assertIsNone(self.bot.get_nearest_bottle())
def test_missing_capture_does_not_call_model_and_clears_cache(self):
self.seed()
called = []
self.bot.model = lambda *a, **k: called.append(True)
self.bot.capture_frame = lambda: None
self.assertEqual(self.bot.detect_objects(), {"bottles": []})
self.assertEqual(called, [])
self.assertEqual(self.bot.detected_bottles, [])
self.assertIsNone(self.bot.get_nearest_bottle())
def test_empty_success_clears_cache(self):
self.seed()
self.bot.model = lambda frame, **kwargs: [types.SimpleNamespace(boxes=[])]
result = self.bot.detect_objects(frame=object())
self.assertEqual(result["bottles"], [])
self.assertEqual(self.bot.detected_bottles, [])
def test_fresh_detection_replaces_cache_and_returns_frame(self):
self.seed()
frame = object()
box = types.SimpleNamespace(cls=[39], conf=[0.8], xyxy=[[10.2, 20.8, 30.9, 40.1]])
self.bot.model = lambda image, **kwargs: [types.SimpleNamespace(boxes=[box])]
result = self.bot.detect_objects(frame=frame)
expected = {"bbox": (10, 20, 30, 40), "center_pixel": (20, 30),
"robot_coords": (20.0, 30.0), "confidence": 0.8}
self.assertIs(result["frame"], frame)
self.assertEqual(result["bottles"], [expected])
self.assertEqual(self.bot.detected_bottles, [expected])
if __name__ == "__main__":
unittest.main()

View File

@@ -55,6 +55,10 @@ class BeerBotVision:
return frame if ret else None
def detect_objects(self, frame=None, visualize=False):
# Jeder Aufruf beschreibt einen neuen Erkennungsversuch; alte Treffer
# dürfen auch bei fehlendem Modell oder Frame nicht erhalten bleiben.
self.detected_bottles = []
if self.model is None:
return {"bottles": []}
@@ -64,7 +68,6 @@ class BeerBotVision:
return {"bottles": []}
results = self.model(frame, verbose=False)
self.detected_bottles = []
for result in results:
for box in result.boxes: