From 6a92bea868620de7630f5e4882a158df3b4054c8 Mon Sep 17 00:00:00 2001 From: thftgr Date: Thu, 18 Jun 2026 20:01:58 +0900 Subject: [PATCH 1/5] Fix cluster HUD label crash and USB mode selection --- selfdrive/carrot/cluster/README.md | 10 +++++-- .../carrot/cluster/cluster_usb_display.py | 30 ++++++++++++++++++- selfdrive/carrot/cluster/main.py | 5 +++- selfdrive/carrot/cluster_autorun.py | 4 +-- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/selfdrive/carrot/cluster/README.md b/selfdrive/carrot/cluster/README.md index e760c5327a..d5526341de 100644 --- a/selfdrive/carrot/cluster/README.md +++ b/selfdrive/carrot/cluster/README.md @@ -192,9 +192,10 @@ debug UI before navi data has arrived. When output is gated off, remain visible. The autorun watcher normalizes locale before this dim-only USB path too, so vendor USB initialization does not fail before the renderer is launched. -Manager autostart leaves realtime affinity off by default. If `CLUSTER_REALTIME=1` -is set, `cluster_autorun.py` uses `ClusterHudCoreMode=0` by default, which maps -to cores `1,2,3,4`; mode `1` maps to all initially allowed CPU cores. +Manager autostart sets `CLUSTER_REALTIME=1` by default unless the environment +already overrides it. With realtime enabled, `cluster_autorun.py` uses +`ClusterHudCoreMode=0` by default, which maps to cores `1,2,3,4`; mode `1` maps +to all initially allowed CPU cores. `ClusterHudPriority` controls the common openpilot realtime helper priority with range `1..99`, default `10`. Changing either param makes the running HUD exit so `cluster_autorun` can @@ -348,6 +349,9 @@ The planned path draws `longitudinalPlan.desiredDistance` as a magenta horizontal bar across the current lane width at the matching forward position. Changing `ClusterHud` to another supported mode or `0` makes the running HUD exit; cleanup sends TURZX brightness zero before releasing the USB device. +When autorun passes a HUD mode, USB open is pinned to that mode's TURZX PID +(`1 -> 0x0092`, `2 -> 0x0123`) so a second connected TURZX panel is not opened +by the vendor library's generic device scan. The bundled TURZX code includes only the Python vendor library. The openpilot device uses the system `libusb-1.0.so` through `pyusb`. diff --git a/selfdrive/carrot/cluster/cluster_usb_display.py b/selfdrive/carrot/cluster/cluster_usb_display.py index 2b79b00667..f074dccae0 100644 --- a/selfdrive/carrot/cluster/cluster_usb_display.py +++ b/selfdrive/carrot/cluster/cluster_usb_display.py @@ -101,11 +101,13 @@ def __init__( frame_drain_timeout_ms: int = 2, fast_frame_drain_attempts: int = 3, fast_frame_drain_timeout_ms: int = 2, + expected_product_id: int | None = None, ) -> None: self.brightness = int(clamp(brightness, 0, 100)) self.display_fps = int(clamp(display_fps, 0, 255)) self.jpeg_quality = int(clamp(jpeg_quality, 1, 95)) self.jpeg_encoder = jpeg_encoder + self.expected_product_id = expected_product_id self.fast_write = fast_write self.wait_for_frame_ack = wait_for_frame_ack self.frame_drain_attempts = max(0, int(frame_drain_attempts)) @@ -231,8 +233,34 @@ def set_brightness(self, brightness: int, *, force: bool = False) -> bool: return True return False + def _find_expected_usb_device(self) -> tuple[Any, int]: + if self.expected_product_id is None: + return self._find_usb_device() + + import usb.core # type: ignore + + dev = usb.core.find(idVendor=TURZX_USB_VENDOR_ID, idProduct=self.expected_product_id) + if dev is None: + raise ValueError(f"USB device not found for pid=0x{self.expected_product_id:04x}") + + try: + dev.set_configuration() + except usb.core.USBError as exc: + print("Warning: set_configuration() failed:", exc) + + if sys.platform.startswith("linux"): + try: + if dev.is_kernel_driver_active(0): + dev.detach_kernel_driver(0) + except usb.core.USBError as exc: + print("Warning: detach_kernel_driver failed:", exc) + + return dev, self.expected_product_id + def _connect_device(self) -> None: - self.dev, self.dev_pid = self._find_usb_device() + self.dev, self.dev_pid = self._find_expected_usb_device() + if self.dev_pid not in self._product_id: + raise RuntimeError(f"TURZX vendor library does not know pid=0x{self.dev_pid:04x}") self._cache_out_endpoint() portrait_width, portrait_height = self._product_id[self.dev_pid] self.landscape_width = portrait_height diff --git a/selfdrive/carrot/cluster/main.py b/selfdrive/carrot/cluster/main.py index 2113c2bf88..57f30f9575 100644 --- a/selfdrive/carrot/cluster/main.py +++ b/selfdrive/carrot/cluster/main.py @@ -65,7 +65,7 @@ from cluster_route_replay import RouteReplaySource from cluster_simulator import ClusterSimulator, RandomInputSource from cluster_system_monitor import ClusterProcessCoreUsageSampler -from cluster_usb_display import TuringUsbDisplay +from cluster_usb_display import TuringUsbDisplay, product_id_for_hud_mode from cluster_usb_pipeline import AsyncJpegUsbPipeline DEFAULT_FPS = 0.0 @@ -584,6 +584,9 @@ def request_stop(signum: int, _frame: object) -> None: frame_drain_timeout_ms=usb_frame_drain_timeout_ms, fast_frame_drain_attempts=usb_fast_drain_attempts, fast_frame_drain_timeout_ms=usb_fast_drain_timeout_ms, + expected_product_id=( + product_id_for_hud_mode(hud_mode_watch) if hud_mode_watch is not None else None + ), ) usb_display.set_profile_enabled(profile_render) profile_stage = time.perf_counter() diff --git a/selfdrive/carrot/cluster_autorun.py b/selfdrive/carrot/cluster_autorun.py index 79dafe4767..8c4aaa0abb 100644 --- a/selfdrive/carrot/cluster_autorun.py +++ b/selfdrive/carrot/cluster_autorun.py @@ -32,7 +32,7 @@ REALTIME_CORES_ENV = "CLUSTER_REALTIME_CORES" REALTIME_PRIORITY_ENV = "CLUSTER_REALTIME_PRIORITY" AUTORUN_DEFAULT_ENV = { - "CLUSTER_REALTIME": "0", + "CLUSTER_REALTIME": "1", } DEFAULT_REALTIME_CORES = [1, 2, 3, 4] DEFAULT_REALTIME_PRIORITY = 10 @@ -494,7 +494,7 @@ def _turn_off_supported_usb_device(expected_product_id: int, reason: str) -> boo if find_supported_usb_product(expected_product_id) is None: return False - display = TuringUsbDisplay(brightness=0, display_fps=0) + display = TuringUsbDisplay(brightness=0, display_fps=0, expected_product_id=expected_product_id) try: display.open() print( From 977b86019571d3562ab7456a8fb7dc408d78b845 Mon Sep 17 00:00:00 2001 From: thftgr Date: Thu, 18 Jun 2026 20:53:22 +0900 Subject: [PATCH 2/5] Handle cluster USB disconnect relaunch --- selfdrive/carrot/cluster/README.md | 3 + .../carrot/cluster/cluster_usb_display.py | 75 ++++++++++++++++--- selfdrive/carrot/cluster_autorun.py | 2 +- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/selfdrive/carrot/cluster/README.md b/selfdrive/carrot/cluster/README.md index d5526341de..f625fdc256 100644 --- a/selfdrive/carrot/cluster/README.md +++ b/selfdrive/carrot/cluster/README.md @@ -352,6 +352,9 @@ exit; cleanup sends TURZX brightness zero before releasing the USB device. When autorun passes a HUD mode, USB open is pinned to that mode's TURZX PID (`1 -> 0x0092`, `2 -> 0x0123`) so a second connected TURZX panel is not opened by the vendor library's generic device scan. +If frame or H264 chunk writes report that the USB device was disconnected, the +active HUD exits instead of trying to recover in-process, letting +`cluster_autorun` wait for the same PID and relaunch after replug. The bundled TURZX code includes only the Python vendor library. The openpilot device uses the system `libusb-1.0.so` through `pyusb`. diff --git a/selfdrive/carrot/cluster/cluster_usb_display.py b/selfdrive/carrot/cluster/cluster_usb_display.py index f074dccae0..870a2c9ed2 100644 --- a/selfdrive/carrot/cluster/cluster_usb_display.py +++ b/selfdrive/carrot/cluster/cluster_usb_display.py @@ -1,5 +1,6 @@ from __future__ import annotations +import errno import sys import os import threading @@ -27,6 +28,22 @@ USB_FRAME_TIMEOUT_MS = 2000 USB_COMMAND_GAP_S = 0.2 TURZX_BRIGHTNESS_COMMAND_MAX = 102 +USB_DISCONNECT_ERRNOS = { + errno.ENODEV, + errno.ENXIO, + errno.EIO, + getattr(errno, "ESHUTDOWN", 108), +} +USB_DISCONNECT_BACKEND_CODES = { + -4, # libusb: LIBUSB_ERROR_NO_DEVICE +} +USB_DISCONNECT_TEXT = ( + "no such device", + "device not found", + "disconnected", + "entity not found", + "device has been disconnected", +) CMD_GET_H264_CHUNK_SIZE = 17 CMD_PLAY_H264_CHUNK = 121 CMD_GET_STREAM_STATUS = 122 @@ -133,6 +150,7 @@ def __init__( self._ep_out = None self._ep_in = None self._dll_dir_handle = None + self._disconnected = False self._frame_error_count = 0 self._turbojpeg = None self._turbojpeg_unavailable = False @@ -205,10 +223,11 @@ def close(self) -> None: self._ep_in = None return - try: - self._send_brightness(0, "brightness-off") - except Exception as exc: - print(f"Warning: TURZX USB brightness-off command skipped during close: {exc}", flush=True) + if not self._disconnected: + try: + self._send_brightness(0, "brightness-off") + except Exception as exc: + print(f"Warning: TURZX USB brightness-off command skipped during close: {exc}", flush=True) try: import usb.util @@ -259,6 +278,7 @@ def _find_expected_usb_device(self) -> tuple[Any, int]: def _connect_device(self) -> None: self.dev, self.dev_pid = self._find_expected_usb_device() + self._disconnected = False if self.dev_pid not in self._product_id: raise RuntimeError(f"TURZX vendor library does not know pid=0x{self.dev_pid:04x}") self._cache_out_endpoint() @@ -380,6 +400,37 @@ def _reset_and_reconnect(self) -> None: time.sleep(1.5) self._connect_device() + @staticmethod + def _exception_indicates_disconnect(exc: BaseException) -> bool: + visited: set[int] = set() + current: BaseException | None = exc + while current is not None and id(current) not in visited: + visited.add(id(current)) + error_no = getattr(current, "errno", None) + if error_no in USB_DISCONNECT_ERRNOS: + return True + backend_error_code = getattr(current, "backend_error_code", None) + if backend_error_code in USB_DISCONNECT_BACKEND_CODES: + return True + text = str(current).lower() + if any(pattern in text for pattern in USB_DISCONNECT_TEXT): + return True + current = current.__cause__ or current.__context__ + return False + + def _mark_disconnected(self) -> None: + self.dev = None + self.dev_pid = None + self._ep_out = None + self._ep_in = None + self._disconnected = True + + def _raise_usb_error(self, error_message: str, exc: BaseException) -> None: + if self._exception_indicates_disconnect(exc): + self._mark_disconnected() + raise RuntimeError("TURZX USB display disconnected") from exc + raise RuntimeError(error_message) from exc + def _add_libusb_search_path(self) -> None: libusb = VENDOR_ROOT / "external" / "libusb-1.0" / "libusb-1.0.dll" if not libusb.exists(): @@ -457,7 +508,9 @@ def send_h264_chunk( if wait_for_ack: try: response = self._send_h264_chunk_ack(chunk, is_last=is_last) - except Exception: + except Exception as exc: + if self._exception_indicates_disconnect(exc): + raise if require_ack_response: raise self._h264_flow_control(target_queue_depth=2) @@ -614,7 +667,7 @@ def _write_payload_checked(self, payload: bytes, error_message: str, timeout_ms: self._profile_add("usb.write_checked.read_ack", profile_stage) return response except Exception as exc: - raise RuntimeError(error_message) from exc + self._raise_usb_error(error_message, exc) def _write_payload_no_ack(self, payload: bytes, error_message: str, timeout_ms: int) -> None: if self._ep_out is None: @@ -629,7 +682,7 @@ def _write_payload_no_ack(self, payload: bytes, error_message: str, timeout_ms: self._ep_out.write(payload, timeout_ms) self._profile_add("usb.write_no_ack.write", profile_stage) except Exception as exc: - raise RuntimeError(error_message) from exc + self._raise_usb_error(error_message, exc) def _build_frame_payload(self, command_id: int, frame: bytes) -> bytes: if self._build_command_packet_header is None or self._encrypt_command_packet is None: @@ -763,7 +816,7 @@ def _send_h264_chunk_ack(self, chunk: bytes, *, is_last: bool) -> bytes: self._profile_add("usb.h264.read_ack", profile_stage) return response except Exception as exc: - raise RuntimeError("TURZX USB H264 chunk timed out") from exc + self._raise_usb_error("TURZX USB H264 chunk timed out", exc) def _h264_flow_control(self, *, target_queue_depth: int, max_attempts: int = 8) -> None: for _ in range(max_attempts): @@ -807,7 +860,7 @@ def _send_h264_chunk_no_ack(self, chunk: bytes, *, is_last: bool, drain_input: b self._ep_out.write(payload, USB_FRAME_TIMEOUT_MS) self._profile_add("usb.h264_no_ack.write", profile_stage) except Exception as exc: - raise RuntimeError("TURZX USB H264 chunk write failed") from exc + self._raise_usb_error("TURZX USB H264 chunk write failed", exc) def _check_frame_response(self, response: bytes | None) -> None: if not response: @@ -815,6 +868,10 @@ def _check_frame_response(self, response: bytes | None) -> None: self._frame_error_count = 0 def _handle_frame_error(self, exc: Exception) -> None: + if self._exception_indicates_disconnect(exc): + self._mark_disconnected() + raise RuntimeError("TURZX USB display disconnected") from exc + self._frame_error_count += 1 print( f"USB frame upload failed " diff --git a/selfdrive/carrot/cluster_autorun.py b/selfdrive/carrot/cluster_autorun.py index 8c4aaa0abb..6a078bca81 100644 --- a/selfdrive/carrot/cluster_autorun.py +++ b/selfdrive/carrot/cluster_autorun.py @@ -25,7 +25,7 @@ IS_ONROAD_PARAM = "IsOnroad" RETRY_INTERVAL_S = 5.0 HUD_CHECK_INTERVAL_S = 5.0 -USB_FALLBACK_SCAN_INTERVAL_S = 60.0 +USB_FALLBACK_SCAN_INTERVAL_S = 5.0 USB_OFF_DIM_INTERVAL_S = 30.0 NETLINK_KOBJECT_UEVENT = 15 AUTORUN_FPS_ENV = "CLUSTER_AUTORUN_FPS" From b8e3bdb35c5184e1d3a34ecbd786453cc556f6a8 Mon Sep 17 00:00:00 2001 From: thftgr Date: Thu, 18 Jun 2026 21:04:07 +0900 Subject: [PATCH 3/5] Keep cluster autorun alive after USB failures --- selfdrive/carrot/cluster/README.md | 3 ++- selfdrive/carrot/cluster/main.py | 4 +++- selfdrive/carrot/cluster_autorun.py | 10 +++++++++- selfdrive/carrot/cluster_run.py | 4 ++-- system/manager/process.py | 3 +++ 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/selfdrive/carrot/cluster/README.md b/selfdrive/carrot/cluster/README.md index f625fdc256..ddb50a1f84 100644 --- a/selfdrive/carrot/cluster/README.md +++ b/selfdrive/carrot/cluster/README.md @@ -353,7 +353,8 @@ When autorun passes a HUD mode, USB open is pinned to that mode's TURZX PID (`1 -> 0x0092`, `2 -> 0x0123`) so a second connected TURZX panel is not opened by the vendor library's generic device scan. If frame or H264 chunk writes report that the USB device was disconnected, the -active HUD exits instead of trying to recover in-process, letting +active HUD exits instead of trying to recover in-process. Autorun calls the +launcher in non-exiting mode so the error returns to the watcher loop, letting `cluster_autorun` wait for the same PID and relaunch after replug. The bundled TURZX code includes only the Python vendor library. The openpilot diff --git a/selfdrive/carrot/cluster/main.py b/selfdrive/carrot/cluster/main.py index 57f30f9575..f1d27a2108 100644 --- a/selfdrive/carrot/cluster/main.py +++ b/selfdrive/carrot/cluster/main.py @@ -1670,7 +1670,7 @@ def parse_args() -> argparse.Namespace: return args -def main() -> None: +def main(*, exit_on_error: bool = True) -> None: args = parse_args() encoder_source = apply_cluster_encoder_param(args) if args.usb_async and args.usb_codec != "jpeg": @@ -1814,6 +1814,8 @@ def main() -> None: except KeyboardInterrupt: print("\nStopped.") except RuntimeError as exc: + if not exit_on_error: + raise raise SystemExit(f"Error: {exc}") from exc diff --git a/selfdrive/carrot/cluster_autorun.py b/selfdrive/carrot/cluster_autorun.py index 6a078bca81..c6f36dc8b5 100644 --- a/selfdrive/carrot/cluster_autorun.py +++ b/selfdrive/carrot/cluster_autorun.py @@ -289,6 +289,14 @@ def _cluster_args( def _run_cluster_once(hud_mode: int, encoder_mode: int, core_mode: int, priority: int) -> None: from selfdrive.carrot import cluster_run + def run_cluster_entry() -> None: + try: + cluster_run.main(exit_on_error=False) + except SystemExit as exc: + if exc.code in (None, 0): + return + raise RuntimeError(f"cluster_run exited with {exc.code}") from exc + previous_argv = sys.argv[:] try: sequence = _encoder_sequence(encoder_mode) @@ -304,7 +312,7 @@ def _run_cluster_once(hud_mode: int, encoder_mode: int, core_mode: int, priority previous_argv[0], *_cluster_args(hud_mode, encoder_mode, active_encoder_mode, core_mode, priority), ] - cluster_run.main() + run_cluster_entry() return except Exception: if encoder_mode != ENCODER_AUTO or index == len(sequence) - 1: diff --git a/selfdrive/carrot/cluster_run.py b/selfdrive/carrot/cluster_run.py index db56ee0d5c..9c36892756 100644 --- a/selfdrive/carrot/cluster_run.py +++ b/selfdrive/carrot/cluster_run.py @@ -99,7 +99,7 @@ def configure_cluster_realtime() -> None: print(f"[cluster_run] failed to configure realtime process: {exc}", flush=True) -def main() -> None: +def main(*, exit_on_error: bool = True) -> None: configure_cluster_locale() args = sys.argv[1:] if "--input" not in args: @@ -109,7 +109,7 @@ def main() -> None: configure_cluster_realtime() from main import main as cluster_main - cluster_main() + cluster_main(exit_on_error=exit_on_error) if __name__ == "__main__": diff --git a/system/manager/process.py b/system/manager/process.py index 91450896b1..2dca308beb 100644 --- a/system/manager/process.py +++ b/system/manager/process.py @@ -256,6 +256,9 @@ def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None running = [] for p in procs: + if p.proc is not None and p.proc.exitcode is not None: + p.stop(block=False) + if p.enabled and p.name not in not_run and p.should_run(started, params, CP): if p.restart_if_crash and p.proc is not None and not p.proc.is_alive(): cloudlog.error(f'Restarting {p.name} (exitcode {p.proc.exitcode})') From 2a02e0477a4bc0d0fa3ef5230de85fc70eac35e9 Mon Sep 17 00:00:00 2001 From: thftgr Date: Thu, 18 Jun 2026 21:21:56 +0900 Subject: [PATCH 4/5] Hide zero-position radar samples --- selfdrive/carrot/cluster/README.md | 2 + selfdrive/carrot/cluster/cluster_models.py | 7 +++ .../carrot/cluster/cluster_route_replay.py | 6 +++ selfdrive/carrot/cluster/cluster_scene.py | 45 ++++++++++++++++--- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/selfdrive/carrot/cluster/README.md b/selfdrive/carrot/cluster/README.md index ddb50a1f84..2569db2038 100644 --- a/selfdrive/carrot/cluster/README.md +++ b/selfdrive/carrot/cluster/README.md @@ -329,6 +329,8 @@ reflections merge cleanly. vehicles yellow, `radarState` front/SCC radar leads red, camera-sourced vehicle leads light blue, comma model leads dark blue, and ADAS corner detections from `0x162`/`0x1EA` green. +Radar samples whose distance and left/right offset are both zero are treated as +empty/default data and are not drawn as radar points or vehicle boxes. Radar-track vehicle classification rejects points outside model road edges, but does not require in-road points to sit near the road-edge line; center-lane points can classify as vehicles when probability/in-lane data or moving radar diff --git a/selfdrive/carrot/cluster/cluster_models.py b/selfdrive/carrot/cluster/cluster_models.py index cac0bbbec7..6f97f32d28 100644 --- a/selfdrive/carrot/cluster/cluster_models.py +++ b/selfdrive/carrot/cluster/cluster_models.py @@ -150,6 +150,13 @@ class RadarPoint: promotion_held: bool = False +RADAR_ZERO_POSITION_EPS_M = 1e-3 + + +def radar_position_is_zero(longitudinal_m: float, lateral_m: float) -> bool: + return abs(longitudinal_m) <= RADAR_ZERO_POSITION_EPS_M and abs(lateral_m) <= RADAR_ZERO_POSITION_EPS_M + + @dataclass(frozen=True, slots=True) class SimulatorInput: throttle: float = 0.0 diff --git a/selfdrive/carrot/cluster/cluster_route_replay.py b/selfdrive/carrot/cluster/cluster_route_replay.py index 5d114805d4..ba6837690b 100644 --- a/selfdrive/carrot/cluster/cluster_route_replay.py +++ b/selfdrive/carrot/cluster/cluster_route_replay.py @@ -40,6 +40,7 @@ ModelRiskPoint, RadarPoint, RouteOverlay, + radar_position_is_zero, ) from cluster_utils import clamp, smoothstep @@ -1456,6 +1457,8 @@ def _update_radar_state(self, radar_state: Any, event_t: float) -> None: continue # openpilot yRel is left-positive; this renderer uses right-positive x. lateral_m = -safe_float(lead, "yRel", 0.0) + if radar_position_is_zero(d_rel, lateral_m): + continue relative_speed_mps = safe_optional_float(lead, "vRel") lead_speed_mps = safe_optional_float(lead, "vLead") absolute_speed_kph = ( @@ -2994,6 +2997,8 @@ def live_track_to_radar_point(track: Any, index: int, ego_speed_kph: float) -> R return None y_rel = safe_float(track, "yRel", 0.0) lateral_m = renderer_lateral_from_openpilot_yrel(y_rel) + if radar_position_is_zero(d_rel, lateral_m): + return None if not -12.0 <= lateral_m <= 12.0: return None track_id = safe_optional_int(track, "trackId") @@ -3029,6 +3034,7 @@ def sorted_radar_points(points: Any) -> tuple[RadarPoint, ...]: for point in points if -12.0 <= point.lateral_m <= 12.0 and RADAR_MIN_LONGITUDINAL_M <= point.longitudinal_m <= RADAR_FRONT_MAX_LONGITUDINAL_M + and not radar_position_is_zero(point.longitudinal_m, point.lateral_m) ] filtered.sort(key=lambda point: (point.longitudinal_m, abs(point.lateral_m), point.label)) return tuple(filtered) diff --git a/selfdrive/carrot/cluster/cluster_scene.py b/selfdrive/carrot/cluster/cluster_scene.py index dfd2802e18..ae01c70cbe 100644 --- a/selfdrive/carrot/cluster/cluster_scene.py +++ b/selfdrive/carrot/cluster/cluster_scene.py @@ -41,7 +41,14 @@ VEHICLE_LENGTH_M, VEHICLE_WIDTH_M, ) -from cluster_models import ClusterUiState, DetectedVehicle, LaneMarking, ModelPathPoint, RadarPoint +from cluster_models import ( + ClusterUiState, + DetectedVehicle, + LaneMarking, + ModelPathPoint, + RadarPoint, + radar_position_is_zero, +) from cluster_utils import clamp, darken, lighten, smoothstep @@ -1636,9 +1643,35 @@ def path_metric_color(accel_mps2: float) -> Color: def radar_points_for_display(state: ClusterUiState) -> tuple[RadarPoint, ...]: + points = state.radar_points + if any(radar_position_is_zero(point.longitudinal_m, point.lateral_m) for point in points): + points = tuple( + point + for point in points + if not radar_position_is_zero(point.longitudinal_m, point.lateral_m) + ) if state.radar_display_mode == CLUSTER_RADAR_DISPLAY_DETAIL: - return state.radar_points - return merged_radar_points(state.radar_points, state) + return points + return merged_radar_points(points, state) + + +def detected_vehicle_is_zero_radar_sample(vehicle: DetectedVehicle) -> bool: + if not radar_position_is_zero(vehicle.longitudinal_m, vehicle.lateral_m): + return False + return ( + vehicle.source == "radarState" + or vehicle.source == "carState" + or vehicle.source in ("radarPoint", "liveTracks") + or vehicle.source.startswith("CAN 0x") + ) + + +def detected_vehicles_without_zero_radar_samples( + vehicles: tuple[DetectedVehicle, ...], +) -> tuple[DetectedVehicle, ...]: + if not any(detected_vehicle_is_zero_radar_sample(vehicle) for vehicle in vehicles): + return vehicles + return tuple(vehicle for vehicle in vehicles if not detected_vehicle_is_zero_radar_sample(vehicle)) def merged_radar_points(points: tuple[RadarPoint, ...], state: ClusterUiState) -> tuple[RadarPoint, ...]: @@ -1865,6 +1898,7 @@ def detected_vehicles_for_display( vehicles: tuple[DetectedVehicle, ...], state: ClusterUiState, ) -> tuple[DetectedVehicle, ...]: + vehicles = detected_vehicles_without_zero_radar_samples(vehicles) if state.radar_display_mode == CLUSTER_RADAR_DISPLAY_DETAIL or len(vehicles) < 2: return vehicles selected: list[DetectedVehicle] = [] @@ -3059,8 +3093,9 @@ def build_cluster_scene( profile_stage = profile_scene_start(profile_add) lane_width_m = max(2.4, min(4.6, state.lane_width_m or DEFAULT_LANE_WIDTH_M)) display_radar_points = radar_points_for_display(state) - if display_radar_points is not state.radar_points: - state = replace(state, radar_points=display_radar_points) + display_detected_vehicles = detected_vehicles_without_zero_radar_samples(state.detected_vehicles) + if display_radar_points is not state.radar_points or display_detected_vehicles != state.detected_vehicles: + state = replace(state, radar_points=display_radar_points, detected_vehicles=display_detected_vehicles) anchor_x_m = ego_anchor_x_m(state, lane_width_m) scene_shift_x_m = -anchor_x_m relative_scene_x_offset_m = -scene_shift_x_m From d19488c4ef9a0d4e5375ea03aa3c3d51866b0866 Mon Sep 17 00:00:00 2001 From: thftgr Date: Thu, 18 Jun 2026 21:43:59 +0900 Subject: [PATCH 5/5] Adjust minimum free disk space percentage to 30% --- system/loggerd/deleter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/loggerd/deleter.py b/system/loggerd/deleter.py index eb8fd35f21..5c943c5d26 100755 --- a/system/loggerd/deleter.py +++ b/system/loggerd/deleter.py @@ -9,7 +9,7 @@ from openpilot.system.loggerd.xattr_cache import getxattr MIN_BYTES = 5 * 1024 * 1024 * 1024 -MIN_PERCENT = 10 +MIN_PERCENT = 30 DELETE_LAST = ['boot', 'crash']