from cereal import car
from common . numpy_fast import clip , interp
from opendbc . can . packer import CANPacker
from selfdrive . car . nissan import nissancan
from selfdrive . car . nissan . values import CAR , CarControllerParams
VisualAlert = car . CarControl . HUDControl . VisualAlert
class CarController :
def __init__ ( self , dbc_name , CP , VM ) :
self . CP = CP
self . car_fingerprint = CP . carFingerprint
self . frame = 0
self . lkas_max_torque = 0
self . last_angle = 0
self . packer = CANPacker ( dbc_name )
def update ( self , CC , CS ) :
actuators = CC . actuators
hud_control = CC . hudControl
pcm_cancel_cmd = CC . cruiseControl . cancel
can_sends = [ ]
### STEER ###
lkas_hud_msg = CS . lkas_hud_msg
lkas_hud_info_msg = CS . lkas_hud_info_msg
apply_angle = actuators . steeringAngleDeg
steer_hud_alert = 1 if hud_control . visualAlert in ( VisualAlert . steerRequired , VisualAlert . ldw ) else 0
if CC . latActive :
# windup slower
if self . last_angle * apply_angle > 0. and abs ( apply_angle ) > abs ( self . last_angle ) :
angle_rate_lim = interp ( CS . out . vEgo , CarControllerParams . ANGLE_DELTA_BP , CarControllerParams . ANGLE_DELTA_V )
else :
angle_rate_lim = interp ( CS . out . vEgo , CarControllerParams . ANGLE_DELTA_BP , CarControllerParams . ANGLE_DELTA_VU )
apply_angle = clip ( apply_angle , self . last_angle - angle_rate_lim , self . last_angle + angle_rate_lim )
# Max torque from driver before EPS will give up and not apply torque
if not bool ( CS . out . steeringPressed ) :
self . lkas_max_torque = CarControllerParams . LKAS_MAX_TORQUE
else :
# Scale max torque based on how much torque the driver is applying to the wheel
self . lkas_max_torque = max (
# Scale max torque down to half LKAX_MAX_TORQUE as a minimum
CarControllerParams . LKAS_MAX_TORQUE * 0.5 ,
# Start scaling torque at STEER_THRESHOLD
CarControllerParams . LKAS_MAX_TORQUE - 0.6 * max ( 0 , abs ( CS . out . steeringTorque ) - CarControllerParams . STEER_THRESHOLD )
)
else :
apply_angle = CS . out . steeringAngleDeg
self . lkas_max_torque = 0
self . last_angle = apply_angle
if self . CP . carFingerprint in ( CAR . ROGUE , CAR . XTRAIL , CAR . ALTIMA ) and pcm_cancel_cmd :
can_sends . append ( nissancan . create_acc_cancel_cmd ( self . packer , self . car_fingerprint , CS . cruise_throttle_msg ) )
# TODO: Find better way to cancel!
# For some reason spamming the cancel button is unreliable on the Leaf
# We now cancel by making propilot think the seatbelt is unlatched,
# this generates a beep and a warning message every time you disengage
if self . CP . carFingerprint in ( CAR . LEAF , CAR . LEAF_IC ) and self . frame % 2 == 0 :
can_sends . append ( nissancan . create_cancel_msg ( self . packer , CS . cancel_msg , pcm_cancel_cmd ) )
can_sends . append ( nissancan . create_steering_control (
self . packer , apply_angle , self . frame , CC . enabled , self . lkas_max_torque ) )
if lkas_hud_msg and lkas_hud_info_msg :
if self . frame % 2 == 0 :
can_sends . append ( nissancan . create_lkas_hud_msg (
self . packer , lkas_hud_msg , CC . enabled , hud_control . leftLaneVisible , hud_control . rightLaneVisible , hud_control . leftLaneDepart , hud_control . rightLaneDepart ) )
if self . frame % 50 == 0 :
can_sends . append ( nissancan . create_lkas_hud_info_msg (
self . packer , lkas_hud_info_msg , steer_hud_alert
) )
new_actuators = actuators . copy ( )
new_actuators . steeringAngleDeg = apply_angle
self . frame + = 1
return new_actuators , can_sends