Files
StarPilot/selfdrive/ui/onroad/driver_state.py
T
2026-07-10 05:04:37 -04:00

116 lines
4.7 KiB
Python

import math
import pyray as rl
from cereal import log
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.selfdrive.ui import UI_BORDER_SIZE
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.widgets import Widget
AlertSize = log.SelfdriveState.AlertSize
BTN_SIZE = 192
DMOJI_SIZE = 128
CONE_SIZE = round(52 / 60 * DMOJI_SIZE)
CONE_COLOR_GREEN = (0, 255, 64)
CONE_COLOR_ORANGE = (255, 115, 0)
AWARENESS_UNFULL_PERCENT = 95
class DriverStateRenderer(Widget):
def __init__(self):
super().__init__()
self.position_x: float = 0.0
self.position_y: float = 0.0
self.x_shift: float = 0.0
self.is_active = False
self.is_rhd = False
self.is_face_detected = False
self._awareness_unfull = False
self._fade_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps)
self._color_fade_filter = FirstOrderFilter(1.0, 0.05, 1 / gui_app.target_fps)
self._pitch_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False)
self._yaw_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False)
self._rotation_filter = FirstOrderFilter(0.0, 0.1, 1 / gui_app.target_fps, initialized=False)
self._dm_background = gui_app.texture("icons_mici/onroad/driver_monitoring/dm_background.png", DMOJI_SIZE, DMOJI_SIZE)
self._dm_person = gui_app.texture("icons_mici/onroad/driver_monitoring/dm_person.png", CONE_SIZE, CONE_SIZE)
self._dm_cone = gui_app.texture("icons_mici/onroad/driver_monitoring/dm_cone.png", CONE_SIZE, CONE_SIZE)
def _render(self, rect):
bg = self._dm_background
rl.draw_texture_ex(bg,
rl.Vector2(self.position_x - bg.width / 2,
self.position_y - bg.height / 2),
0.0, 1.0,
rl.Color(255, 255, 255, int(255 * self._fade_filter.x)))
person = self._dm_person
rl.draw_texture_ex(person,
rl.Vector2(self.position_x - person.width / 2,
self.position_y - person.height / 2),
0.0, 1.0,
rl.Color(255, 255, 255, int(255 * 0.9 * self._fade_filter.x)))
green_amount = self._color_fade_filter.update(0.0 if self._awareness_unfull else 1.0)
if self.is_active:
cone = self._dm_cone
source_rect = rl.Rectangle(0, 0, cone.width, cone.height)
dest_rect = rl.Rectangle(
self.position_x,
self.position_y,
cone.width,
cone.height,
)
r = int(round(CONE_COLOR_GREEN[0] * green_amount + CONE_COLOR_ORANGE[0] * (1 - green_amount)))
g = int(round(CONE_COLOR_GREEN[1] * green_amount + CONE_COLOR_ORANGE[1] * (1 - green_amount)))
b = int(round(CONE_COLOR_GREEN[2] * green_amount + CONE_COLOR_ORANGE[2] * (1 - green_amount)))
rl.draw_texture_pro(
cone,
source_rect,
dest_rect,
rl.Vector2(dest_rect.width / 2, dest_rect.height / 2),
self._rotation_filter.x - 90,
rl.Color(r, g, b, int(255 * self._fade_filter.x)),
)
def _update_state(self):
sm = ui_state.sm
dm_state = sm["driverMonitoringState"]
self.is_active = dm_state.activePolicy == log.DriverMonitoringState.MonitoringPolicy.vision
self.is_rhd = dm_state.isRHD
self.is_face_detected = dm_state.visionPolicyState.faceDetected
self._awareness_unfull = self.is_active and dm_state.visionPolicyState.awarenessPercent < AWARENESS_UNFULL_PERCENT
face_pitch = dm_state.visionPolicyState.pose.pitch + math.radians(6)
face_yaw = -dm_state.visionPolicyState.pose.yaw
pitch = self._pitch_filter.update(face_pitch)
yaw = self._yaw_filter.update(face_yaw)
rotation = math.degrees(math.atan2(pitch * 2, yaw))
angle_diff = rotation - self._rotation_filter.x
angle_diff = ((angle_diff + 180) % 360) - 180
self._rotation_filter.update(self._rotation_filter.x + angle_diff)
alert_ok = sm["selfdriveState"].alertSize == AlertSize.none
data_fresh = sm.recv_frame["driverStateV2"] > ui_state.started_frame
not_hidden = not ui_state.starpilot_toggles.get("hide_dm_icon", False)
should_draw = alert_ok and data_fresh and not_hidden
if not should_draw:
self._fade_filter.update(0.0)
elif not self.is_active:
self._fade_filter.update(0.35)
else:
self._fade_filter.update(1.0)
self._pre_calculate_position()
def _pre_calculate_position(self):
width, height = self._rect.width, self._rect.height
offset = UI_BORDER_SIZE + BTN_SIZE // 2 + 20
self.position_x = self._rect.x + (width - offset if self.is_rhd else offset) + self.x_shift
self.position_y = self._rect.y + height - offset