mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-26 19:52:06 +08:00
499 lines
20 KiB
Python
499 lines
20 KiB
Python
#!/usr/bin/env python3
|
|
from enum import IntEnum
|
|
import math
|
|
|
|
import numpy as np
|
|
import cereal.messaging as messaging
|
|
from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX
|
|
|
|
from openpilot.common.constants import CV
|
|
from openpilot.common.filter_simple import FirstOrderFilter
|
|
from openpilot.common.realtime import DT_MDL
|
|
from openpilot.common.swaglog import cloudlog
|
|
from openpilot.selfdrive.car.cruise import V_CRUISE_UNSET
|
|
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N
|
|
from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState
|
|
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import (
|
|
STOP_DISTANCE,
|
|
T_IDXS as T_IDXS_MPC,
|
|
LongitudinalMpc,
|
|
should_trigger_planner_fcw,
|
|
)
|
|
from openpilot.selfdrive.controls.lib.longitudinal_vehicle_tunes import get_longitudinal_planner_tune
|
|
from openpilot.selfdrive.modeld.constants import ModelConstants
|
|
|
|
|
|
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
|
|
MIN_PLAN_HORIZON = 0.30
|
|
A_CRUISE_MAX_BP = [0.0, 5.0, 10.0, 15.0, 20.0, 25.0, 40.0]
|
|
A_CRUISE_MAX_VALS = [1.125, 1.125, 1.125, 1.125, 1.25, 1.25, 1.5]
|
|
A_CRUISE_MIN = -1.0
|
|
|
|
ALLOW_THROTTLE_THRESHOLD = 0.40
|
|
ALLOW_THROTTLE_HYSTERESIS = 0.05
|
|
ALLOW_THROTTLE_CONFIRM_TIME = 0.25
|
|
MIN_ALLOW_THROTTLE_SPEED = 1.5
|
|
COAST_TARGET_SPEED = 5.0
|
|
COAST_TARGET_GAIN = 0.35
|
|
|
|
MODEL_STOP_BEGIN_TIME = 9.0
|
|
MODEL_STOP_FULL_TIME = 4.0
|
|
MODEL_STOP_MIN_END_SPEED = 2.0
|
|
MODEL_FIRST_AUTHORITY = 0.75
|
|
MODEL_FIRST_OPEN_ROAD_AUTHORITY = 0.35
|
|
MODEL_FIRST_CRUISE_ERROR = 1.5
|
|
|
|
STOP_ENTER_AUTHORITY = 0.72
|
|
STOP_RELEASE_TIME = 0.50
|
|
STOPPED_SPEED = 0.15
|
|
STOP_CONTROL_SPEED = 1.5
|
|
DEPART_COMPLETE_SPEED = 2.5
|
|
DEPART_LEAD_MIN_SPEED = 0.10
|
|
DEPART_LEAD_FAST_SPEED = 0.75
|
|
DEPART_CONFIRM_TIME = 0.20
|
|
DEPART_LEAD_MIN_GAP = 0.0
|
|
DEPART_MODEL_MIN_ACCEL = 0.08
|
|
DEPART_LEAD_MAX_BRAKE = 0.30
|
|
|
|
RAW_SAFETY_REACTION_BUFFER = 0.20
|
|
RAW_SAFETY_MIN_CLOSING_SPEED = 0.25
|
|
RAW_SAFETY_MIN_DECEL = 0.20
|
|
RAW_SAFETY_URGENT_DECEL = 0.80
|
|
RAW_SAFETY_URGENT_TTC = 4.0
|
|
RAW_SAFETY_EXTRA_STOP_BUFFER = 1.0
|
|
|
|
FORCE_STOP_HANDOFF_MAX_VCRUISE = 0.5
|
|
|
|
_A_TOTAL_MAX_V = [3.5, 3.5, 3.2]
|
|
_A_TOTAL_MAX_BP = [0.0, 20.0, 40.0]
|
|
|
|
|
|
class PlanState(IntEnum):
|
|
moving = 0
|
|
stopping = 1
|
|
stopped = 2
|
|
departing = 3
|
|
|
|
|
|
def get_coast_accel(pitch):
|
|
return np.sin(pitch) * -5.65 - 0.3
|
|
|
|
|
|
def get_max_accel(v_ego):
|
|
return float(np.interp(v_ego, A_CRUISE_MAX_BP, A_CRUISE_MAX_VALS))
|
|
|
|
|
|
def limit_accel_in_turns(v_ego, angle_steers, accel_limits, CP):
|
|
a_total_max = np.interp(v_ego, _A_TOTAL_MAX_BP, _A_TOTAL_MAX_V)
|
|
a_y = v_ego ** 2 * angle_steers * CV.DEG_TO_RAD / (CP.steerRatio * CP.wheelbase)
|
|
a_x_allowed = math.sqrt(max(a_total_max ** 2 - a_y ** 2, 0.0))
|
|
return [accel_limits[0], min(accel_limits[1], a_x_allowed)]
|
|
|
|
|
|
def get_vehicle_min_accel(CP, v_ego):
|
|
is_gm = getattr(CP, "carName", "") == "gm" or getattr(CP, "brand", "") == "gm"
|
|
if is_gm and getattr(CP, "enableGasInterceptorDEPRECATED", False):
|
|
try:
|
|
from opendbc.car.gm.values import CAR, GMFlags
|
|
if bool(CP.flags & GMFlags.PEDAL_LONG.value):
|
|
bolt_pedal_long = {
|
|
CAR.CHEVROLET_BOLT_CC_2017,
|
|
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
|
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
|
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
|
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
|
}
|
|
values = [-0.93, -1.28, -1.98, -2.58, -2.86, -2.95] if CP.carFingerprint in bolt_pedal_long else \
|
|
[-0.95, -1.30, -1.85, -2.30, -2.60, -2.80]
|
|
return float(np.interp(v_ego, [0.0, 1.5, 4.0, 8.0, 15.0, 30.0], values))
|
|
except Exception:
|
|
pass
|
|
return float(ACCEL_MIN)
|
|
|
|
|
|
def get_planner_v_ego(CP, car_state):
|
|
is_gm = getattr(CP, "carName", "") == "gm" or getattr(CP, "brand", "") == "gm"
|
|
if is_gm and getattr(CP, "enableGasInterceptorDEPRECATED", False):
|
|
try:
|
|
from opendbc.car.gm.values import GMFlags
|
|
if bool(CP.flags & GMFlags.PEDAL_LONG.value):
|
|
return float(car_state.vEgo)
|
|
except Exception:
|
|
pass
|
|
return float(max(car_state.vEgo, car_state.vEgoCluster))
|
|
|
|
|
|
def get_accel_from_plan(speeds, accels, action_t=DT_MDL, v_ego_stopping=0.05):
|
|
if len(speeds) != CONTROL_N:
|
|
return 0.0, False
|
|
|
|
v_now = float(speeds[0])
|
|
a_now = float(accels[0])
|
|
action_t = max(float(action_t), DT_MDL)
|
|
v_target = float(np.interp(action_t, CONTROL_N_T_IDX, speeds))
|
|
a_target = 2.0 * (v_target - v_now) / action_t - a_now
|
|
v_target_1sec = float(np.interp(action_t + 1.0, CONTROL_N_T_IDX, speeds))
|
|
should_stop = v_target < v_ego_stopping and v_target_1sec < v_ego_stopping
|
|
return a_target, should_stop
|
|
|
|
|
|
def _smoothstep(value, low, high):
|
|
if high <= low:
|
|
return float(value >= high)
|
|
x = float(np.clip((value - low) / (high - low), 0.0, 1.0))
|
|
return x * x * (3.0 - 2.0 * x)
|
|
|
|
|
|
class LongitudinalPlanner:
|
|
def __init__(self, CP, init_v=0.0, init_a=0.0, dt=DT_MDL):
|
|
self.CP = CP
|
|
self.dt = dt
|
|
self.mpc = LongitudinalMpc(dt=dt)
|
|
self.tune = get_longitudinal_planner_tune(CP)
|
|
self.mpc.lead_filter_tau = self.tune.lead_filter_tau
|
|
|
|
self.v_desired_filter = FirstOrderFilter(init_v, 2.0, dt)
|
|
self.a_desired = init_a
|
|
self.output_a_target = init_a
|
|
self.output_should_stop = False
|
|
self.allow_throttle = True
|
|
self._throttle_transition_time = 0.0
|
|
|
|
self.state = PlanState.moving
|
|
self.stop_release_time = 0.0
|
|
self.departure_confirm_time = 0.0
|
|
self.model_authority = 0.0
|
|
self.plan_source = "cruise"
|
|
self.fcw = False
|
|
|
|
self.v_desired_trajectory = np.zeros(CONTROL_N)
|
|
self.a_desired_trajectory = np.zeros(CONTROL_N)
|
|
self.j_desired_trajectory = np.zeros(CONTROL_N)
|
|
|
|
@staticmethod
|
|
def _model_stop_authority(model, v_ego):
|
|
if bool(getattr(model.action, "shouldStop", False)):
|
|
return 1.0
|
|
if not len(model.position.x):
|
|
return 0.0
|
|
|
|
model_length = max(float(model.position.x[-1]), 0.0)
|
|
horizon = model_length / max(float(v_ego), 1.0)
|
|
end_speed = float(model.velocity.x[-1]) if len(model.velocity.x) else float(v_ego)
|
|
time_score = 1.0 - _smoothstep(horizon, MODEL_STOP_FULL_TIME, MODEL_STOP_BEGIN_TIME)
|
|
speed_score = 1.0 - _smoothstep(end_speed, 0.5, MODEL_STOP_MIN_END_SPEED)
|
|
return float(time_score * speed_score)
|
|
|
|
@staticmethod
|
|
def _curve_authority(sm, v_ego):
|
|
curvature = abs(float(getattr(sm["starpilotPlan"], "roadCurvature", 0.0)))
|
|
lateral_accel = curvature * float(v_ego) ** 2
|
|
curve_score = _smoothstep(lateral_accel, 0.7, 2.0)
|
|
turning = bool(sm["carState"].leftBlinker or sm["carState"].rightBlinker)
|
|
turn_score = 0.55 if turning and v_ego < 15.0 else 0.0
|
|
return max(curve_score, turn_score)
|
|
|
|
def _get_model_authority(self, sm, v_ego, v_cruise, has_lead, model_first, stop_authority):
|
|
curve_authority = self._curve_authority(sm, v_ego)
|
|
hard_stop = bool(
|
|
getattr(sm["starpilotPlan"], "redLight", False) or
|
|
getattr(sm["starpilotPlan"], "forcingStop", False) or
|
|
getattr(sm["starpilotPlan"], "stopSignConfirmed", False)
|
|
)
|
|
scene_authority = 1.0 if hard_stop else max(stop_authority, curve_authority)
|
|
|
|
if not model_first:
|
|
return scene_authority
|
|
|
|
cruise_error = max(0.0, float(v_cruise) - float(v_ego))
|
|
open_road = not has_lead and scene_authority < 0.1
|
|
open_road_blend = _smoothstep(cruise_error, 0.0, MODEL_FIRST_CRUISE_ERROR)
|
|
base_authority = (
|
|
MODEL_FIRST_AUTHORITY +
|
|
(MODEL_FIRST_OPEN_ROAD_AUTHORITY - MODEL_FIRST_AUTHORITY) * open_road_blend
|
|
if open_road else MODEL_FIRST_AUTHORITY
|
|
)
|
|
return max(scene_authority, base_authority)
|
|
|
|
@staticmethod
|
|
def _lead_safety_cap(lead, v_ego, reaction_time, stop_buffer):
|
|
if lead is None or not bool(getattr(lead, "status", False)):
|
|
return None, False
|
|
|
|
d_rel = max(float(getattr(lead, "dRel", 0.0)), 0.0)
|
|
v_lead = max(float(getattr(lead, "vLead", 0.0)), 0.0)
|
|
closing_speed = max(0.0, float(v_ego) - v_lead)
|
|
if closing_speed < RAW_SAFETY_MIN_CLOSING_SPEED:
|
|
return None, False
|
|
|
|
lead_brake = max(0.0, -float(getattr(lead, "aLeadK", 0.0)))
|
|
projected_closing = closing_speed + lead_brake * reaction_time
|
|
available_distance = d_rel - stop_buffer - closing_speed * reaction_time
|
|
ttc = max(d_rel - stop_buffer, 0.0) / max(closing_speed, 1e-3)
|
|
|
|
if available_distance <= 0.0:
|
|
return float(ACCEL_MIN), True
|
|
|
|
required_decel = projected_closing ** 2 / (2.0 * available_distance)
|
|
required_decel += 0.35 * lead_brake
|
|
urgent = required_decel >= RAW_SAFETY_URGENT_DECEL or ttc <= RAW_SAFETY_URGENT_TTC
|
|
if required_decel < RAW_SAFETY_MIN_DECEL and not urgent:
|
|
return None, False
|
|
return -float(required_decel), urgent
|
|
|
|
def _get_raw_safety_cap(self, sm, v_ego, reaction_time):
|
|
stop_buffer = (
|
|
RAW_SAFETY_EXTRA_STOP_BUFFER +
|
|
float(getattr(sm["starpilotPlan"], "increasedStoppedDistance", 0.0))
|
|
)
|
|
caps = [
|
|
self._lead_safety_cap(lead, v_ego, reaction_time, stop_buffer)
|
|
for lead in (sm["radarState"].leadOne, sm["radarState"].leadTwo)
|
|
]
|
|
valid_caps = [cap for cap, _ in caps if cap is not None]
|
|
return (min(valid_caps) if valid_caps else None), any(urgent for _, urgent in caps)
|
|
|
|
@staticmethod
|
|
def _safe_departure(sm, model_accel, stop_buffer):
|
|
force_stop = bool(getattr(sm["starpilotPlan"], "forcingStop", False))
|
|
stop_sign = bool(getattr(sm["starpilotPlan"], "stopSignConfirmed", False))
|
|
if force_stop or stop_sign or bool(sm["carState"].brakePressed):
|
|
return False, False
|
|
|
|
leads = [lead for lead in (sm["radarState"].leadOne, sm["radarState"].leadTwo)
|
|
if bool(getattr(lead, "status", False))]
|
|
if not leads:
|
|
return model_accel >= DEPART_MODEL_MIN_ACCEL, False
|
|
|
|
lead = min(leads, key=lambda item: float(item.dRel))
|
|
lead_brake = max(0.0, -float(getattr(lead, "aLeadK", 0.0)))
|
|
candidate = bool(
|
|
float(lead.vLead) >= DEPART_LEAD_MIN_SPEED and
|
|
float(lead.dRel) >= stop_buffer + DEPART_LEAD_MIN_GAP and
|
|
lead_brake <= DEPART_LEAD_MAX_BRAKE
|
|
)
|
|
return candidate, candidate and float(lead.vLead) >= DEPART_LEAD_FAST_SPEED
|
|
|
|
def _update_state(self, sm, v_ego, stop_request, safe_departure):
|
|
if stop_request:
|
|
self.stop_release_time = 0.0
|
|
self.state = PlanState.stopped if v_ego <= STOPPED_SPEED else PlanState.stopping
|
|
return
|
|
|
|
if self.state in (PlanState.stopping, PlanState.stopped):
|
|
if safe_departure:
|
|
self.state = PlanState.departing
|
|
self.stop_release_time = 0.0
|
|
else:
|
|
self.stop_release_time += self.dt
|
|
if self.stop_release_time >= STOP_RELEASE_TIME and v_ego > STOPPED_SPEED:
|
|
self.state = PlanState.moving
|
|
return
|
|
|
|
if self.state == PlanState.departing and v_ego >= DEPART_COMPLETE_SPEED:
|
|
self.state = PlanState.moving
|
|
elif self.state == PlanState.departing and not safe_departure:
|
|
self.state = PlanState.stopped if v_ego <= STOPPED_SPEED else PlanState.stopping
|
|
|
|
def _update_allow_throttle(self, throttle_prob, v_ego):
|
|
if v_ego <= MIN_ALLOW_THROTTLE_SPEED:
|
|
self.allow_throttle = True
|
|
self._throttle_transition_time = 0.0
|
|
else:
|
|
lower = ALLOW_THROTTLE_THRESHOLD - ALLOW_THROTTLE_HYSTERESIS
|
|
upper = ALLOW_THROTTLE_THRESHOLD + ALLOW_THROTTLE_HYSTERESIS
|
|
requested = throttle_prob <= lower if self.allow_throttle else throttle_prob > upper
|
|
self._throttle_transition_time = self._throttle_transition_time + self.dt if requested else 0.0
|
|
if self._throttle_transition_time >= ALLOW_THROTTLE_CONFIRM_TIME:
|
|
self.allow_throttle = not self.allow_throttle
|
|
self._throttle_transition_time = 0.0
|
|
|
|
def _smooth_output(self, target, urgent):
|
|
if urgent or target <= self.output_a_target - RAW_SAFETY_URGENT_DECEL:
|
|
return float(target)
|
|
lower = self.output_a_target - self.tune.brake_slew_rate * self.dt
|
|
upper = self.output_a_target + self.tune.accel_slew_rate * self.dt
|
|
return float(np.clip(target, lower, upper))
|
|
|
|
def update(self, sm, starpilot_toggles):
|
|
car_state = sm["carState"]
|
|
v_ego = get_planner_v_ego(self.CP, car_state)
|
|
scene_v_ego = float(car_state.vEgo)
|
|
v_cruise = float(sm["starpilotPlan"].vCruise)
|
|
if not np.isfinite(v_cruise):
|
|
cloudlog.error(f"Longitudinal planner received non-finite vCruise={v_cruise}")
|
|
v_cruise = max(v_ego, 0.0)
|
|
|
|
reset_state = (
|
|
(sm["controlsState"].longControlState == LongCtrlState.off
|
|
if self.CP.openpilotLongitudinalControl else not sm["selfdriveState"].enabled) or
|
|
car_state.vCruise == V_CRUISE_UNSET
|
|
)
|
|
|
|
accel_limits = [
|
|
float(sm["starpilotPlan"].minAcceleration),
|
|
float(sm["starpilotPlan"].maxAcceleration),
|
|
]
|
|
steer_angle = car_state.steeringAngleDeg - sm["liveParameters"].angleOffsetDeg
|
|
accel_limits = limit_accel_in_turns(v_ego, steer_angle, accel_limits, self.CP)
|
|
accel_limits[0] = max(get_vehicle_min_accel(self.CP, v_ego), accel_limits[0])
|
|
|
|
if reset_state:
|
|
self.v_desired_filter.x = v_ego
|
|
self.a_desired = float(np.clip(car_state.aEgo, *accel_limits))
|
|
self.output_a_target = self.a_desired
|
|
self.state = PlanState.moving
|
|
self.stop_release_time = 0.0
|
|
self.departure_confirm_time = 0.0
|
|
|
|
self.v_desired_filter.x = max(0.0, self.v_desired_filter.update(v_ego))
|
|
|
|
gas_probs = sm["modelV2"].meta.disengagePredictions.gasPressProbs
|
|
throttle_prob = float(gas_probs[1]) if len(gas_probs) > 1 else 1.0
|
|
self._update_allow_throttle(throttle_prob, v_ego)
|
|
if not self.allow_throttle:
|
|
pitch = sm["carControl"].orientationNED[1] if len(sm["carControl"].orientationNED) == 3 else 0.0
|
|
coast_accel = get_coast_accel(pitch)
|
|
speed_guard = -COAST_TARGET_GAIN * (v_ego - COAST_TARGET_SPEED)
|
|
accel_limits[1] = min(accel_limits[1], max(coast_accel, speed_guard, accel_limits[0]))
|
|
|
|
if sm["controlsState"].forceDecel:
|
|
v_cruise = 0.0
|
|
|
|
accel_limits[0] = min(accel_limits[0], self.a_desired + 0.05)
|
|
accel_limits[1] = max(accel_limits[1], self.a_desired - 0.05)
|
|
self.mpc.set_accel_limits(*accel_limits)
|
|
self.mpc.set_weights(
|
|
acceleration_jerk=float(sm["starpilotPlan"].accelerationJerk) / 200.0,
|
|
danger_jerk=float(sm["starpilotPlan"].dangerJerk) / 100.0,
|
|
speed_jerk=float(sm["starpilotPlan"].speedJerk) / 5.0,
|
|
prev_accel_constraint=not (reset_state or car_state.standstill),
|
|
)
|
|
self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
|
|
self.mpc.update(
|
|
sm["radarState"],
|
|
v_cruise,
|
|
danger_factor=float(sm["starpilotPlan"].dangerFactor),
|
|
t_follow=float(sm["starpilotPlan"].tFollow),
|
|
personality=sm["selfdriveState"].personality,
|
|
)
|
|
|
|
self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution)
|
|
self.a_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution)
|
|
self.j_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC[:-1], self.mpc.j_solution)
|
|
|
|
previous_a = self.a_desired
|
|
self.a_desired = float(np.interp(self.dt, CONTROL_N_T_IDX, self.a_desired_trajectory))
|
|
self.v_desired_filter.x += self.dt * (self.a_desired + previous_a) / 2.0
|
|
|
|
actuator_delay = float(getattr(starpilot_toggles, "longitudinalActuatorDelay", self.CP.longitudinalActuatorDelay))
|
|
action_t = max(actuator_delay + self.dt, MIN_PLAN_HORIZON)
|
|
mpc_target, mpc_should_stop = get_accel_from_plan(
|
|
self.v_desired_trajectory,
|
|
self.a_desired_trajectory,
|
|
action_t,
|
|
float(getattr(starpilot_toggles, "vEgoStopping", 0.05)),
|
|
)
|
|
|
|
model_target = float(getattr(sm["modelV2"].action, "desiredAcceleration", mpc_target))
|
|
if not np.isfinite(model_target):
|
|
model_target = mpc_target
|
|
|
|
leads = (sm["radarState"].leadOne, sm["radarState"].leadTwo)
|
|
has_lead = any(bool(getattr(lead, "status", False)) for lead in leads)
|
|
model_first = bool(getattr(starpilot_toggles, "longitudinal_model_preference", False))
|
|
stop_authority = self._model_stop_authority(sm["modelV2"], scene_v_ego)
|
|
self.model_authority = self._get_model_authority(
|
|
sm, scene_v_ego, v_cruise, has_lead, model_first, stop_authority,
|
|
)
|
|
model_limited_target = min(mpc_target, model_target)
|
|
target = mpc_target + self.model_authority * (model_limited_target - mpc_target)
|
|
self.plan_source = "e2e" if model_limited_target < mpc_target - 1e-3 and self.model_authority > 0.05 else self.mpc.source
|
|
|
|
pinned_stop = bool(
|
|
getattr(sm["starpilotPlan"], "forcingStop", False) or
|
|
getattr(sm["starpilotPlan"], "stopSignConfirmed", False) or
|
|
car_state.brakePressed
|
|
)
|
|
scene_stop = bool(
|
|
getattr(sm["starpilotPlan"], "redLight", False) or
|
|
pinned_stop or
|
|
getattr(sm["modelV2"].action, "shouldStop", False)
|
|
)
|
|
model_stop_request = stop_authority >= STOP_ENTER_AUTHORITY
|
|
stop_request = bool(scene_stop or mpc_should_stop or model_stop_request)
|
|
increased_stop_distance = float(getattr(sm["starpilotPlan"], "increasedStoppedDistance", 0.0))
|
|
departure_buffer = STOP_DISTANCE + increased_stop_distance
|
|
departure_candidate, fast_departure = self._safe_departure(sm, model_target, departure_buffer)
|
|
self.departure_confirm_time = self.departure_confirm_time + self.dt if departure_candidate else 0.0
|
|
safe_departure = departure_candidate and (
|
|
fast_departure or self.departure_confirm_time >= DEPART_CONFIRM_TIME
|
|
)
|
|
if safe_departure and not pinned_stop:
|
|
stop_request = False
|
|
was_departing = self.state == PlanState.departing
|
|
was_stopped = self.state == PlanState.stopped
|
|
self._update_state(sm, scene_v_ego, stop_request, safe_departure)
|
|
departure_aborted = was_departing and self.state in (PlanState.stopping, PlanState.stopped)
|
|
departure_started = was_stopped and self.state == PlanState.departing
|
|
|
|
if self.state == PlanState.stopped:
|
|
target = min(target, float(getattr(starpilot_toggles, "stopAccel", -0.5)))
|
|
elif self.state == PlanState.stopping and scene_v_ego <= DEPART_COMPLETE_SPEED:
|
|
target = min(target, float(getattr(starpilot_toggles, "stopAccel", -0.5)))
|
|
elif self.state == PlanState.departing and safe_departure:
|
|
target = max(target, self.tune.launch_accel)
|
|
|
|
safety_cap, urgent = self._get_raw_safety_cap(
|
|
sm,
|
|
scene_v_ego,
|
|
actuator_delay + RAW_SAFETY_REACTION_BUFFER,
|
|
)
|
|
urgent |= departure_aborted or departure_started
|
|
if safety_cap is not None:
|
|
target = min(target, safety_cap)
|
|
if safety_cap is not None and safety_cap < -RAW_SAFETY_MIN_DECEL:
|
|
if self.state == PlanState.departing:
|
|
self.state = PlanState.stopping
|
|
stop_request |= scene_v_ego <= STOP_CONTROL_SPEED
|
|
|
|
target = float(np.clip(target, accel_limits[0], accel_limits[1]))
|
|
self.output_a_target = self._smooth_output(target, urgent)
|
|
self.output_should_stop = bool(
|
|
self.state in (PlanState.stopping, PlanState.stopped) and
|
|
(scene_v_ego <= STOP_CONTROL_SPEED or mpc_should_stop)
|
|
)
|
|
|
|
self.fcw = self.mpc.crash_cnt > 2 and not car_state.standstill and any(
|
|
should_trigger_planner_fcw(lead, scene_v_ego) for lead in leads
|
|
)
|
|
if self.fcw:
|
|
cloudlog.info("FCW triggered")
|
|
|
|
def publish(self, sm, pm):
|
|
plan_send = messaging.new_message("longitudinalPlan")
|
|
plan_send.valid = sm.all_checks(service_list=["carState", "controlsState", "selfdriveState", "radarState"])
|
|
|
|
plan = plan_send.longitudinalPlan
|
|
plan.modelMonoTime = sm.logMonoTime["modelV2"]
|
|
plan.processingDelay = (plan_send.logMonoTime / 1e9) - sm.logMonoTime["modelV2"]
|
|
plan.solverExecutionTime = self.mpc.solve_time
|
|
plan.speeds = self.v_desired_trajectory.tolist()
|
|
plan.accels = self.a_desired_trajectory.tolist()
|
|
plan.jerks = self.j_desired_trajectory.tolist()
|
|
plan.hasLead = any(lead.status for lead in (sm["radarState"].leadOne, sm["radarState"].leadTwo))
|
|
plan.longitudinalPlanSource = self.plan_source
|
|
plan.fcw = self.fcw
|
|
plan.aTarget = float(self.output_a_target)
|
|
|
|
force_stop_handoff = bool(
|
|
sm["starpilotPlan"].forcingStop and (
|
|
sm["starpilotPlan"].forcingStopLength < 1.0 or
|
|
sm["starpilotPlan"].vCruise <= FORCE_STOP_HANDOFF_MAX_VCRUISE
|
|
)
|
|
)
|
|
plan.shouldStop = bool(self.output_should_stop or force_stop_handoff)
|
|
plan.allowBrake = True
|
|
plan.allowThrottle = bool(self.allow_throttle)
|
|
pm.send("longitudinalPlan", plan_send)
|