mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-06-28 10:02:06 +08:00
337 lines
12 KiB
Python
337 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
import time
|
|
|
|
from openpilot.common.constants import CV
|
|
|
|
from openpilot.starpilot.common.experimental_state import (
|
|
CCStatus,
|
|
is_manual_cc_status,
|
|
restore_persisted_cc_state,
|
|
)
|
|
|
|
|
|
class ConditionalChillMode:
|
|
CCM_STOP_MODEL_TIME = 7.0
|
|
CHILL_SPEED_ENTRY_CONFIRM_TIME = 0.35
|
|
CHILL_LEAD_ENTRY_CONFIRM_TIME = 1.0
|
|
CHILL_LAUNCH_ENTRY_CONFIRM_TIME = 0.0
|
|
CHILL_EXIT_BUFFER_TIME = 0.35
|
|
CHILL_MIN_DWELL_TIME = 1.2
|
|
|
|
CHILL_LAUNCH_EXIT_SPEED = 15 * CV.MPH_TO_MS
|
|
CHILL_LAUNCH_MAX_ENTRY_SPEED = 1.0
|
|
CHILL_LAUNCH_MAX_BRAKE = 0.2
|
|
CHILL_LAUNCH_MAX_CLOSING_SPEED = 0.75
|
|
CHILL_LAUNCH_MIN_LEAD_SPEED = 0.35
|
|
CHILL_LAUNCH_MIN_LEAD_DELTA = 0.2
|
|
CHILL_LAUNCH_MIN_LEAD_VREL = 0.2
|
|
CHILL_LAUNCH_MIN_LEAD_ACCEL = 0.08
|
|
|
|
STABLE_LEAD_MIN_MODEL_PROB = 0.9
|
|
STABLE_LEAD_MAX_BRAKE = 0.2
|
|
STABLE_LEAD_MIN_SPEED = 1.5
|
|
STABLE_LEAD_MAX_DISTANCE = 90.0
|
|
STABLE_LEAD_MAX_DISTANCE_TIME = 4.5
|
|
STABLE_LEAD_MAX_CLOSING_SPEED = 1.25
|
|
STABLE_LEAD_MAX_CLOSING_RATIO = 0.05
|
|
|
|
ADJACENT_LEAD_VETO_MIN_SPEED = 1.0
|
|
ADJACENT_LEAD_VETO_MAX_DISTANCE = 65.0
|
|
ADJACENT_LEAD_VETO_MAX_DISTANCE_TIME = 3.5
|
|
ADJACENT_LEAD_VETO_MAX_LATERAL_OFFSET = 5.5
|
|
|
|
LOW_SPEED_STOP_SCENE_MAX_SPEED = 18 * CV.MPH_TO_MS
|
|
|
|
def __init__(self, StarPilotPlanner, detector):
|
|
self.starpilot_planner = StarPilotPlanner
|
|
self.detector = detector
|
|
self.params = self.starpilot_planner.params
|
|
self.params_memory = self.starpilot_planner.params_memory
|
|
|
|
self.experimental_mode = True
|
|
self.status_value = CCStatus["OFF"]
|
|
self._active_auto_status = CCStatus["OFF"]
|
|
self._candidate_since = 0.0
|
|
self._soft_exit_since = 0.0
|
|
self._chill_hold_until = 0.0
|
|
self._prev_cc_status = None
|
|
self._launch_active = False
|
|
|
|
def update(self, v_ego, v_cruise, sm, starpilot_toggles):
|
|
now = time.monotonic()
|
|
safe_mode = self.params.get_bool("SafeMode")
|
|
|
|
self.status_value = CCStatus["OFF"] if safe_mode else restore_persisted_cc_state(self.params, self.params_memory)
|
|
|
|
if is_manual_cc_status(self.status_value):
|
|
self._reset_timers()
|
|
self.experimental_mode = self.status_value == CCStatus["USER_EXPERIMENTAL"]
|
|
self._write_status(self.status_value)
|
|
return
|
|
|
|
self._refresh_detector(v_ego, sm)
|
|
auto_status, launch_candidate = self._get_chill_status(v_ego, v_cruise, sm, starpilot_toggles)
|
|
|
|
if safe_mode or self._has_hard_veto(v_ego, sm, allow_launch=launch_candidate):
|
|
self._reset_timers()
|
|
self.experimental_mode = False if safe_mode else True
|
|
self.status_value = CCStatus["OFF"]
|
|
self._write_status(CCStatus["OFF"])
|
|
return
|
|
|
|
chill_candidate = auto_status != CCStatus["OFF"]
|
|
entry_confirm_time = self._get_entry_confirm_time(auto_status, launch_candidate)
|
|
|
|
if chill_candidate:
|
|
if self._candidate_since == 0.0:
|
|
self._candidate_since = now
|
|
self._soft_exit_since = 0.0
|
|
|
|
if not self.experimental_mode or (now - self._candidate_since) >= entry_confirm_time:
|
|
self.experimental_mode = False
|
|
self._active_auto_status = auto_status
|
|
self._chill_hold_until = max(self._chill_hold_until, now + self.CHILL_MIN_DWELL_TIME)
|
|
self.status_value = self._active_auto_status if not self.experimental_mode else CCStatus["OFF"]
|
|
else:
|
|
self._candidate_since = 0.0
|
|
if not self.experimental_mode:
|
|
if self._soft_exit_since == 0.0:
|
|
self._soft_exit_since = now
|
|
|
|
hold_active = now < self._chill_hold_until
|
|
exit_buffer_active = (now - self._soft_exit_since) < self.CHILL_EXIT_BUFFER_TIME
|
|
if hold_active or exit_buffer_active:
|
|
self.status_value = self._active_auto_status
|
|
else:
|
|
self.experimental_mode = True
|
|
self._active_auto_status = CCStatus["OFF"]
|
|
self.status_value = CCStatus["OFF"]
|
|
else:
|
|
self._soft_exit_since = 0.0
|
|
self.status_value = CCStatus["OFF"]
|
|
|
|
self._write_status(self.status_value if not self.experimental_mode else CCStatus["OFF"])
|
|
|
|
def _reset_timers(self):
|
|
self._active_auto_status = CCStatus["OFF"]
|
|
self._candidate_since = 0.0
|
|
self._soft_exit_since = 0.0
|
|
self._chill_hold_until = 0.0
|
|
self._launch_active = False
|
|
|
|
def _refresh_detector(self, v_ego, sm):
|
|
detector_toggles = type("DetectorToggles", (), {
|
|
"conditional_curves": True,
|
|
"conditional_curves_lead": True,
|
|
"conditional_lead": True,
|
|
"conditional_slower_lead": True,
|
|
"conditional_stopped_lead": True,
|
|
})()
|
|
self.detector.curve_detection(v_ego, detector_toggles)
|
|
self.detector.slow_lead(detector_toggles, v_ego)
|
|
self.detector.stop_sign_and_light(v_ego, sm, self.CCM_STOP_MODEL_TIME)
|
|
|
|
def _has_hard_veto(self, v_ego, sm, allow_launch=False):
|
|
if sm["carState"].standstill and not allow_launch:
|
|
return True
|
|
|
|
if sm["carState"].leftBlinker or sm["carState"].rightBlinker:
|
|
return True
|
|
|
|
if sm["starpilotCarState"].trafficModeEnabled:
|
|
return True
|
|
|
|
if self.starpilot_planner.starpilot_vcruise.slc.experimental_mode:
|
|
return True
|
|
|
|
if self.detector.curve_detected or self.detector.slow_lead_detected or self.detector.stop_light_detected:
|
|
return True
|
|
|
|
if self.starpilot_planner.starpilot_vcruise.stop_sign_confirmed or self.starpilot_planner.starpilot_vcruise.forcing_stop:
|
|
return True
|
|
|
|
if self._adjacent_lead_ambiguous(sm, v_ego):
|
|
return True
|
|
|
|
return self._low_speed_stop_scene(v_ego) and not allow_launch
|
|
|
|
def _low_speed_stop_scene(self, v_ego):
|
|
if v_ego >= self.LOW_SPEED_STOP_SCENE_MAX_SPEED:
|
|
return False
|
|
|
|
if self.starpilot_planner.raw_model_stopped or self.starpilot_planner.model_stopped or self.detector.stop_light_model_detected:
|
|
return True
|
|
|
|
lead = self.starpilot_planner.lead_one
|
|
if not getattr(lead, "status", False):
|
|
return False
|
|
|
|
lead_distance = float(getattr(lead, "dRel", float("inf")))
|
|
lead_speed = float(getattr(lead, "vLead", float("inf")))
|
|
lead_distance_limit = max(40.0, v_ego * self.STABLE_LEAD_MAX_DISTANCE_TIME)
|
|
return lead_distance < lead_distance_limit and lead_speed < max(6.0, v_ego + 0.5)
|
|
|
|
def _get_chill_status(self, v_ego, v_cruise, sm, starpilot_toggles):
|
|
if getattr(starpilot_toggles, "conditional_chill_launch_assist", False):
|
|
launch_status = self._get_launch_status(v_ego, sm)
|
|
if launch_status != CCStatus["OFF"]:
|
|
return launch_status, True
|
|
|
|
lead = self.starpilot_planner.lead_one
|
|
lead_status = bool(getattr(lead, "status", False))
|
|
tracking_lead = bool(getattr(self.starpilot_planner, "tracking_lead", False))
|
|
set_speed_error = max(0.0, v_cruise - v_ego)
|
|
|
|
if (not lead_status and not tracking_lead and
|
|
v_ego >= starpilot_toggles.conditional_chill_speed and
|
|
set_speed_error >= starpilot_toggles.conditional_chill_speed_margin):
|
|
return CCStatus["SPEED"], False
|
|
|
|
if not starpilot_toggles.conditional_chill_lead:
|
|
return CCStatus["OFF"], False
|
|
|
|
if v_ego < starpilot_toggles.conditional_chill_speed_lead or not lead_status or not tracking_lead:
|
|
return CCStatus["OFF"], False
|
|
|
|
lead_distance = float(getattr(lead, "dRel", float("inf")))
|
|
lead_speed = float(getattr(lead, "vLead", 0.0))
|
|
lead_brake = max(0.0, -float(getattr(lead, "aLeadK", 0.0)))
|
|
lead_prob = float(getattr(lead, "modelProb", 0.0))
|
|
closing_speed = max(0.0, v_ego - lead_speed)
|
|
max_closing_speed = max(self.STABLE_LEAD_MAX_CLOSING_SPEED, self.STABLE_LEAD_MAX_CLOSING_RATIO * v_ego)
|
|
max_distance = min(self.STABLE_LEAD_MAX_DISTANCE, max(35.0, v_ego * self.STABLE_LEAD_MAX_DISTANCE_TIME))
|
|
lead_confident = bool(getattr(lead, "radar", False)) or lead_prob >= self.STABLE_LEAD_MIN_MODEL_PROB
|
|
|
|
if not lead_confident:
|
|
return CCStatus["OFF"], False
|
|
|
|
if lead_distance >= max_distance or lead_speed <= self.STABLE_LEAD_MIN_SPEED:
|
|
return CCStatus["OFF"], False
|
|
|
|
if lead_brake > self.STABLE_LEAD_MAX_BRAKE or closing_speed > max_closing_speed:
|
|
return CCStatus["OFF"], False
|
|
|
|
return CCStatus["LEAD"], False
|
|
|
|
def _get_launch_status(self, v_ego, sm):
|
|
if self._launch_active and self._launch_exit_required(v_ego, sm):
|
|
self._launch_active = False
|
|
return CCStatus["OFF"]
|
|
|
|
if self._launch_active:
|
|
return self._get_launch_cc_status()
|
|
|
|
if not self._launch_scene_eligible(v_ego, sm):
|
|
return CCStatus["OFF"]
|
|
|
|
self._launch_active = True
|
|
return self._get_launch_cc_status()
|
|
|
|
def _launch_scene_eligible(self, v_ego, sm):
|
|
if v_ego > self.CHILL_LAUNCH_MAX_ENTRY_SPEED and not self._launch_active:
|
|
return False
|
|
|
|
selfdrive_state = self._get_sm_service(sm, "selfdriveState")
|
|
longitudinal_plan = self._get_sm_service(sm, "longitudinalPlan")
|
|
starpilot_plan = self._get_sm_service(sm, "starpilotPlan")
|
|
if selfdrive_state is None or longitudinal_plan is None:
|
|
return False
|
|
|
|
if not bool(getattr(selfdrive_state, "enabled", False)):
|
|
return False
|
|
|
|
if (
|
|
self.detector.stop_light_detected or
|
|
self.detector.stop_light_model_detected or
|
|
self.starpilot_planner.raw_model_stopped or
|
|
self.starpilot_planner.model_stopped or
|
|
self.starpilot_planner.starpilot_vcruise.stop_sign_confirmed or
|
|
self.starpilot_planner.starpilot_vcruise.forcing_stop or
|
|
bool(getattr(starpilot_plan, "redLight", False)) or
|
|
bool(getattr(starpilot_plan, "forcingStop", False))
|
|
):
|
|
return False
|
|
|
|
if bool(getattr(longitudinal_plan, "shouldStop", False)) or not bool(getattr(longitudinal_plan, "allowThrottle", False)):
|
|
return False
|
|
|
|
lead = getattr(self.starpilot_planner, "lead_one", None)
|
|
lead_status = bool(getattr(lead, "status", False))
|
|
tracking_lead = bool(getattr(self.starpilot_planner, "tracking_lead", False))
|
|
if not lead_status and not tracking_lead:
|
|
return True
|
|
|
|
lead_speed = float(getattr(lead, "vLead", 0.0))
|
|
lead_brake = max(0.0, -float(getattr(lead, "aLeadK", 0.0)))
|
|
lead_accel = float(getattr(lead, "aLeadK", 0.0))
|
|
lead_delta = lead_speed - float(v_ego)
|
|
lead_vrel = float(getattr(lead, "vRel", lead_delta))
|
|
closing_speed = max(0.0, v_ego - lead_speed)
|
|
|
|
return (
|
|
lead_speed >= self.CHILL_LAUNCH_MIN_LEAD_SPEED and
|
|
lead_delta >= self.CHILL_LAUNCH_MIN_LEAD_DELTA and
|
|
lead_vrel >= self.CHILL_LAUNCH_MIN_LEAD_VREL and
|
|
lead_accel >= self.CHILL_LAUNCH_MIN_LEAD_ACCEL and
|
|
lead_brake <= self.CHILL_LAUNCH_MAX_BRAKE and
|
|
closing_speed <= self.CHILL_LAUNCH_MAX_CLOSING_SPEED
|
|
)
|
|
|
|
def _launch_exit_required(self, v_ego, sm):
|
|
if v_ego >= self.CHILL_LAUNCH_EXIT_SPEED:
|
|
return True
|
|
|
|
if not self._launch_scene_eligible(v_ego, sm):
|
|
return True
|
|
|
|
return False
|
|
|
|
def _get_launch_cc_status(self):
|
|
lead = getattr(self.starpilot_planner, "lead_one", None)
|
|
lead_status = bool(getattr(lead, "status", False))
|
|
tracking_lead = bool(getattr(self.starpilot_planner, "tracking_lead", False))
|
|
return CCStatus["LEAD"] if lead_status or tracking_lead else CCStatus["SPEED"]
|
|
|
|
def _get_entry_confirm_time(self, auto_status, launch_candidate):
|
|
if launch_candidate:
|
|
return self.CHILL_LAUNCH_ENTRY_CONFIRM_TIME
|
|
|
|
if auto_status == CCStatus["LEAD"]:
|
|
return self.CHILL_LEAD_ENTRY_CONFIRM_TIME
|
|
|
|
return self.CHILL_SPEED_ENTRY_CONFIRM_TIME
|
|
|
|
def _adjacent_lead_ambiguous(self, sm, v_ego):
|
|
radar_state = self._get_sm_service(sm, "starpilotRadarState")
|
|
if radar_state is None:
|
|
return False
|
|
|
|
max_distance = min(self.ADJACENT_LEAD_VETO_MAX_DISTANCE, max(25.0, v_ego * self.ADJACENT_LEAD_VETO_MAX_DISTANCE_TIME))
|
|
for lead in (getattr(radar_state, "leadLeft", None), getattr(radar_state, "leadRight", None)):
|
|
if lead is None or not getattr(lead, "status", False):
|
|
continue
|
|
|
|
lateral_offset = abs(float(getattr(lead, "yRel", 0.0)))
|
|
if lateral_offset > self.ADJACENT_LEAD_VETO_MAX_LATERAL_OFFSET:
|
|
continue
|
|
|
|
if float(getattr(lead, "dRel", float("inf"))) < max_distance and float(getattr(lead, "vLead", 0.0)) > self.ADJACENT_LEAD_VETO_MIN_SPEED:
|
|
return True
|
|
|
|
return False
|
|
|
|
@staticmethod
|
|
def _get_sm_service(sm, key):
|
|
if isinstance(sm, dict):
|
|
return sm.get(key)
|
|
|
|
try:
|
|
return sm[key]
|
|
except (KeyError, IndexError, TypeError, AttributeError):
|
|
return None
|
|
|
|
def _write_status(self, status_value):
|
|
if status_value != self._prev_cc_status:
|
|
self.params_memory.put_int("CCStatus", status_value)
|
|
self._prev_cc_status = status_value
|