import numpy as np
from cereal import car
from openpilot . common . realtime import DT_CTRL
from openpilot . selfdrive . controls . lib . drive_helpers import CONTROL_N
from openpilot . common . pid import PIDController
from openpilot . selfdrive . modeld . constants import ModelConstants
CONTROL_N_T_IDX = ModelConstants . T_IDXS [ : CONTROL_N ]
LongCtrlState = car . CarControl . Actuators . LongControlState
def long_control_state_trans ( CP , active , long_control_state , v_ego ,
should_stop , brake_pressed , cruise_standstill ) :
stopping_condition = should_stop
starting_condition = ( not should_stop and
not cruise_standstill and
not brake_pressed )
started_condition = v_ego > CP . vEgoStarting
if not active :
long_control_state = LongCtrlState . off
else :
if long_control_state == LongCtrlState . off :
if not starting_condition :
long_control_state = LongCtrlState . stopping
else :
if starting_condition and CP . startingState :
long_control_state = LongCtrlState . starting
else :
long_control_state = LongCtrlState . pid
elif long_control_state == LongCtrlState . stopping :
if starting_condition and CP . startingState :
long_control_state = LongCtrlState . starting
elif starting_condition :
long_control_state = LongCtrlState . pid
elif long_control_state in [ LongCtrlState . starting , LongCtrlState . pid ] :
if stopping_condition :
long_control_state = LongCtrlState . stopping
elif started_condition :
long_control_state = LongCtrlState . pid
return long_control_state
class LongControl :
def __init__ ( self , CP ) :
self . CP = CP
self . long_control_state = LongCtrlState . off
self . pid = PIDController ( ( CP . longitudinalTuning . kpBP , CP . longitudinalTuning . kpV ) ,
( CP . longitudinalTuning . kiBP , CP . longitudinalTuning . kiV ) ,
k_f = CP . longitudinalTuning . kf , rate = 1 / DT_CTRL )
self . last_output_accel = 0.0
def reset ( self ) :
self . pid . reset ( )
def update ( self , active , CS , a_target , should_stop , accel_limits ) :
""" Update longitudinal control. This updates the state machine and runs a PID loop """
self . pid . neg_limit = accel_limits [ 0 ]
self . pid . pos_limit = accel_limits [ 1 ]
self . long_control_state = long_control_state_trans ( self . CP , active , self . long_control_state , CS . vEgo ,
should_stop , CS . brakePressed ,
CS . cruiseState . standstill )
if self . long_control_state == LongCtrlState . off :
self . reset ( )
output_accel = 0.
elif self . long_control_state == LongCtrlState . stopping :
output_accel = self . last_output_accel
if output_accel > self . CP . stopAccel :
output_accel = min ( output_accel , 0.0 )
output_accel - = self . CP . stoppingDecelRate * DT_CTRL
self . reset ( )
elif self . long_control_state == LongCtrlState . starting :
output_accel = self . CP . startAccel
self . reset ( )
else : # LongCtrlState.pid
error = a_target - CS . aEgo
output_accel = self . pid . update ( error , speed = CS . vEgo ,
feedforward = a_target )
self . last_output_accel = np . clip ( output_accel , accel_limits [ 0 ] , accel_limits [ 1 ] )
return self . last_output_accel