From d7770ad55ca67fc15f766e75b66e7e3946d8595e Mon Sep 17 00:00:00 2001 From: Kumar <36933347+rav4kumar@users.noreply.github.com> Date: Fri, 23 Jan 2026 23:36:47 -0700 Subject: [PATCH] [MICI] ui: display blindspot indicators when available (#1525) * always bsm * c4 bsm for c3x * position * sperate * sp dir * revert * decouple * final --------- Co-authored-by: Jason Wen --- .../ui/mici/onroad/augmented_road_view.py | 1 + selfdrive/ui/mici/onroad/hud_renderer.py | 4 +- .../ui/sunnypilot/mici/onroad/hud_renderer.py | 27 ++++++++++ .../onroad/blind_spot_indicators.py | 49 +++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 selfdrive/ui/sunnypilot/mici/onroad/hud_renderer.py create mode 100644 selfdrive/ui/sunnypilot/onroad/blind_spot_indicators.py diff --git a/selfdrive/ui/mici/onroad/augmented_road_view.py b/selfdrive/ui/mici/onroad/augmented_road_view.py index 0546920e53..316052fdc1 100644 --- a/selfdrive/ui/mici/onroad/augmented_road_view.py +++ b/selfdrive/ui/mici/onroad/augmented_road_view.py @@ -20,6 +20,7 @@ from openpilot.common.transformations.orientation import rot_from_euler from enum import IntEnum if gui_app.sunnypilot_ui(): + from openpilot.selfdrive.ui.sunnypilot.mici.onroad.hud_renderer import HudRendererSP as HudRenderer from openpilot.selfdrive.ui.sunnypilot.ui_state import OnroadTimerStatus OpState = log.SelfdriveState.OpenpilotState diff --git a/selfdrive/ui/mici/onroad/hud_renderer.py b/selfdrive/ui/mici/onroad/hud_renderer.py index 7f489ccf98..44ce12437a 100644 --- a/selfdrive/ui/mici/onroad/hud_renderer.py +++ b/selfdrive/ui/mici/onroad/hud_renderer.py @@ -183,11 +183,13 @@ class HudRenderer(Widget): def _draw_steering_wheel(self, rect: rl.Rectangle) -> None: wheel_txt = self._txt_wheel_critical if self._show_wheel_critical else self._txt_wheel + bsm_detected = self._has_blind_spot_detected() if gui_app.sunnypilot_ui() else False + if self._show_wheel_critical: self._wheel_alpha_filter.update(255) self._wheel_y_filter.update(0) else: - if ui_state.status == UIStatus.DISENGAGED: + if ui_state.status == UIStatus.DISENGAGED or bsm_detected: self._wheel_alpha_filter.update(0) self._wheel_y_filter.update(wheel_txt.height / 2) else: diff --git a/selfdrive/ui/sunnypilot/mici/onroad/hud_renderer.py b/selfdrive/ui/sunnypilot/mici/onroad/hud_renderer.py new file mode 100644 index 0000000000..de52bb4622 --- /dev/null +++ b/selfdrive/ui/sunnypilot/mici/onroad/hud_renderer.py @@ -0,0 +1,27 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +import pyray as rl + +from openpilot.selfdrive.ui.mici.onroad.hud_renderer import HudRenderer +from openpilot.selfdrive.ui.sunnypilot.onroad.blind_spot_indicators import BlindSpotIndicators + + +class HudRendererSP(HudRenderer): + def __init__(self): + super().__init__() + self.blind_spot_indicators = BlindSpotIndicators() + + def _update_state(self) -> None: + super()._update_state() + self.blind_spot_indicators.update() + + def _render(self, rect: rl.Rectangle) -> None: + super()._render(rect) + self.blind_spot_indicators.render(rect) + + def _has_blind_spot_detected(self) -> bool: + return self.blind_spot_indicators.detected diff --git a/selfdrive/ui/sunnypilot/onroad/blind_spot_indicators.py b/selfdrive/ui/sunnypilot/onroad/blind_spot_indicators.py new file mode 100644 index 0000000000..1087579fef --- /dev/null +++ b/selfdrive/ui/sunnypilot/onroad/blind_spot_indicators.py @@ -0,0 +1,49 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +import pyray as rl + +from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.system.ui.lib.application import gui_app +from openpilot.common.filter_simple import FirstOrderFilter + + +class BlindSpotIndicators: + def __init__(self): + self._txt_blind_spot_left: rl.Texture = gui_app.texture('icons_mici/onroad/blind_spot_left.png', 108, 128) + self._txt_blind_spot_right: rl.Texture = gui_app.texture('icons_mici/onroad/blind_spot_right.png', 108, 128) + + self._blind_spot_left_alpha_filter = FirstOrderFilter(0, 0.15, 1 / gui_app.target_fps) + self._blind_spot_right_alpha_filter = FirstOrderFilter(0, 0.15, 1 / gui_app.target_fps) + + def update(self) -> None: + sm = ui_state.sm + CS = sm['carState'] + + self._blind_spot_left_alpha_filter.update(1.0 if CS.leftBlindspot else 0.0) + self._blind_spot_right_alpha_filter.update(1.0 if CS.rightBlindspot else 0.0) + + @property + def detected(self) -> bool: + return self._blind_spot_left_alpha_filter.x > 0.01 or self._blind_spot_right_alpha_filter.x > 0.01 + + def render(self, rect: rl.Rectangle) -> None: + BLIND_SPOT_MARGIN_X = 20 # Distance from edge of screen + BLIND_SPOT_Y_OFFSET = 100 # Distance from top of screen + + if self._blind_spot_left_alpha_filter.x > 0.01: + pos_x = int(rect.x + BLIND_SPOT_MARGIN_X) + pos_y = int(rect.y + BLIND_SPOT_Y_OFFSET) + alpha = int(255 * self._blind_spot_left_alpha_filter.x) + color = rl.Color(255, 255, 255, alpha) + rl.draw_texture(self._txt_blind_spot_left, pos_x, pos_y, color) + + if self._blind_spot_right_alpha_filter.x > 0.01: + pos_x = int(rect.x + rect.width - BLIND_SPOT_MARGIN_X - self._txt_blind_spot_right.width) + pos_y = int(rect.y + BLIND_SPOT_Y_OFFSET) + alpha = int(255 * self._blind_spot_right_alpha_filter.x) + color = rl.Color(255, 255, 255, alpha) + rl.draw_texture(self._txt_blind_spot_right, pos_x, pos_y, color)