From fd1937c6d49e73328b503d4bc5d43d805cb216f4 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 28 Feb 2026 15:23:04 -0500 Subject: [PATCH] ui: Speed Limit Assist `preActive` improvements (#1743) * mici init * obv * hybrid * too soon junior * make them all flash the same pls * abstract * shorter * also too soon junior * not so fast --- selfdrive/ui/sunnypilot/onroad/speed_limit.py | 61 +++++++++++-------- .../selfdrive/assets/img_minus_arrow_down.png | 4 +- .../selfdrive/assets/img_plus_arrow_up.png | 4 +- sunnypilot/selfdrive/selfdrived/events.py | 18 +++--- 4 files changed, 51 insertions(+), 36 deletions(-) diff --git a/selfdrive/ui/sunnypilot/onroad/speed_limit.py b/selfdrive/ui/sunnypilot/onroad/speed_limit.py index 39a9848d37..962c96e17a 100644 --- a/selfdrive/ui/sunnypilot/onroad/speed_limit.py +++ b/selfdrive/ui/sunnypilot/onroad/speed_limit.py @@ -6,7 +6,6 @@ See the LICENSE.md file in the root directory for more details. """ from dataclasses import dataclass -import math import pyray as rl from cereal import custom @@ -41,9 +40,32 @@ class Colors: MUTCD_LINES = rl.Color(255, 255, 255, 100) -class SpeedLimitRenderer(Widget): +class SpeedLimitAlertRenderer: def __init__(self): - super().__init__() + arrow_size = 200 + self.arrow_up = gui_app.texture("../../sunnypilot/selfdrive/assets/img_plus_arrow_up.png", arrow_size, arrow_size) + self.arrow_down = gui_app.texture("../../sunnypilot/selfdrive/assets/img_minus_arrow_down.png", arrow_size, arrow_size) + + self._pre_active_alpha_filter = FirstOrderFilter(1.0, 0.05, 1 / gui_app.target_fps) + self._pre_active_alert_frame = 0 + + def update(self): + assist_state = ui_state.sm['longitudinalPlanSP'].speedLimit.assist.state + if assist_state == AssistState.preActive: + self._pre_active_alert_frame += 1 + if (self._pre_active_alert_frame % gui_app.target_fps) < (gui_app.target_fps * 0.75): + self._pre_active_alpha_filter.x = 1.0 + else: + self._pre_active_alpha_filter.update(0.0) + else: + self._pre_active_alert_frame = 0 + self._pre_active_alpha_filter.update(1.0) + + +class SpeedLimitRenderer(Widget, SpeedLimitAlertRenderer): + def __init__(self): + Widget.__init__(self) + SpeedLimitAlertRenderer.__init__(self) self.speed_limit = 0.0 self.speed_limit_last = 0.0 @@ -60,7 +82,6 @@ class SpeedLimitRenderer(Widget): self.speed_limit_ahead_valid = False self.speed_limit_ahead_frame = 0 - self.assist_frame = 0 self.is_cruise_set: bool = False self.is_cruise_available: bool = True self.set_speed: float = SET_SPEED_NA @@ -70,17 +91,13 @@ class SpeedLimitRenderer(Widget): self.font_bold = gui_app.font(FontWeight.BOLD) self.font_demi = gui_app.font(FontWeight.SEMI_BOLD) self.font_norm = gui_app.font(FontWeight.NORMAL) - self._sign_alpha_filter = FirstOrderFilter(1.0, 0.5, 1 / gui_app.target_fps) - - arrow_size = 90 - self._arrow_up = gui_app.texture("../../sunnypilot/selfdrive/assets/img_plus_arrow_up.png", arrow_size, arrow_size) - self._arrow_down = gui_app.texture("../../sunnypilot/selfdrive/assets/img_minus_arrow_down.png", arrow_size, arrow_size) @property def speed_conv(self): return CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH def update(self): + SpeedLimitAlertRenderer.update(self) sm = ui_state.sm if sm.recv_frame["carState"] < ui_state.started_frame: self.set_speed = SET_SPEED_NA @@ -143,13 +160,7 @@ class SpeedLimitRenderer(Widget): sign_rect = rl.Rectangle(x, y, width, UI_CONFIG.set_speed_height + 6 * 2) - if self.speed_limit_assist_state == AssistState.preActive: - self.assist_frame += 1 - pulse_value = 0.65 + 0.35 * math.sin(self.assist_frame * math.pi / gui_app.target_fps) - alpha = self._sign_alpha_filter.update(pulse_value) - else: - self.assist_frame = 0 - alpha = self._sign_alpha_filter.update(1.0) + alpha = self._pre_active_alpha_filter.x if ui_state.speed_limit_mode != SpeedLimitMode.off: self._draw_sign_main(sign_rect, alpha) @@ -184,19 +195,19 @@ class SpeedLimitRenderer(Widget): set_speed_rounded = round(self.set_speed) limit_rounded = round(self.speed_limit_final_last) - bounce_frequency = 2.0 * math.pi / (gui_app.target_fps * 2.5) - bounce_offset = int(20 * math.sin(self.assist_frame * bounce_frequency)) - sign_margin = 12 arrow_spacing = int(sign_margin * 1.4) arrow_x = sign_rect.x + sign_rect.width + arrow_spacing - if set_speed_rounded < limit_rounded: - arrow_y = sign_rect.y + (sign_rect.height - self._arrow_up.height) / 2 + bounce_offset - rl.draw_texture(self._arrow_up, int(arrow_x), int(arrow_y), rl.WHITE) - elif set_speed_rounded > limit_rounded: - arrow_y = sign_rect.y + (sign_rect.height - self._arrow_down.height) / 2 - bounce_offset - rl.draw_texture(self._arrow_down, int(arrow_x), int(arrow_y), rl.WHITE) + icon_alpha = max(0.0, min(self._pre_active_alpha_filter.x * 255.0, 255.0)) + if icon_alpha > 0: + color = rl.Color(255, 255, 255, int(icon_alpha)) + if set_speed_rounded < limit_rounded: + arrow_y = sign_rect.y + (sign_rect.height - self.arrow_up.height) / 2 + rl.draw_texture(self.arrow_up, int(arrow_x), int(arrow_y), color) + elif set_speed_rounded > limit_rounded: + arrow_y = sign_rect.y + (sign_rect.height - self.arrow_down.height) / 2 + rl.draw_texture(self.arrow_down, int(arrow_x), int(arrow_y), color) def _render_vienna(self, rect, val, sub, color, has_limit, alpha=1.0): center = rl.Vector2(rect.x + rect.width / 2, rect.y + rect.height / 2) diff --git a/sunnypilot/selfdrive/assets/img_minus_arrow_down.png b/sunnypilot/selfdrive/assets/img_minus_arrow_down.png index 250f640585..2dc99789a5 100644 --- a/sunnypilot/selfdrive/assets/img_minus_arrow_down.png +++ b/sunnypilot/selfdrive/assets/img_minus_arrow_down.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f4a0bf26cfd2c64939759d43bee839aadfd017a6f74065095a27b03615e55a4 -size 26627 +oid sha256:ad42ecaeff96a0a6c1a6db67b01e3c0452566906dd3a7f0336a1010ae854d27b +size 60924 diff --git a/sunnypilot/selfdrive/assets/img_plus_arrow_up.png b/sunnypilot/selfdrive/assets/img_plus_arrow_up.png index 14c1529da3..4247827340 100644 --- a/sunnypilot/selfdrive/assets/img_plus_arrow_up.png +++ b/sunnypilot/selfdrive/assets/img_plus_arrow_up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e451ff31e9b3f144822ab282b8e47cd5a603ca59ff94bd1849271181b86c6b1 -size 29220 +oid sha256:1fdea39873f60a0c4b216b75792e826f3633091a72ea2abd05740bd6e4e72089 +size 63058 diff --git a/sunnypilot/selfdrive/selfdrived/events.py b/sunnypilot/selfdrive/selfdrived/events.py index 4edc0bd470..d7507ab2e3 100644 --- a/sunnypilot/selfdrive/selfdrived/events.py +++ b/sunnypilot/selfdrive/selfdrived/events.py @@ -1,3 +1,9 @@ +""" +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 cereal.messaging as messaging from cereal import log, car, custom from openpilot.common.constants import CV @@ -34,7 +40,6 @@ def speed_limit_pre_active_alert(CP: car.CarParams, CS: car.CarState, sm: messag speed_limit_final_last = sm['longitudinalPlanSP'].speedLimit.resolver.speedLimitFinalLast speed_limit_final_last_conv = round(speed_limit_final_last * speed_conv) alert_1_str = "" - alert_2_str = "" alert_size = AlertSize.none if CP.openpilotLongitudinalControl and CP.pcmCruise: @@ -44,13 +49,12 @@ def speed_limit_pre_active_alert(CP: car.CarParams, CS: car.CarState, sm: messag pcm_long_required_max_set_speed_conv = round(pcm_long_required_max * speed_conv) speed_unit = "km/h" if metric else "mph" - alert_1_str = "Speed Limit Assist: Activation Required" - alert_2_str = f"Manually change set speed to {pcm_long_required_max_set_speed_conv} {speed_unit} to activate" - alert_size = AlertSize.mid + alert_1_str = f"Speed Limit Assist: set to {pcm_long_required_max_set_speed_conv} {speed_unit} to engage" + alert_size = AlertSize.small return Alert( alert_1_str, - alert_2_str, + "", AlertStatus.normal, alert_size, Priority.LOW, VisualAlert.none, AudibleAlertSP.promptSingleLow, .1) @@ -193,7 +197,7 @@ EVENTS_SP: dict[int, dict[str, Alert | AlertCallbackType]] = { EventNameSP.speedLimitActive: { ET.WARNING: Alert( - "Automatically adjusting to the posted speed limit", + "Auto adjusting to speed limit", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlertSP.promptSingleHigh, 5.), @@ -213,7 +217,7 @@ EVENTS_SP: dict[int, dict[str, Alert | AlertCallbackType]] = { EventNameSP.speedLimitPending: { ET.WARNING: Alert( - "Automatically adjusting to the last speed limit", + "Auto adjusting to last speed limit", "", AlertStatus.normal, AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlertSP.promptSingleHigh, 5.),