from cereal import car
from common . numpy_fast import clip , interp
from opendbc . can . packer import CANPacker
from selfdrive . car . ford import fordcan
from selfdrive . car . ford . values import CarControllerParams
VisualAlert = car . CarControl . HUDControl . VisualAlert
def apply_ford_steer_angle_limits ( apply_steer , apply_steer_last , vEgo ) :
# rate limit
steer_up = apply_steer * apply_steer_last > 0. and abs ( apply_steer ) > abs ( apply_steer_last )
rate_limit = CarControllerParams . STEER_RATE_LIMIT_UP if steer_up else CarControllerParams . STEER_RATE_LIMIT_DOWN
max_angle_diff = interp ( vEgo , rate_limit . speed_points , rate_limit . max_angle_diff_points )
apply_steer = clip ( apply_steer , ( apply_steer_last - max_angle_diff ) , ( apply_steer_last + max_angle_diff ) )
return apply_steer
class CarController :
def __init__ ( self , dbc_name , CP , VM ) :
self . CP = CP
self . VM = VM
self . packer = CANPacker ( dbc_name )
self . frame = 0
self . apply_steer_last = 0
self . main_on_last = False
self . lkas_enabled_last = False
self . steer_alert_last = False
def update ( self , CC , CS ) :
can_sends = [ ]
actuators = CC . actuators
hud_control = CC . hudControl
main_on = CS . out . cruiseState . available
steer_alert = hud_control . visualAlert in ( VisualAlert . steerRequired , VisualAlert . ldw )
if CC . cruiseControl . cancel :
# cancel stock ACC
can_sends . append ( fordcan . spam_cancel_button ( self . packer ) )
# apply rate limits
new_steer = actuators . steeringAngleDeg
apply_steer = apply_ford_steer_angle_limits ( new_steer , self . apply_steer_last , CS . out . vEgo )
# send steering commands at 20Hz
if ( self . frame % CarControllerParams . LKAS_STEER_STEP ) == 0 :
lca_rq = 1 if CC . latActive else 0
# use LatCtlPath_An_Actl to actuate steering for now until curvature control is implemented
path_angle = apply_steer
# convert steer angle to curvature
curvature = self . VM . calc_curvature ( apply_steer , CS . out . vEgo , 0.0 )
# TODO: get other actuators
curvature_rate = 0
path_offset = 0
ramp_type = 3 # 0=Slow, 1=Medium, 2=Fast, 3=Immediately
precision = 0 # 0=Comfortable, 1=Precise
self . apply_steer_last = apply_steer
can_sends . append ( fordcan . create_lkas_command ( self . packer , apply_steer , curvature ) )
can_sends . append ( fordcan . create_tja_command ( self . packer , lca_rq , ramp_type , precision ,
path_offset , path_angle , curvature_rate , curvature ) )
send_ui = ( self . main_on_last != main_on ) or ( self . lkas_enabled_last != CC . latActive ) or ( self . steer_alert_last != steer_alert )
# send lkas ui command at 1Hz or if ui state changes
if ( self . frame % CarControllerParams . LKAS_UI_STEP ) == 0 or send_ui :
can_sends . append ( fordcan . create_lkas_ui_command ( self . packer , main_on , CC . latActive , steer_alert , CS . lkas_status_stock_values ) )
# send acc ui command at 20Hz or if ui state changes
if ( self . frame % CarControllerParams . ACC_UI_STEP ) == 0 or send_ui :
can_sends . append ( fordcan . create_acc_ui_command ( self . packer , main_on , CC . latActive , CS . acc_tja_status_stock_values ) )
self . main_on_last = main_on
self . lkas_enabled_last = CC . latActive
self . steer_alert_last = steer_alert
new_actuators = actuators . copy ( )
new_actuators . steeringAngleDeg = apply_steer
self . frame + = 1
return new_actuators , can_sends