mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-15 00:43:46 +08:00
200 lines
5.6 KiB
Python
200 lines
5.6 KiB
Python
"""Dom’s stock cruise button timing, with current physical readiness guards."""
|
||
|
||
import math
|
||
|
||
|
||
from opendbc.car.structs import car
|
||
|
||
|
||
from opendbc.car.subaru.software_cruise import selected
|
||
|
||
|
||
from opendbc.car import apply_hysteresis
|
||
|
||
|
||
from openpilot.common.constants import CV
|
||
|
||
|
||
from openpilot.common.realtime import DT_CTRL
|
||
|
||
|
||
ButtonType = car.CarState.ButtonEvent.Type
|
||
|
||
|
||
SEND_BUTTON_NONE = 0
|
||
|
||
|
||
SEND_BUTTON_INCREASE = 1
|
||
|
||
|
||
SEND_BUTTON_DECREASE = 2
|
||
|
||
|
||
HYST_GAP = 0.0
|
||
|
||
|
||
INCREASE_INACTIVE_TIMER = 0.12
|
||
|
||
|
||
DECREASE_INACTIVE_TIMER = 0.05
|
||
|
||
|
||
LEAD_INCREASE_INACTIVE_TIMER = 0.05
|
||
|
||
|
||
CRUISE_BUTTON_TIMERS = {
|
||
int(ButtonType.decelCruise): 0,
|
||
int(ButtonType.accelCruise): 0,
|
||
int(ButtonType.setCruise): 0,
|
||
int(ButtonType.resumeCruise): 0,
|
||
int(ButtonType.cancel): 0,
|
||
int(ButtonType.mainCruise): 0,
|
||
}
|
||
|
||
|
||
def get_minimum_set_speed(is_metric: bool) -> int:
|
||
return 30 if is_metric else 20
|
||
|
||
|
||
def update_manual_button_timers(CS: car.CarState, button_timers: dict[int, int]) -> None:
|
||
for button_type in button_timers:
|
||
if button_timers[button_type] > 0:
|
||
button_timers[button_type] += 1
|
||
|
||
for event in CS.buttonEvents:
|
||
button_type = event.type.raw if hasattr(event.type, "raw") else int(event.type)
|
||
if button_type in button_timers:
|
||
button_timers[button_type] = 1 if event.pressed else 0
|
||
|
||
|
||
class SoftwareCruise:
|
||
def __init__(self, CP):
|
||
self.CP = CP
|
||
|
||
self.v_target = 0
|
||
self.v_target_ms_last = 0.0
|
||
self.v_cruise_cluster = 0
|
||
self.v_cruise_min = 0
|
||
self.state = "inactive"
|
||
self.pre_active_timer = 0
|
||
self.is_ready = False
|
||
self.is_ready_prev = False
|
||
self.cruise_button_timers = dict(CRUISE_BUTTON_TIMERS)
|
||
|
||
@staticmethod
|
||
def _send_button_for_state(state: str) -> int:
|
||
if state == "increasing":
|
||
return SEND_BUTTON_INCREASE
|
||
if state == "decreasing":
|
||
return SEND_BUTTON_DECREASE
|
||
return SEND_BUTTON_NONE
|
||
|
||
def _reset(self) -> None:
|
||
self.state = "inactive"
|
||
self.pre_active_timer = 0
|
||
self.is_ready = False
|
||
self.is_ready_prev = False
|
||
|
||
def _update_calculations(self, CS: car.CarState, v_target_ms: float, is_metric: bool) -> None:
|
||
speed_conv = CV.MS_TO_KPH if is_metric else CV.MS_TO_MPH
|
||
ms_conv = CV.KPH_TO_MS if is_metric else CV.MPH_TO_MS
|
||
|
||
self.v_target_ms_last = apply_hysteresis(v_target_ms, self.v_target_ms_last, HYST_GAP * ms_conv)
|
||
self.v_target = round(self.v_target_ms_last * speed_conv)
|
||
self.v_cruise_min = get_minimum_set_speed(is_metric)
|
||
self.v_cruise_cluster = round(CS.cruiseState.speedCluster * speed_conv)
|
||
|
||
def _update_readiness(self, CS: car.CarState, CC: car.CarControl) -> None:
|
||
update_manual_button_timers(CS, self.cruise_button_timers)
|
||
button_pressed = any(timer > 0 for timer in self.cruise_button_timers.values())
|
||
stock_cruise_ready = (
|
||
CS.cruiseState.enabled
|
||
and CS.cruiseState.available
|
||
and CS.canValid
|
||
and not CS.canTimeout
|
||
and not CS.brakePressed
|
||
and not CS.gasPressed
|
||
and not CS.accFaulted
|
||
and not CS.steerFaultPermanent
|
||
and not CS.steerFaultTemporary
|
||
and CS.gearShifter == car.CarState.GearShifter.drive
|
||
and not CS.standstill
|
||
)
|
||
self.is_ready = (
|
||
CC.enabled and stock_cruise_ready and not CC.cruiseControl.override and not CC.cruiseControl.cancel and not CC.cruiseControl.resume and not button_pressed
|
||
)
|
||
|
||
def _desired_state(self) -> str:
|
||
if self.v_target > self.v_cruise_cluster:
|
||
return "increasing"
|
||
if self.v_target < self.v_cruise_cluster and self.v_cruise_cluster > self.v_cruise_min:
|
||
return "decreasing"
|
||
return "holding"
|
||
|
||
@staticmethod
|
||
def _get_pre_active_frames(state: str, lead_present: bool) -> int:
|
||
if state == "decreasing":
|
||
timer = DECREASE_INACTIVE_TIMER
|
||
elif lead_present:
|
||
timer = LEAD_INCREASE_INACTIVE_TIMER
|
||
else:
|
||
timer = INCREASE_INACTIVE_TIMER
|
||
return int(timer / DT_CTRL)
|
||
|
||
def _arm_pre_active(self, desired_state: str, lead_present: bool) -> None:
|
||
if desired_state == "holding":
|
||
self.state = "holding"
|
||
self.pre_active_timer = 0
|
||
return
|
||
|
||
self.state = "preActive"
|
||
self.pre_active_timer = self._get_pre_active_frames(desired_state, lead_present)
|
||
|
||
def _update_state_machine(self, lead_present: bool) -> int:
|
||
desired_state = self._desired_state()
|
||
|
||
if not self.is_ready:
|
||
self.state = "inactive"
|
||
self.pre_active_timer = 0
|
||
elif self.state == "inactive":
|
||
if not self.is_ready_prev:
|
||
self._arm_pre_active(desired_state, lead_present)
|
||
elif self.state == "preActive":
|
||
if desired_state == "holding":
|
||
self.state = "holding"
|
||
self.pre_active_timer = 0
|
||
else:
|
||
desired_frames = self._get_pre_active_frames(desired_state, lead_present)
|
||
self.pre_active_timer = max(0, min(self.pre_active_timer, desired_frames) - 1)
|
||
if self.pre_active_timer <= 0:
|
||
self.state = desired_state
|
||
elif self.state == "holding":
|
||
if desired_state != "holding":
|
||
self._arm_pre_active(desired_state, lead_present)
|
||
elif self.state != desired_state:
|
||
if desired_state == "holding":
|
||
self.state = "holding"
|
||
else:
|
||
self._arm_pre_active(desired_state, lead_present)
|
||
|
||
return self._send_button_for_state(self.state)
|
||
|
||
def run(
|
||
self,
|
||
CS: car.CarState,
|
||
CC: car.CarControl,
|
||
v_target_ms: float,
|
||
is_metric: bool,
|
||
lead_present: bool = False,
|
||
) -> tuple[int, int]:
|
||
if not selected(self.CP) or not math.isfinite(v_target_ms):
|
||
self._reset()
|
||
return SEND_BUTTON_NONE, 0
|
||
|
||
self._update_calculations(CS, v_target_ms, is_metric)
|
||
self._update_readiness(CS, CC)
|
||
send_button = self._update_state_machine(lead_present)
|
||
|
||
self.is_ready_prev = self.is_ready
|
||
return send_button, self.v_target
|