Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,4 @@ third_party/bootstrap/bootstrap-icons.ttf -filter -diff -merge

# BluePilot custom sounds: store directly in Git, not upstream GitLab LFS.
selfdrive/assets/sounds/bluepilot/*.wav -filter -diff -merge
selfdrive/assets/icons/liveDelay.png -filter -diff -merge
1 change: 1 addition & 0 deletions common/params_keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"BPRadRacerTheme", {PERSISTENT | BACKUP, BOOL, "0"}},
{"BPRainbowLines", {PERSISTENT | BACKUP, BOOL, "0"}},
{"BPShowConfidenceBall", {PERSISTENT | BACKUP, BOOL, "1"}},
{"BPShowLiveDelayIndicator", {PERSISTENT | BACKUP, BOOL, "1"}},
{"BPAnimateSteeringWheel", {PERSISTENT | BACKUP, BOOL, "1"}},
// BluePilot: No static defaults; the first active UI persists its matching device styles (C4=0, C3X=1).
{"BPSteeringWheelIconStyle", {PERSISTENT | BACKUP, INT}},
Expand Down
Binary file added selfdrive/assets/icons/liveDelay.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions selfdrive/ui/bp/layouts/settings/bluepilot.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def __init__(self):
("ShowBrakeStatus", self._show_brake_status),
("BPHideOnroadBorder", self._hide_onroad_border),
("BPShowConfidenceBall", self._show_confidence_ball),
("BPShowLiveDelayIndicator", self._show_live_delay),
("BPAnimateSteeringWheel", self._animate_steering_wheel),
("BPUseCustomSounds", self._use_custom_sounds),
("FordPrefShowRadarLeadOverlay", self._show_ford_radar_overlay),
Expand Down Expand Up @@ -202,6 +203,15 @@ def _initialize_items(self):
icon="warning.png"
)

# Live delay (steering lag calibration) indicator toggle
self._show_live_delay = toggle_item(
lambda: tr("Show Steering Lag Calibration"),
lambda: tr("Display the steering lag calibration icon onroad until the estimate is done."),
initial_state=self._safe_get_bool(self._params, "BPShowLiveDelayIndicator", True),
callback=lambda state: self._toggle_callback(state, "BPShowLiveDelayIndicator"),
icon="warning.png"
)

# Animate steering wheel toggle
self._animate_steering_wheel = toggle_item(
lambda: tr("Animate Steering Wheel"),
Expand Down Expand Up @@ -689,6 +699,7 @@ def _section(title: str, items: list) -> list:
self._show_blindspot,
self._show_brake_status,
self._show_confidence_ball,
self._show_live_delay,
self._animate_steering_wheel,
self._wheel_icon_style_btn,
self._dm_icon_style_btn,
Expand Down
62 changes: 62 additions & 0 deletions selfdrive/ui/bp/lib/live_delay_indicator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Onroad steering-lag calibration indicator (shared by TICI and MICI HUDs).

Shows the icon while liveDelay is not yet estimated; green once vEgo is above
MIN_VEGO, meaning samples are actually being collected. Hidden once estimated.
"""
import pyray as rl

from openpilot.common.params import Params
from openpilot.common.params_pyx import UnknownKeyName
from openpilot.selfdrive.locationd.lagd import MIN_VEGO
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.ui.lib.application import gui_app

ICON_ASPECT = 270 / 387 # liveDelay.png is 387x270

IDLE = rl.Color(255, 255, 255, 200) # too slow to collect samples
ACTIVE = rl.Color(60, 220, 120, 235) # above MIN_VEGO, estimating
BACKDROP = rl.Color(0, 0, 0, 65)
PAD = 10


class LiveDelayIndicator:
def __init__(self, width: int = 64):
self.width = width
self.height = round(width * ICON_ASPECT)
self._icon = gui_app.texture("icons/liveDelay.png", width, self.height)
self._params = Params()
self._param_counter = 0
self._enabled = self._get_enabled()

def _get_enabled(self) -> bool:
try:
return self._params.get_bool("BPShowLiveDelayIndicator")
except UnknownKeyName: # dev environment with reduced params
return True

def render(self, x: float, y: float) -> None:
self._param_counter += 1 # refresh the toggle ~1s at 60fps
if self._param_counter >= 60:
self._param_counter = 0
self._enabled = self._get_enabled()
if not self._enabled:
return

sm = ui_state.sm
if not sm.valid.get('liveDelay') or sm['liveDelay'].status == 'estimated':
return

backdrop = rl.Rectangle(x - PAD, y - PAD, self.width + PAD * 2, self.height + PAD * 2)
rl.draw_rectangle_rounded(backdrop, 0.15, 10, BACKDROP)
rl.draw_texture(self._icon, int(x), int(y), ACTIVE if sm['carState'].vEgo >= MIN_VEGO else IDLE)


def demo():
ind = LiveDelayIndicator(width=64)
assert ind.height == 45
assert MIN_VEGO > 0
print("ok")


if __name__ == "__main__":
demo()
6 changes: 3 additions & 3 deletions selfdrive/ui/bp/mici/layouts/settings/audio_mici.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ def __init__(self, back_callback: Callable[[], None] | None = None):
self.set_back_callback(back_callback)

self.use_custom_sounds = BigParamControlBP(
"Use Custom Engage/Disengage Sounds",
"use custom engage/disengage sounds",
"BPUseCustomSounds",
toggle_callback=self._on_custom_sounds_toggled,
)
self.custom_sound_selection = BigMultiParamToggleBP(
"Engage/Disengage Sound",
"engage/disengage sound",
"BPCustSoundsSelection",
["Comma 4", "Comma 3x", "Tesla"],
["comma 4", "comma 3x", "tesla"],
select_callback=self._on_sound_selection_changed,
)

Expand Down
4 changes: 2 additions & 2 deletions selfdrive/ui/bp/mici/layouts/settings/bluepilot.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self, back_callback: Callable[[], None]):
)
self.preferred_network_btn.set_click_callback(self._select_preferred_network)
self.show_web_routes_qr = BigButtonBP(
"QR code", "", "icons_mici/settings/network/wifi_strength_full.png", icon_size=80,
"qr code", "", "icons_mici/settings/network/wifi_strength_full.png", icon_size=80,
)
self.show_web_routes_qr.set_click_callback(self._show_qr_dialog)
self.clear_model_cache = BigButtonBP(
Expand All @@ -72,7 +72,7 @@ def __init__(self, back_callback: Callable[[], None]):

# Primary lateral control selector lives above the lat sub-panel
self.primary_lateral_control = BigMultiParamToggleBP(
"Primary Control Variable", "FordPrefLateralControl", ["curvature", "angle"],
"primary control variable", "FordPrefLateralControl", ["curvature", "angle"],
)

# Sub-panels
Expand Down
34 changes: 17 additions & 17 deletions selfdrive/ui/bp/mici/layouts/settings/lateral_mici.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,56 +17,56 @@ def __init__(self, back_callback: Callable[[], None] | None = None):

# --- Angle-mode-only items ---
self.low_speed_factor = BigParamFloatControl(
"Low Speed Adjustment Factor", "FordLowSpeedFactor_ang", min=0.5, max=1.5, step=0.01,
"low speed adjustment factor", "FordLowSpeedFactor_ang", min=0.5, max=1.5, step=0.01,
)
self.high_speed_factor = BigParamFloatControl(
"High Speed Adjustment Factor", "FordHighSpeedFactor_ang", min=0.5, max=1.5, step=0.01,
"high speed adjustment factor", "FordHighSpeedFactor_ang", min=0.5, max=1.5, step=0.01,
)
self.high_speed_dampening = BigParamFloatControl(
"High Speed Low Curve Adjustment Factor", "FordHighSpeedDampening_ang", min=0.75, max=1.25, step=0.01,
"high speed low curve adjustment factor", "FordHighSpeedDampening_ang", min=0.75, max=1.25, step=0.01,
)
self.lane_change_factor_high_ang = BigParamFloatControl(
"Lane Change Factor High", "lane_change_factor_high_ang", min=0.85, max=1.50,
"lane change factor high", "lane_change_factor_high_ang", min=0.85, max=1.50,
)

# --- Always-visible items ---
self.disable_BP_lat = BigParamControlBP("Disable BP Lateral Control", "disable_BP_lat_UI")
self.disable_BP_lat = BigParamControlBP("disable bp lateral control", "disable_BP_lat_UI")
self.disable_lane_change_under_speed = BigParamControlBP(
"Disable Auto Lane Change Under Speed", "BlinkerPauseLaneChange",
"disable auto lane change under speed", "BlinkerPauseLaneChange",
toggle_callback=lambda state: self.blinker_min_speed.set_enabled(state),
)
self.blinker_min_speed = BigParamIntControl(
"Minimum Speed to Pause Lane Change", "BlinkerMinLateralControlSpeed", min=5, max=50, step=5,
"minimum speed to pause lane change", "BlinkerMinLateralControlSpeed", min=5, max=50, step=5,
)
self.show_lateral_control = BigParamControlBP("Show Lateral Control Mode", "BpShowLateralControl")
self.show_lateral_control = BigParamControlBP("show lateral control mode", "BpShowLateralControl")

# --- Curvature-mode-only items ---
self.lane_change_factor_high_curv = BigParamFloatControl(
"Lane Change Factor High", "lane_change_factor_high_curv", min=0.5, max=1.0,
"lane change factor high", "lane_change_factor_high_curv", min=0.5, max=1.0,
)
self.custom_path_offset = BigParamFloatControl(
"In-Lane Offset", "custom_path_offset_curv", min=-0.5, max=0.5,
"in-lane offset", "custom_path_offset_curv", min=-0.5, max=0.5,
)
self.enable_human_turn_detection = BigParamControlBP(
"Enable Human Turn Detection", "enable_human_turn_detection_curv",
"enable human turn detection", "enable_human_turn_detection_curv",
)
self.enable_lane_positioning = BigParamControlBP(
"Enable Lane Positioning", "enable_lane_positioning_curv",
"enable lane positioning", "enable_lane_positioning_curv",
)
self.enable_lane_full_mode = BigParamControlBP(
"Enable Lanefull Mode", "enable_lane_full_mode_curv",
"enable lanefull mode", "enable_lane_full_mode_curv",
)
self.custom_profile = BigParamControlBP(
"Use Custom Tuning Profile", "custom_profile_curv",
"use custom tuning profile", "custom_profile_curv",
)
self.pc_blend_ratio_high_C = BigParamFloatControl(
"Predicted Curvature Blend Ratio High", "pc_blend_ratio_high_C_UI_curv", min=0.0, max=1.0, step=0.05,
"predicted curvature blend ratio high", "pc_blend_ratio_high_C_UI_curv", min=0.0, max=1.0, step=0.05,
)
self.pc_blend_ratio_low_C = BigParamFloatControl(
"Predicted Curvature Blend Ratio Low", "pc_blend_ratio_low_C_UI_curv", min=0.0, max=1.0, step=0.05,
"predicted curvature blend ratio low", "pc_blend_ratio_low_C_UI_curv", min=0.0, max=1.0, step=0.05,
)
self.lc_pid_gain = BigParamFloatControl(
"Centering PID Gain", "LC_PID_gain_UI_curv", min=0.0, max=50.0, step=0.5,
"centering pid gain", "LC_PID_gain_UI_curv", min=0.0, max=50.0, step=0.5,
)

self._scroller.add_widgets([
Expand Down
6 changes: 3 additions & 3 deletions selfdrive/ui/bp/mici/layouts/settings/longitudinal_mici.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ def __init__(self, back_callback: Callable[[], None] | None = None):
if back_callback is not None:
self.set_back_callback(back_callback)

self.disable_BP_long = BigParamControlBP("Bypass BP Longitudinal Control", "disable_BP_long_UI")
self.disable_downhill_comp = BigParamControlBP("Disable Downhill Compensation", "disable_downhill_comp_UI")
self.disable_ford_radar = BigParamControlBP("Disable Ford Radar (Vision-Only Leads)", "disable_ford_radar_UI")
self.disable_BP_long = BigParamControlBP("bypass bp longitudinal control", "disable_BP_long_UI")
self.disable_downhill_comp = BigParamControlBP("disable downhill compensation", "disable_downhill_comp_UI")
self.disable_ford_radar = BigParamControlBP("disable ford radar (vision-only leads)", "disable_ford_radar_UI")

self._scroller.add_widgets([
self.disable_BP_long,
Expand Down
6 changes: 3 additions & 3 deletions selfdrive/ui/bp/mici/layouts/settings/vehicle_mici.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ def __init__(self, back_callback: Callable[[], None] | None = None):
self.set_back_callback(back_callback)
self._params = Params()

self.show_hands_free_ui = BigParamControlBP("Show BlueCruise UI on Cluster", "send_hands_free_cluster_msg")
self.show_hands_free_ui = BigParamControlBP("show bluecruise ui on cluster", "send_hands_free_cluster_msg")
# Init-time param (read once at car init, mirrored into panda safety); takes effect after restart.
# FORD_EDGE_MK2's pinion sensor only reports a relative angle -- the safety/control
# layers already no-op this toggle there, so grey it out too (see values_ext.py
# FORD_PINION_GEOMETRY_INDEX).
self.steer_angle_curvature = BigParamControlBP("Use Pinion Yaw Sensor", "FordPrefSteerAngleCurvature")
self.steer_angle_curvature = BigParamControlBP("use pinion yaw sensor", "FordPrefSteerAngleCurvature")
self.steer_angle_curvature.set_enabled(self._pinion_yaw_sensor_supported)
self.vbatt_pause_charging = BigParamFloatControl("12V Battery Limit", "vbatt_pause_charging", min=11.0, max=14.0, step=0.1)
self.vbatt_pause_charging = BigParamFloatControl("12v battery limit", "vbatt_pause_charging", min=11.0, max=14.0, step=0.1)

self._scroller.add_widgets([
self.show_hands_free_ui,
Expand Down
31 changes: 17 additions & 14 deletions selfdrive/ui/bp/mici/layouts/settings/visuals_mici.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,30 @@ def __init__(self, back_callback: Callable[[], None] | None = None):
self.set_back_callback(back_callback)

self.show_lead_vehicle = BigMultiParamToggleBP(
"Lower Right Display", "mici_complication",
"lower right display", "mici_complication",
["off", "lead car speed", "speed", "lead car distance", "time to lead car"],
)
self.rainbow_mode = BigParamControlBP("Rainbow Mode", "RainbowMode")
self.rad_racer_theme = BigParamControlBP("8-Bit Racer Theme", "BPRadRacerTheme")
self.hide_fade = BigParamControlBP("Hide Onroad Fade", "mici_hide_onroad_fade")
self.hide_border = BigParamControlBP("Hide Onroad Border", "BPHideOnroadBorder")
self.hide_camera_view = BigParamControlBP("Minimal Driving View", "BPHideCameraView")
self.rainbow_lane_lines = BigParamControlBP("Rainbow Lane Lines", "BPRainbowLines")
self.show_blindspot_ui = BigParamControlBP("Show Blindspot Overlay", "ShowBlindspotOverlay")
self.show_brake_status = BigParamControlBP("Show Brake Status", "ShowBrakeStatus")
self.animate_steering_wheel = BigParamControlBP("Animate Steering Wheel", "BPAnimateSteeringWheel")
self.rainbow_mode = BigParamControlBP("rainbow mode", "RainbowMode")
self.rad_racer_theme = BigParamControlBP("8-bit racer theme", "BPRadRacerTheme")
self.hide_fade = BigParamControlBP("hide onroad fade", "mici_hide_onroad_fade")
self.hide_border = BigParamControlBP("hide onroad border", "BPHideOnroadBorder")
self.hide_camera_view = BigParamControlBP("minimal driving view", "BPHideCameraView")
self.rainbow_lane_lines = BigParamControlBP("rainbow lane lines", "BPRainbowLines")
self.show_blindspot_ui = BigParamControlBP("show blindspot overlay", "ShowBlindspotOverlay")
self.show_brake_status = BigParamControlBP("show brake status", "ShowBrakeStatus")
self.show_live_delay = BigParamControlBP("show steering lag calibration", "BPShowLiveDelayIndicator")
self.animate_steering_wheel = BigParamControlBP("animate steering wheel", "BPAnimateSteeringWheel")
ensure_steering_wheel_icon_style_initialized(Params(), SteeringWheelIconStyle.COMMA_4)
self.wheel_icon_style = BigMultiParamToggleBP(
"Wheel Icon Style", "BPSteeringWheelIconStyle", ["Comma 4", "Comma 3x"],
"wheel icon style", "BPSteeringWheelIconStyle", ["comma 4", "comma 3x"],
)
ensure_dm_icon_style_initialized(Params(), DMIconStyle.COMMA_4)
self.dm_icon_style = BigMultiParamToggleBP(
"DM Icon Style", "BPDMStylingChoice", ["Comma 4", "Comma 3x"],
"dm icon style", "BPDMStylingChoice", ["comma 4", "comma 3x"],
)
self.show_hybrid_power_flow = BigParamControlBP("Show Hybrid Power Flow", "FordPrefHybridPowerFlow")
self.show_hybrid_power_flow = BigParamControlBP("show hybrid power flow", "FordPrefHybridPowerFlow")
self.hybrid_power_flow_style = BigMultiParamToggleBoolBP(
"Hybrid/EV Power Flow Style", "FordPrefHybridPowerFlowAlternate", ["flat", "round"],
"hybrid/ev power flow style", "FordPrefHybridPowerFlowAlternate", ["flat", "round"],
)

self._scroller.add_widgets([
Expand All @@ -64,6 +65,7 @@ def __init__(self, back_callback: Callable[[], None] | None = None):
self.rainbow_lane_lines,
self.show_blindspot_ui,
self.show_brake_status,
self.show_live_delay,
self.animate_steering_wheel,
self.wheel_icon_style,
self.dm_icon_style,
Expand All @@ -80,6 +82,7 @@ def __init__(self, back_callback: Callable[[], None] | None = None):
("BPRainbowLines", self.rainbow_lane_lines),
("ShowBlindspotOverlay", self.show_blindspot_ui),
("ShowBrakeStatus", self.show_brake_status),
("BPShowLiveDelayIndicator", self.show_live_delay),
("BPAnimateSteeringWheel", self.animate_steering_wheel),
("FordPrefHybridPowerFlow", self.show_hybrid_power_flow),
)
Expand Down
5 changes: 5 additions & 0 deletions selfdrive/ui/bp/mici/onroad/hud_renderer_bp.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
SteeringWheelIconStyle,
)
from openpilot.selfdrive.ui.bp.lib.ui_debug_logger import bp_ui_log
from openpilot.selfdrive.ui.bp.lib.live_delay_indicator import LiveDelayIndicator
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.lib.application import gui_app
from openpilot.bluepilot.ui.lib.bp_shaders import draw_shader_circle_gradient
Expand All @@ -27,6 +28,7 @@ def __init__(self):
# BluePilot: HudRenderer initializes upstream TorqueBar; replace it with ours.
self._torque_bar = TorqueBar()
self._bp_params = Params()
self._live_delay = LiveDelayIndicator(width=44)
self._brakes_on = False
self._power_flow = MiciPowerflowGauge()
self._txt_wheel_comma_3x = gui_app.texture("icons/chffr_wheel.png", self._txt_wheel.width, self._txt_wheel.height)
Expand Down Expand Up @@ -78,6 +80,9 @@ def _render(self, rect: rl.Rectangle) -> None:

self._draw_steering_wheel(rect)

# Steering-lag calibration status, top-right corner
self._live_delay.render(rect.x + rect.width - self._live_delay.width - 14, rect.y + 14)

def _draw_steering_wheel(self, rect: rl.Rectangle) -> None:
"""Override to add brake status coloring to wheel icon, powerflow gauge, and lateral control overlay."""
normal_wheel_txt = self._txt_wheel_comma_3x if self._wheel_icon_style == SteeringWheelIconStyle.COMMA_3X else self._txt_wheel
Expand Down
8 changes: 8 additions & 0 deletions selfdrive/ui/bp/onroad/hud_renderer_bp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from openpilot.selfdrive.ui.onroad.hud_renderer import UI_CONFIG, FONT_SIZES, COLORS
from openpilot.selfdrive.ui.sunnypilot.onroad.hud_renderer import HudRendererSP
from openpilot.selfdrive.ui.bp.onroad.exp_button_bp import ExpButtonBP
from openpilot.selfdrive.ui.bp.lib.live_delay_indicator import LiveDelayIndicator
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.selfdrive.ui.bp.lib.ui_debug_logger import bp_ui_log
Expand All @@ -29,6 +30,7 @@ def __init__(self):
# BluePilot: Restore the animated C3X wheel without modifying the upstream ExpButton.
self._exp_button = ExpButtonBP(UI_CONFIG.button_size, UI_CONFIG.wheel_icon_size)
self._bp_params = Params()
self._live_delay = LiveDelayIndicator(width=140)
self._brakes_on = False
self.speed_right = 0
self._gradient_rect = None # BluePilot: Full-width rect for header gradient
Expand Down Expand Up @@ -103,6 +105,12 @@ def _render(self, rect: rl.Rectangle) -> None:
UI_CONFIG.button_size,
)

# Steering-lag calibration status, left of the wheel button
self._live_delay.render(
button_x - self._live_delay.width - 24,
button_y + (UI_CONFIG.button_size - self._live_delay.height) / 2,
)

# SP additions (dev UI, road name, speed limit, SCC, turn signals, circular alerts, rocket fuel)
self.developer_ui.render(rect)
self.road_name_renderer.render(rect)
Expand Down
2 changes: 1 addition & 1 deletion selfdrive/ui/layouts/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,6 @@ def _refresh(self):
self._prev_alerts_present = alerts_present

def _get_version_text(self) -> str:
brand = "sunnypilot"
brand = "bluepilot"
description = self.params.get("UpdaterCurrentDescription")
return f"{brand} {description}" if description else brand
Loading