From 76c2aba14e4bdcdeb4828fcdcb7703c68e899b90 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Mon, 26 Oct 2026 13:36:59 -0500 Subject: [PATCH] Keep Lane Width Calculation Out of NumPy Cold Imports --- openpilot/starpilot/lateral/lane_tracking.py | 10 ++++--- .../lateral/tests/test_lane_centering.py | 27 +++++++++++++++++++ prebuilt.json | 2 +- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/openpilot/starpilot/lateral/lane_tracking.py b/openpilot/starpilot/lateral/lane_tracking.py index e253a46467..eb8176005a 100644 --- a/openpilot/starpilot/lateral/lane_tracking.py +++ b/openpilot/starpilot/lateral/lane_tracking.py @@ -93,7 +93,9 @@ class SingleLineTracker: raise ValueError slope = sum(float(np.interp(distance, x, np.gradient(y, x))) for x, y in valid_lines) * 0.5 widths.append((right - left) / math.hypot(1.0, slope)) - width = float(np.median(widths)) + if not all(math.isfinite(value) for value in widths): + raise ValueError + width = sorted(widths)[1] if not 2.6 <= width <= 4.8: raise ValueError self.width, self.width_at, self.width_travel = width, self.clock, self.travel @@ -137,7 +139,7 @@ class SingleLineTracker: self._clear_line() self.side = side self.acquired_at = self.clock - self.pending = max(self.MIN_CLEARANCE, float(np.median(distances))) + self.pending = max(self.MIN_CLEARANCE, sorted(distances)[1]) if ( not jump and self.width is not None @@ -147,9 +149,9 @@ class SingleLineTracker: self.spacing = self.width * 0.5 elif self.spacing is None and self.clock - self.acquired_at >= self.ACQUIRE_SECONDS: assert self.pending is not None - if abs(float(np.median(distances)) - self.pending) > 0.25: + if abs(sorted(distances)[1] - self.pending) > 0.25: self.acquired_at = self.clock - self.pending = max(self.MIN_CLEARANCE, float(np.median(distances))) + self.pending = max(self.MIN_CLEARANCE, sorted(distances)[1]) else: self.spacing = self.pending self.last_good_at, self.last_near, self.last_slope = self.clock, near, slope diff --git a/openpilot/starpilot/lateral/tests/test_lane_centering.py b/openpilot/starpilot/lateral/tests/test_lane_centering.py index f741b19eb5..666a6c310b 100644 --- a/openpilot/starpilot/lateral/tests/test_lane_centering.py +++ b/openpilot/starpilot/lateral/tests/test_lane_centering.py @@ -49,6 +49,33 @@ def settled(controller, source, ticks=300): class TestLaneCentering(unittest.TestCase): + def test_lane_width_avoids_first_use_numpy_median_import(self): + from openpilot.starpilot.lateral.lane_tracking import SingleLineTracker + tracker = SingleLineTracker() + with patch.object(np, 'median', side_effect=AssertionError('No lazy masked-array import in Controls')): + result = tracker.target(model(), 1, 10.0, 0.01, 0.0) + self.assertFalse(result.blocked) + assert tracker.width is not None + self.assertAlmostEqual(tracker.width, 3.6, places=5) + + def test_single_line_spacing_avoids_first_use_numpy_median_import(self): + from openpilot.starpilot.lateral.lane_tracking import SingleLineTracker + tracker = SingleLineTracker() + with patch.object(np, 'median', side_effect=AssertionError('No lazy masked-array import in Controls')): + for frame in range(8): + tracker.target(single(line=-1.7, edge=10.), frame, 20., .05, 0.) + assert tracker.spacing is not None + self.assertAlmostEqual(tracker.spacing, 1.7, places=5) + + def test_lane_width_rejects_nonfinite_gradient_before_median(self): + from openpilot.starpilot.lateral.lane_tracking import SingleLineTracker + for value in (float('nan'), float('inf'), float('-inf')): + tracker = SingleLineTracker() + with patch.object(np, 'gradient', return_value=np.full(len(XS), value)): + result = tracker.target(model(), 1, 10.0, 0.01, 0.0) + self.assertTrue(result.blocked) + self.assertIsNone(tracker.width) + def test_reader_cache_detects_mutation_samples_and_reset(self): from openpilot.starpilot.lateral import lane_centering as module builder = model() diff --git a/prebuilt.json b/prebuilt.json index a6e47740a9..f923d7c2d8 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "efd11c6fa09ef1c09fd6c394aec7b482b63101c1009824054635ef9bac18cf4b", "schema_version": 1} +{"protected_inventory_sha256": "7517fc683e66896ce2ee8d4f60c770b54ff6109d4bfaa9f7edd8ef0db6107134", "schema_version": 1}