You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
7.0 KiB
161 lines
7.0 KiB
from cereal import car
|
|
from common.numpy_fast import clip
|
|
from selfdrive.car import apply_toyota_steer_torque_limits, create_gas_command, make_can_msg
|
|
from selfdrive.car.toyota.toyotacan import create_steer_command, create_ui_command, \
|
|
create_accel_command, create_acc_cancel_command, \
|
|
create_fcw_command, create_lta_steer_command
|
|
from selfdrive.car.toyota.values import Ecu, CAR, STATIC_MSGS, NO_STOP_TIMER_CAR, TSS2_CAR, \
|
|
MIN_ACC_SPEED, PEDAL_HYST_GAP, CarControllerParams
|
|
from opendbc.can.packer import CANPacker
|
|
|
|
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
|
|
|
|
|
def accel_hysteresis(accel, accel_steady, enabled):
|
|
|
|
# for small accel oscillations within ACCEL_HYST_GAP, don't change the accel command
|
|
if not enabled:
|
|
# send 0 when disabled, otherwise acc faults
|
|
accel_steady = 0.
|
|
elif accel > accel_steady + CarControllerParams.ACCEL_HYST_GAP:
|
|
accel_steady = accel - CarControllerParams.ACCEL_HYST_GAP
|
|
elif accel < accel_steady - CarControllerParams.ACCEL_HYST_GAP:
|
|
accel_steady = accel + CarControllerParams.ACCEL_HYST_GAP
|
|
accel = accel_steady
|
|
|
|
return accel, accel_steady
|
|
|
|
|
|
class CarController():
|
|
def __init__(self, dbc_name, CP, VM):
|
|
self.last_steer = 0
|
|
self.accel_steady = 0.
|
|
self.alert_active = False
|
|
self.last_standstill = False
|
|
self.standstill_req = False
|
|
self.steer_rate_limited = False
|
|
self.use_interceptor = False
|
|
|
|
self.fake_ecus = set()
|
|
if CP.enableCamera:
|
|
self.fake_ecus.add(Ecu.fwdCamera)
|
|
if CP.enableDsu:
|
|
self.fake_ecus.add(Ecu.dsu)
|
|
|
|
self.packer = CANPacker(dbc_name)
|
|
|
|
def update(self, enabled, CS, frame, actuators, pcm_cancel_cmd, hud_alert,
|
|
left_line, right_line, lead, left_lane_depart, right_lane_depart):
|
|
|
|
# *** compute control surfaces ***
|
|
|
|
# gas and brake
|
|
interceptor_gas_cmd = 0.
|
|
pcm_accel_cmd = actuators.gas - actuators.brake
|
|
|
|
if CS.CP.enableGasInterceptor:
|
|
# handle hysteresis when around the minimum acc speed
|
|
if CS.out.vEgo < MIN_ACC_SPEED:
|
|
self.use_interceptor = True
|
|
elif CS.out.vEgo > MIN_ACC_SPEED + PEDAL_HYST_GAP:
|
|
self.use_interceptor = False
|
|
|
|
if self.use_interceptor and enabled:
|
|
# only send negative accel when using interceptor. gas handles acceleration
|
|
# +0.06 offset to reduce ABS pump usage when OP is engaged
|
|
interceptor_gas_cmd = clip(actuators.gas, 0., 1.)
|
|
pcm_accel_cmd = 0.06 - actuators.brake
|
|
|
|
pcm_accel_cmd, self.accel_steady = accel_hysteresis(pcm_accel_cmd, self.accel_steady, enabled)
|
|
pcm_accel_cmd = clip(pcm_accel_cmd * CarControllerParams.ACCEL_SCALE, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
|
|
|
|
# steer torque
|
|
new_steer = int(round(actuators.steer * CarControllerParams.STEER_MAX))
|
|
apply_steer = apply_toyota_steer_torque_limits(new_steer, self.last_steer, CS.out.steeringTorqueEps, CarControllerParams)
|
|
self.steer_rate_limited = new_steer != apply_steer
|
|
|
|
# Cut steering while we're in a known fault state (2s)
|
|
if not enabled or CS.steer_state in [9, 25]:
|
|
apply_steer = 0
|
|
apply_steer_req = 0
|
|
else:
|
|
apply_steer_req = 1
|
|
|
|
if not enabled and CS.pcm_acc_status:
|
|
# send pcm acc cancel cmd if drive is disabled but pcm is still on, or if the system can't be activated
|
|
pcm_cancel_cmd = 1
|
|
|
|
# on entering standstill, send standstill request
|
|
if CS.out.standstill and not self.last_standstill and CS.CP.carFingerprint not in NO_STOP_TIMER_CAR:
|
|
self.standstill_req = True
|
|
if CS.pcm_acc_status != 8:
|
|
# pcm entered standstill or it's disabled
|
|
self.standstill_req = False
|
|
|
|
self.last_steer = apply_steer
|
|
self.last_accel = pcm_accel_cmd
|
|
self.last_standstill = CS.out.standstill
|
|
|
|
can_sends = []
|
|
|
|
#*** control msgs ***
|
|
#print("steer {0} {1} {2} {3}".format(apply_steer, min_lim, max_lim, CS.steer_torque_motor)
|
|
|
|
# toyota can trace shows this message at 42Hz, with counter adding alternatively 1 and 2;
|
|
# sending it at 100Hz seem to allow a higher rate limit, as the rate limit seems imposed
|
|
# on consecutive messages
|
|
if Ecu.fwdCamera in self.fake_ecus:
|
|
can_sends.append(create_steer_command(self.packer, apply_steer, apply_steer_req, frame))
|
|
if frame % 2 == 0 and CS.CP.carFingerprint in TSS2_CAR:
|
|
can_sends.append(create_lta_steer_command(self.packer, 0, 0, frame // 2))
|
|
|
|
# LTA mode. Set ret.steerControlType = car.CarParams.SteerControlType.angle and whitelist 0x191 in the panda
|
|
# if frame % 2 == 0:
|
|
# can_sends.append(create_steer_command(self.packer, 0, 0, frame // 2))
|
|
# can_sends.append(create_lta_steer_command(self.packer, actuators.steeringAngleDeg, apply_steer_req, frame // 2))
|
|
|
|
# we can spam can to cancel the system even if we are using lat only control
|
|
if (frame % 3 == 0 and CS.CP.openpilotLongitudinalControl) or (pcm_cancel_cmd and Ecu.fwdCamera in self.fake_ecus):
|
|
lead = lead or CS.out.vEgo < 12. # at low speed we always assume the lead is present do ACC can be engaged
|
|
|
|
# Lexus IS uses a different cancellation message
|
|
if pcm_cancel_cmd and CS.CP.carFingerprint == CAR.LEXUS_IS:
|
|
can_sends.append(create_acc_cancel_command(self.packer))
|
|
elif CS.CP.openpilotLongitudinalControl:
|
|
can_sends.append(create_accel_command(self.packer, pcm_accel_cmd, pcm_cancel_cmd, self.standstill_req, lead))
|
|
else:
|
|
can_sends.append(create_accel_command(self.packer, 0, pcm_cancel_cmd, False, lead))
|
|
|
|
if frame % 2 == 0 and CS.CP.enableGasInterceptor:
|
|
# send exactly zero if gas cmd is zero. Interceptor will send the max between read value and gas cmd.
|
|
# This prevents unexpected pedal range rescaling
|
|
can_sends.append(create_gas_command(self.packer, interceptor_gas_cmd, frame // 2))
|
|
|
|
# ui mesg is at 100Hz but we send asap if:
|
|
# - there is something to display
|
|
# - there is something to stop displaying
|
|
fcw_alert = hud_alert == VisualAlert.fcw
|
|
steer_alert = hud_alert == VisualAlert.steerRequired
|
|
|
|
send_ui = False
|
|
if ((fcw_alert or steer_alert) and not self.alert_active) or \
|
|
(not (fcw_alert or steer_alert) and self.alert_active):
|
|
send_ui = True
|
|
self.alert_active = not self.alert_active
|
|
elif pcm_cancel_cmd:
|
|
# forcing the pcm to disengage causes a bad fault sound so play a good sound instead
|
|
send_ui = True
|
|
|
|
if (frame % 100 == 0 or send_ui) and Ecu.fwdCamera in self.fake_ecus:
|
|
can_sends.append(create_ui_command(self.packer, steer_alert, pcm_cancel_cmd, left_line, right_line, left_lane_depart, right_lane_depart))
|
|
|
|
if frame % 100 == 0 and Ecu.dsu in self.fake_ecus:
|
|
can_sends.append(create_fcw_command(self.packer, fcw_alert))
|
|
|
|
#*** static msgs ***
|
|
|
|
for (addr, ecu, cars, bus, fr_step, vl) in STATIC_MSGS:
|
|
if frame % fr_step == 0 and ecu in self.fake_ecus and CS.CP.carFingerprint in cars:
|
|
can_sends.append(make_can_msg(addr, vl, bus))
|
|
|
|
return can_sends
|
|
|