[TIZI/TICI] ui: standstill timer (#1677)

* standstill timer

* final
This commit is contained in:
Jason Wen
2026-02-08 19:40:04 -05:00
committed by GitHub
parent 020f503364
commit a9229e11a0
3 changed files with 55 additions and 13 deletions

View File

@@ -14,7 +14,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE
from openpilot.system.ui.lib.text_measure import measure_text_cached
class E2eAlertsRenderer:
class CircularAlertsRenderer:
def __init__(self):
self._green_light_alert_img = gui_app.texture("../../sunnypilot/selfdrive/assets/images/green_light.png", 250, 250)
self._lead_depart_alert_img = gui_app.texture("../../sunnypilot/selfdrive/assets/images/lead_depart.png", 250, 250)
@@ -23,6 +23,9 @@ class E2eAlertsRenderer:
self._e2e_alert_frame = 0
self._green_light_alert = False
self._lead_depart_alert = False
self._standstill_timer = False
self._standstill_elapsed_time = 0.0
self._is_standstill = False
self._alert_text = ""
self._alert_img = None
self._allow_e2e_alerts = False
@@ -30,14 +33,22 @@ class E2eAlertsRenderer:
def update(self) -> None:
sm = ui_state.sm
lp_sp = sm['longitudinalPlanSP']
car_state = sm['carState']
self._green_light_alert = lp_sp.e2eAlerts.greenLightAlert
self._lead_depart_alert = lp_sp.e2eAlerts.leadDepartAlert
self._standstill_timer = ui_state.standstill_timer
self._is_standstill = car_state.standstill
if not ui_state.started:
self._standstill_elapsed_time = 0.0
self._allow_e2e_alerts = sm['selfdriveState'].alertSize == log.SelfdriveState.AlertSize.none and \
sm.recv_frame['driverStateV2'] > ui_state.started_frame
if self._green_light_alert or self._lead_depart_alert:
self._e2e_alert_display_timer = 3 * gui_app.target_fps
# reset onroad sleep timer for e2e alerts
ui_state.reset_onroad_sleep_timer()
if self._e2e_alert_display_timer > 0:
self._e2e_alert_frame += 1
@@ -49,11 +60,22 @@ class E2eAlertsRenderer:
elif self._lead_depart_alert:
self._alert_text = "LEAD VEHICLE\nDEPARTING"
self._alert_img = self._lead_depart_alert_img
elif self._standstill_timer and self._is_standstill:
self._alert_img = None
self._standstill_elapsed_time += 1.0 / gui_app.target_fps
minute = int(self._standstill_elapsed_time / 60)
second = int(self._standstill_elapsed_time - (minute * 60))
self._alert_text = f"{minute:01d}:{second:02d}"
self._e2e_alert_frame += 1
else:
self._e2e_alert_frame = 0
if not self._is_standstill:
self._standstill_elapsed_time = 0.0
def render(self, rect: rl.Rectangle) -> None:
if not self._allow_e2e_alerts or self._e2e_alert_display_timer <= 0:
if not self._allow_e2e_alerts or (self._e2e_alert_display_timer <= 0 and not (self._standstill_timer and self._is_standstill)):
return
e2e_alert_size = 250
@@ -67,7 +89,12 @@ class E2eAlertsRenderer:
# Pulse logic
is_pulsing = (self._e2e_alert_frame % gui_app.target_fps) < (gui_app.target_fps / 2.5)
frame_color = rl.Color(255, 255, 255, 75) if is_pulsing else rl.Color(0, 255, 0, 75)
# Standstill Timer (STOPPED) should be static white
if self._e2e_alert_display_timer == 0 and self._standstill_timer and self._is_standstill:
frame_color = rl.Color(255, 255, 255, 75)
else:
frame_color = rl.Color(255, 255, 255, 75) if is_pulsing else rl.Color(0, 255, 0, 75)
# Draw Circle
rl.draw_circle_v(center, e2e_alert_size, rl.Color(0, 0, 0, 190))
@@ -75,7 +102,7 @@ class E2eAlertsRenderer:
rl.draw_ring(center, e2e_alert_size - 7.5, e2e_alert_size + 7.5, 0, 360, 0, frame_color)
# Draw Image
if self._alert_img:
if self._alert_img and self._e2e_alert_display_timer > 0:
img_x = int(center.x - self._alert_img.width / 2)
img_y = int(center.y - self._alert_img.height / 2)
rl.draw_texture(self._alert_img, img_x, img_y, rl.WHITE)
@@ -94,8 +121,22 @@ class E2eAlertsRenderer:
# Draw lines upwards from bottom
current_y = bottom_y - (len(lines) * text_size * FONT_SCALE)
for line in lines:
measure = measure_text_cached(font, line, text_size, spacing)
line_x = center.x - measure.x / 2
rl.draw_text_ex(font, line, rl.Vector2(line_x, current_y), text_size, spacing, txt_color)
current_y += text_size * FONT_SCALE
if self._e2e_alert_display_timer == 0 and self._standstill_timer and self._is_standstill:
# Standstill Timer Text
alert_alt_text = "STOPPED"
top_text_size = 80
measure_top = measure_text_cached(font, alert_alt_text, top_text_size, spacing)
top_y = alert_rect.y + alert_rect.height / 3.5
rl.draw_text_ex(font, alert_alt_text, rl.Vector2(center.x - measure_top.x / 2, top_y), top_text_size, spacing, rl.Color(255, 175, 3, 240))
# Timer
timer_text_size = 100
measure_timer = measure_text_cached(font, self._alert_text, timer_text_size, spacing)
timer_y = (alert_rect.y + alert_rect.height) - (alert_rect.height / 5) - measure_timer.y
rl.draw_text_ex(font, self._alert_text, rl.Vector2(center.x - measure_timer.x / 2, timer_y), timer_text_size, spacing, rl.WHITE)
else:
for line in lines:
measure = measure_text_cached(font, line, text_size, spacing)
line_x = center.x - measure.x / 2
rl.draw_text_ex(font, line, rl.Vector2(line_x, current_y), text_size, spacing, txt_color)
current_y += text_size * FONT_SCALE

View File

@@ -15,7 +15,7 @@ from openpilot.selfdrive.ui.sunnypilot.onroad.rocket_fuel import RocketFuel
from openpilot.selfdrive.ui.sunnypilot.onroad.speed_limit import SpeedLimitRenderer
from openpilot.selfdrive.ui.sunnypilot.onroad.smart_cruise_control import SmartCruiseControlRenderer
from openpilot.selfdrive.ui.sunnypilot.onroad.turn_signal import TurnSignalController
from openpilot.selfdrive.ui.sunnypilot.onroad.e2e_alerts import E2eAlertsRenderer
from openpilot.selfdrive.ui.sunnypilot.onroad.circular_alerts import CircularAlertsRenderer
class HudRendererSP(HudRenderer):
@@ -27,7 +27,7 @@ class HudRendererSP(HudRenderer):
self.speed_limit_renderer = SpeedLimitRenderer()
self.smart_cruise_control_renderer = SmartCruiseControlRenderer()
self.turn_signal_controller = TurnSignalController()
self.e2e_alerts_renderer = E2eAlertsRenderer()
self.circular_alerts_renderer = CircularAlertsRenderer()
self._torque_bar = TorqueBar(scale=3.0, always=True)
def _update_state(self) -> None:
@@ -36,7 +36,7 @@ class HudRendererSP(HudRenderer):
self.speed_limit_renderer.update()
self.smart_cruise_control_renderer.update()
self.turn_signal_controller.update()
self.e2e_alerts_renderer.update()
self.circular_alerts_renderer.update()
def _render(self, rect: rl.Rectangle) -> None:
super()._render(rect)
@@ -52,7 +52,7 @@ class HudRendererSP(HudRenderer):
self.speed_limit_renderer.render(rect)
self.smart_cruise_control_renderer.render(rect)
self.turn_signal_controller.render(rect)
self.e2e_alerts_renderer.render(rect)
self.circular_alerts_renderer.render(rect)
if ui_state.rocket_fuel:
self.rocket_fuel.render(rect, ui_state.sm)

View File

@@ -129,6 +129,7 @@ class UIStateSP:
self.active_bundle = self.params.get("ModelManager_ActiveBundle")
self.custom_interactive_timeout = self.params.get("InteractivityTimeout", return_default=True)
self.speed_limit_mode = self.params.get("SpeedLimitMode", return_default=True)
self.standstill_timer = self.params.get_bool("StandstillTimer")
# Onroad Screen Brightness
self.onroad_brightness = int(float(self.params.get("OnroadScreenOffBrightness", return_default=True)))