@ -1,7 +1,7 @@
from cereal import car
from openpilot . common . numpy_fast import clip , interp
from openpilot . common . numpy_fast import clip
from openpilot . common . realtime import DT_CTRL
from openpilot . selfdrive . controls . lib . drive_helpers import CONTROL_N , apply_deadzone
from openpilot . selfdrive . controls . lib . drive_helpers import CONTROL_N
from openpilot . selfdrive . controls . lib . pid import PIDController
from openpilot . selfdrive . modeld . constants import ModelConstants
@ -10,18 +10,10 @@ 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 , v_target ,
v_target_1sec , brake_pressed , cruise_standstill ) :
accelerating = v_target_1sec > v_target
planned_stop = ( v_target < CP . vEgoStopping and
v_target_1sec < CP . vEgoStopping and
not accelerating )
stay_stopped = ( v_ego < CP . vEgoStopping and
( brake_pressed or cruise_standstill ) )
stopping_condition = planned_stop or stay_stopped
starting_condition = ( v_target_1sec > CP . vEgoStarting and
accelerating and
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
@ -34,7 +26,6 @@ def long_control_state_trans(CP, active, long_control_state, v_ego, v_target,
long_control_state = LongCtrlState . pid
if stopping_condition :
long_control_state = LongCtrlState . stopping
elif long_control_state == LongCtrlState . stopping :
if starting_condition and CP . startingState :
long_control_state = LongCtrlState . starting
@ -49,78 +40,45 @@ def long_control_state_trans(CP, active, long_control_state, v_ego, v_target,
return long_control_state
class LongControl :
def __init__ ( self , CP ) :
self . CP = CP
self . long_control_state = LongCtrlState . off # initialized to off
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 . v_pid = 0.0
self . last_output_accel = 0.0
def reset ( self , v_pid ) :
""" Reset PID controller and change setpoint """
def reset ( self ) :
self . pid . reset ( )
self . v_pid = v_pid
def update ( self , active , CS , long_plan , accel_limits , t_since_plan ) :
def update ( self , active , CS , a_target , should_stop , accel_limits ) :
""" Update longitudinal control. This updates the state machine and runs a PID loop """
# Interp control trajectory
speeds = long_plan . speeds
if len ( speeds ) == CONTROL_N :
v_target_now = interp ( t_since_plan , CONTROL_N_T_IDX , speeds )
a_target_now = interp ( t_since_plan , CONTROL_N_T_IDX , long_plan . accels )
v_target = interp ( self . CP . longitudinalActuatorDelay + t_since_plan , CONTROL_N_T_IDX , speeds )
a_target = 2 * ( v_target - v_target_now ) / self . CP . longitudinalActuatorDelay - a_target_now
v_target_1sec = interp ( self . CP . longitudinalActuatorDelay + t_since_plan + 1.0 , CONTROL_N_T_IDX , speeds )
else :
v_target = 0.0
v_target_now = 0.0
v_target_1sec = 0.0
a_target = 0.0
self . pid . neg_limit = accel_limits [ 0 ]
self . pid . pos_limit = accel_limits [ 1 ]
output_accel = self . last_output_accel
self . long_control_state = long_control_state_trans ( self . CP , active , self . long_control_state , CS . vEgo ,
v_target , v_target_1sec , CS . brakePressed ,
should_stop , CS . brakePressed ,
CS . cruiseState . standstill )
if self . long_control_state == LongCtrlState . off :
self . reset ( CS . vEgo )
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 ( CS . vEgo )
self . reset ( )
elif self . long_control_state == LongCtrlState . starting :
output_accel = self . CP . startAccel
self . reset ( CS . vEgo )
elif self . long_control_state == LongCtrlState . pid :
self . v_pid = v_target_now
self . reset ( )
# Toyota starts braking more when it thinks you want to stop
# Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration
# TODO too complex, needs to be simplified and tested on toyotas
prevent_overshoot = not self . CP . stoppingControl and CS . vEgo < 1.5 and v_target_1sec < 0.7 and v_target_1sec < self . v_pid
deadzone = interp ( CS . vEgo , self . CP . longitudinalTuning . deadzoneBP , self . CP . longitudinalTuning . deadzoneV )
freeze_integrator = prevent_overshoot
error = self . v_pid - CS . vEgo
error_deadzone = apply_deadzone ( error , deadzone )
output_accel = self . pid . update ( error_deadzone , speed = CS . vEgo ,
feedforward = a_target ,
freeze_integrator = freeze_integrator )
else : # LongCtrlState.pid
error = a_target - CS . aEgo
output_accel = self . pid . update ( error , speed = CS . vEgo ,
feedforward = a_target )
self . last_output_accel = clip ( output_accel , accel_limits [ 0 ] , accel_limits [ 1 ] )
return self . last_output_accel