feat: speed limit ding sound when cruise warning sign appears

Plays a ding via soundd when the cruise warning sign becomes visible
(cruise set speed out of range vs speed limit) or when the speed limit
changes while the warning sign is already showing. Max 1 ding per 30s.

Ding is mixed independently into soundd output at max volume without
interrupting alert sounds. bench_cmd ding available for manual trigger.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-15 03:49:02 +00:00
parent 6e7117b177
commit adcffad276
6 changed files with 76 additions and 14 deletions

View File

@@ -9,6 +9,7 @@ Usage:
python3 -m selfdrive.clearpilot.bench_cmd cruise 55
python3 -m selfdrive.clearpilot.bench_cmd cruiseactive 0|1|2 (0=disabled, 1=active, 2=paused)
python3 -m selfdrive.clearpilot.bench_cmd engaged 1
python3 -m selfdrive.clearpilot.bench_cmd ding (trigger speed limit ding sound)
python3 -m selfdrive.clearpilot.bench_cmd debugbutton (simulate LKAS debug button press)
python3 -m selfdrive.clearpilot.bench_cmd dump
python3 -m selfdrive.clearpilot.bench_cmd wait_ready
@@ -108,6 +109,10 @@ def main():
elif cmd == "wait_ready":
wait_ready()
elif cmd == "ding":
params.put("ClearpilotPlayDing", "1")
print("Ding triggered")
elif cmd == "debugbutton":
# Simulate LKAS debug button — same state machine as controlsd.clearpilot_state_control()
current = params.get_int("ScreenDisplayMode")

Binary file not shown.

View File

@@ -9,6 +9,7 @@ unit setting), detects speed limit changes, and writes results to params_memory
for the onroad UI to read.
"""
import math
import time
from openpilot.common.params import Params
from openpilot.common.conversions import Conversions as CV
@@ -18,20 +19,18 @@ class SpeedState:
self.params_memory = Params("/dev/shm/params")
self.prev_speed_limit = 0
# Ding state tracking
self.last_ding_time = 0.0
self.prev_warning = ""
self.prev_warning_speed_limit = 0
def update(self, speed_ms: float, has_speed: bool, speed_limit_ms: float, is_metric: bool,
cruise_speed_ms: float = 0.0, cruise_active: bool = False, cruise_standstill: bool = False):
"""
Convert raw m/s values to display-ready strings and write to params_memory.
Args:
speed_ms: current vehicle speed in m/s (from GPS or bench)
has_speed: whether we have a valid speed source
speed_limit_ms: current speed limit in m/s (from CAN or bench)
is_metric: True if car's CAN reports metric units (e.g. Canada)
cruise_speed_ms: cruise control set speed in m/s
cruise_active: True if cruise is engaged and not paused
cruise_standstill: True if cruise is paused at standstill
"""
now = time.monotonic()
if is_metric:
speed_display = speed_ms * CV.MS_TO_KPH
speed_limit_display = speed_limit_ms * CV.MS_TO_KPH
@@ -47,9 +46,7 @@ class SpeedState:
speed_limit_int = int(math.floor(speed_limit_display))
cruise_int = int(round(cruise_display))
# Detect speed limit changes (groundwork for future chime)
if speed_limit_int != self.prev_speed_limit:
self.prev_speed_limit = speed_limit_int
self.prev_speed_limit = speed_limit_int
# Write display-ready values to params_memory
self.params_memory.put("ClearpilotHasSpeed", "1" if has_speed and speed_int > 0 else "0")
@@ -59,7 +56,6 @@ class SpeedState:
self.params_memory.put("ClearpilotIsMetric", "1" if is_metric else "0")
# Cruise warning logic
# Only evaluate when speed limit >= 20 and cruise is active (not paused, not disabled)
warning = ""
warning_speed = ""
cruise_engaged = cruise_active and not cruise_standstill
@@ -75,3 +71,20 @@ class SpeedState:
self.params_memory.put("ClearpilotCruiseWarning", warning)
self.params_memory.put("ClearpilotCruiseWarningSpeed", warning_speed)
# Ding logic: play when warning sign appears or speed limit changes while visible
should_ding = False
if warning:
if not self.prev_warning:
# Warning sign just appeared
should_ding = True
elif speed_limit_int != self.prev_warning_speed_limit:
# Speed limit changed while warning sign is visible
should_ding = True
if should_ding and now - self.last_ding_time >= 30:
self.params_memory.put("ClearpilotPlayDing", "1")
self.last_ding_time = now
self.prev_warning = warning
self.prev_warning_speed_limit = speed_limit_int if warning else 0