openpilot v0.9.6 release

date: 2024-01-12T10:13:37
master commit: ba792d576a49a0899b88a753fa1c52956bedf9e6
This commit is contained in:
FrogAi
2024-01-12 22:39:28 -07:00
commit 08e9fb1edc
1881 changed files with 653708 additions and 0 deletions

View File

View File

@@ -0,0 +1,144 @@
from openpilot.common.numpy_fast import clip, interp
from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance
from openpilot.selfdrive.car.subaru import subarucan
from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_ES_ADDR, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, STEER_RATE_LIMITED, \
CanBus, CarControllerParams, SubaruFlags
# FIXME: These limits aren't exact. The real limit is more than likely over a larger time period and
# involves the total steering angle change rather than rate, but these limits work well for now
MAX_STEER_RATE = 25 # deg/s
MAX_STEER_RATE_FRAMES = 7 # tx control frames needed before torque can be cut
class CarController:
def __init__(self, dbc_name, CP, VM):
self.CP = CP
self.apply_steer_last = 0
self.frame = 0
self.cruise_button_prev = 0
self.steer_rate_counter = 0
self.p = CarControllerParams(CP)
self.packer = CANPacker(DBC[CP.carFingerprint]['pt'])
def update(self, CC, CS, now_nanos):
actuators = CC.actuators
hud_control = CC.hudControl
pcm_cancel_cmd = CC.cruiseControl.cancel
can_sends = []
# *** steering ***
if (self.frame % self.p.STEER_STEP) == 0:
apply_steer = int(round(actuators.steer * self.p.STEER_MAX))
# limits due to driver torque
new_steer = int(round(apply_steer))
apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.p)
if not CC.latActive:
apply_steer = 0
if self.CP.carFingerprint in PREGLOBAL_CARS:
can_sends.append(subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_steer, CC.latActive))
else:
apply_steer_req = CC.latActive
if self.CP.carFingerprint in STEER_RATE_LIMITED:
# Steering rate fault prevention
self.steer_rate_counter, apply_steer_req = \
common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req,
self.steer_rate_counter, MAX_STEER_RATE_FRAMES)
can_sends.append(subarucan.create_steering_control(self.packer, apply_steer, apply_steer_req))
self.apply_steer_last = apply_steer
# *** longitudinal ***
if CC.longActive:
apply_throttle = int(round(interp(actuators.accel, CarControllerParams.THROTTLE_LOOKUP_BP, CarControllerParams.THROTTLE_LOOKUP_V)))
apply_rpm = int(round(interp(actuators.accel, CarControllerParams.RPM_LOOKUP_BP, CarControllerParams.RPM_LOOKUP_V)))
apply_brake = int(round(interp(actuators.accel, CarControllerParams.BRAKE_LOOKUP_BP, CarControllerParams.BRAKE_LOOKUP_V)))
# limit min and max values
cruise_throttle = clip(apply_throttle, CarControllerParams.THROTTLE_MIN, CarControllerParams.THROTTLE_MAX)
cruise_rpm = clip(apply_rpm, CarControllerParams.RPM_MIN, CarControllerParams.RPM_MAX)
cruise_brake = clip(apply_brake, CarControllerParams.BRAKE_MIN, CarControllerParams.BRAKE_MAX)
else:
cruise_throttle = CarControllerParams.THROTTLE_INACTIVE
cruise_rpm = CarControllerParams.RPM_MIN
cruise_brake = CarControllerParams.BRAKE_MIN
# *** alerts and pcm cancel ***
if self.CP.carFingerprint in PREGLOBAL_CARS:
if self.frame % 5 == 0:
# 1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 = resume deep
# disengage ACC when OP is disengaged
if pcm_cancel_cmd:
cruise_button = 1
# turn main on if off and past start-up state
elif not CS.out.cruiseState.available and CS.ready:
cruise_button = 1
else:
cruise_button = CS.cruise_button
# unstick previous mocked button press
if cruise_button == 1 and self.cruise_button_prev == 1:
cruise_button = 0
self.cruise_button_prev = cruise_button
can_sends.append(subarucan.create_preglobal_es_distance(self.packer, cruise_button, CS.es_distance_msg))
else:
if self.frame % 10 == 0:
can_sends.append(subarucan.create_es_dashstatus(self.packer, self.frame // 10, CS.es_dashstatus_msg, CC.enabled,
self.CP.openpilotLongitudinalControl, CC.longActive, hud_control.leadVisible))
can_sends.append(subarucan.create_es_lkas_state(self.packer, self.frame // 10, CS.es_lkas_state_msg, CC.enabled, hud_control.visualAlert,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
hud_control.leftLaneDepart, hud_control.rightLaneDepart))
if self.CP.flags & SubaruFlags.SEND_INFOTAINMENT:
can_sends.append(subarucan.create_es_infotainment(self.packer, self.frame // 10, CS.es_infotainment_msg, hud_control.visualAlert))
if self.CP.openpilotLongitudinalControl:
if self.frame % 5 == 0:
can_sends.append(subarucan.create_es_status(self.packer, self.frame // 5, CS.es_status_msg,
self.CP.openpilotLongitudinalControl, CC.longActive, cruise_rpm))
can_sends.append(subarucan.create_es_brake(self.packer, self.frame // 5, CS.es_brake_msg,
self.CP.openpilotLongitudinalControl, CC.longActive, cruise_brake))
can_sends.append(subarucan.create_es_distance(self.packer, self.frame // 5, CS.es_distance_msg, 0, pcm_cancel_cmd,
self.CP.openpilotLongitudinalControl, cruise_brake > 0, cruise_throttle))
else:
if pcm_cancel_cmd:
if self.CP.carFingerprint not in HYBRID_CARS:
bus = CanBus.alt if self.CP.carFingerprint in GLOBAL_GEN2 else CanBus.main
can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg["COUNTER"] + 1, CS.es_distance_msg, bus, pcm_cancel_cmd))
if self.CP.flags & SubaruFlags.DISABLE_EYESIGHT:
# Tester present (keeps eyesight disabled)
if self.frame % 100 == 0:
can_sends.append([GLOBAL_ES_ADDR, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", CanBus.camera])
# Create all of the other eyesight messages to keep the rest of the car happy when eyesight is disabled
if self.frame % 5 == 0:
can_sends.append(subarucan.create_es_highbeamassist(self.packer))
if self.frame % 10 == 0:
can_sends.append(subarucan.create_es_static_1(self.packer))
if self.frame % 2 == 0:
can_sends.append(subarucan.create_es_static_2(self.packer))
new_actuators = actuators.copy()
new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX
new_actuators.steerOutputCan = self.apply_steer_last
self.frame += 1
return new_actuators, can_sends

View File

@@ -0,0 +1,220 @@
import copy
from cereal import car
from opendbc.can.can_define import CANDefine
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car.interfaces import CarStateBase
from opendbc.can.parser import CANParser
from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, CanBus, SubaruFlags
from openpilot.selfdrive.car import CanSignalRateCalculator
class CarState(CarStateBase):
def __init__(self, CP):
super().__init__(CP)
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
self.shifter_values = can_define.dv["Transmission"]["Gear"]
self.angle_rate_calulator = CanSignalRateCalculator(50)
def update(self, cp, cp_cam, cp_body):
ret = car.CarState.new_message()
throttle_msg = cp.vl["Throttle"] if self.car_fingerprint not in HYBRID_CARS else cp_body.vl["Throttle_Hybrid"]
ret.gas = throttle_msg["Throttle_Pedal"] / 255.
ret.gasPressed = ret.gas > 1e-5
if self.car_fingerprint in PREGLOBAL_CARS:
ret.brakePressed = cp.vl["Brake_Pedal"]["Brake_Pedal"] > 0
else:
cp_brakes = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp
ret.brakePressed = cp_brakes.vl["Brake_Status"]["Brake"] == 1
cp_wheels = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp
ret.wheelSpeeds = self.get_wheel_speeds(
cp_wheels.vl["Wheel_Speeds"]["FL"],
cp_wheels.vl["Wheel_Speeds"]["FR"],
cp_wheels.vl["Wheel_Speeds"]["RL"],
cp_wheels.vl["Wheel_Speeds"]["RR"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.vEgoRaw == 0
# continuous blinker signals for assisted lane change
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["Dashlights"]["LEFT_BLINKER"],
cp.vl["Dashlights"]["RIGHT_BLINKER"])
if self.CP.enableBsm:
ret.leftBlindspot = (cp.vl["BSD_RCTA"]["L_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["L_APPROACHING"] == 1)
ret.rightBlindspot = (cp.vl["BSD_RCTA"]["R_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["R_APPROACHING"] == 1)
cp_transmission = cp_body if self.car_fingerprint in HYBRID_CARS else cp
can_gear = int(cp_transmission.vl["Transmission"]["Gear"])
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None))
ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"]
if self.car_fingerprint not in PREGLOBAL_CARS:
# ideally we get this from the car, but unclear if it exists. diagnostic software doesn't even have it
ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, cp.vl["Steering_Torque"]["COUNTER"])
ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"]
ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"]
steer_threshold = 75 if self.CP.carFingerprint in PREGLOBAL_CARS else 80
ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold
cp_cruise = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp
if self.car_fingerprint in HYBRID_CARS:
ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0
ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0
else:
ret.cruiseState.enabled = cp_cruise.vl["CruiseControl"]["Cruise_Activated"] != 0
ret.cruiseState.available = cp_cruise.vl["CruiseControl"]["Cruise_On"] != 0
ret.cruiseState.speed = cp_cam.vl["ES_DashStatus"]["Cruise_Set_Speed"] * CV.KPH_TO_MS
if (self.car_fingerprint in PREGLOBAL_CARS and cp.vl["Dash_State2"]["UNITS"] == 1) or \
(self.car_fingerprint not in PREGLOBAL_CARS and cp.vl["Dashlights"]["UNITS"] == 1):
ret.cruiseState.speed *= CV.MPH_TO_KPH
ret.seatbeltUnlatched = cp.vl["Dashlights"]["SEATBELT_FL"] == 1
ret.doorOpen = any([cp.vl["BodyInfo"]["DOOR_OPEN_RR"],
cp.vl["BodyInfo"]["DOOR_OPEN_RL"],
cp.vl["BodyInfo"]["DOOR_OPEN_FR"],
cp.vl["BodyInfo"]["DOOR_OPEN_FL"]])
ret.steerFaultPermanent = cp.vl["Steering_Torque"]["Steer_Error_1"] == 1
cp_es_distance = cp_body if self.car_fingerprint in (GLOBAL_GEN2 | HYBRID_CARS) else cp_cam
if self.car_fingerprint in PREGLOBAL_CARS:
self.cruise_button = cp_cam.vl["ES_Distance"]["Cruise_Button"]
self.ready = not cp_cam.vl["ES_DashStatus"]["Not_Ready_Startup"]
else:
ret.steerFaultTemporary = cp.vl["Steering_Torque"]["Steer_Warning"] == 1
ret.cruiseState.nonAdaptive = cp_cam.vl["ES_DashStatus"]["Conventional_Cruise"] == 1
ret.cruiseState.standstill = cp_cam.vl["ES_DashStatus"]["Cruise_State"] == 3
ret.stockFcw = (cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 1) or \
(cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2)
self.es_lkas_state_msg = copy.copy(cp_cam.vl["ES_LKAS_State"])
cp_es_brake = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam
self.es_brake_msg = copy.copy(cp_es_brake.vl["ES_Brake"])
cp_es_status = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam
# TODO: Hybrid cars don't have ES_Distance, need a replacement
if self.car_fingerprint not in HYBRID_CARS:
# 8 is known AEB, there are a few other values related to AEB we ignore
ret.stockAeb = (cp_es_distance.vl["ES_Brake"]["AEB_Status"] == 8) and \
(cp_es_distance.vl["ES_Brake"]["Brake_Pressure"] != 0)
self.es_status_msg = copy.copy(cp_es_status.vl["ES_Status"])
self.cruise_control_msg = copy.copy(cp_cruise.vl["CruiseControl"])
if self.car_fingerprint not in HYBRID_CARS:
self.es_distance_msg = copy.copy(cp_es_distance.vl["ES_Distance"])
self.es_dashstatus_msg = copy.copy(cp_cam.vl["ES_DashStatus"])
if self.CP.flags & SubaruFlags.SEND_INFOTAINMENT:
self.es_infotainment_msg = copy.copy(cp_cam.vl["ES_Infotainment"])
return ret
@staticmethod
def get_common_global_body_messages(CP):
messages = [
("Wheel_Speeds", 50),
("Brake_Status", 50),
]
if CP.carFingerprint not in HYBRID_CARS:
messages.append(("CruiseControl", 20))
return messages
@staticmethod
def get_common_global_es_messages(CP):
messages = [
("ES_Brake", 20),
]
if CP.carFingerprint not in HYBRID_CARS:
messages += [
("ES_Distance", 20),
("ES_Status", 20)
]
return messages
@staticmethod
def get_common_preglobal_body_messages():
messages = [
("CruiseControl", 50),
("Wheel_Speeds", 50),
("Dash_State2", 1),
]
return messages
@staticmethod
def get_can_parser(CP):
messages = [
# sig_address, frequency
("Dashlights", 10),
("Steering_Torque", 50),
("BodyInfo", 1),
("Brake_Pedal", 50),
]
if CP.carFingerprint not in HYBRID_CARS:
messages += [
("Throttle", 100),
("Transmission", 100)
]
if CP.enableBsm:
messages.append(("BSD_RCTA", 17))
if CP.carFingerprint not in PREGLOBAL_CARS:
if CP.carFingerprint not in GLOBAL_GEN2:
messages += CarState.get_common_global_body_messages(CP)
else:
messages += CarState.get_common_preglobal_body_messages()
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus.main)
@staticmethod
def get_cam_can_parser(CP):
if CP.carFingerprint in PREGLOBAL_CARS:
messages = [
("ES_DashStatus", 20),
("ES_Distance", 20),
]
else:
messages = [
("ES_DashStatus", 10),
("ES_LKAS_State", 10),
]
if CP.carFingerprint not in GLOBAL_GEN2:
messages += CarState.get_common_global_es_messages(CP)
if CP.flags & SubaruFlags.SEND_INFOTAINMENT:
messages.append(("ES_Infotainment", 10))
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus.camera)
@staticmethod
def get_body_can_parser(CP):
messages = []
if CP.carFingerprint in GLOBAL_GEN2:
messages += CarState.get_common_global_body_messages(CP)
messages += CarState.get_common_global_es_messages(CP)
if CP.carFingerprint in HYBRID_CARS:
messages += [
("Throttle_Hybrid", 40),
("Transmission", 100)
]
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus.alt)

View File

@@ -0,0 +1,563 @@
from cereal import car
from openpilot.selfdrive.car.subaru.values import CAR
Ecu = car.CarParams.Ecu
FW_VERSIONS = {
CAR.ASCENT: {
(Ecu.abs, 0x7b0, None): [
b'\xa5 \x19\x02\x00',
b'\xa5 !\x02\x00',
b'\xf1\x82\xa5 \x19\x02\x00',
],
(Ecu.eps, 0x746, None): [
b'\x05\xc0\xd0\x00',
b'\x85\xc0\xd0\x00',
b'\x95\xc0\xd0\x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00d\xb9\x00\x00\x00\x00',
b'\x00\x00d\xb9\x1f@ \x10',
b'\x00\x00e@\x00\x00\x00\x00',
b'\x00\x00e@\x1f@ $',
b"\x00\x00e~\x1f@ '",
],
(Ecu.engine, 0x7e0, None): [
b'\xbb,\xa0t\x07',
b'\xd1,\xa0q\x07',
b'\xf1\x82\xbb,\xa0t\x07',
b'\xf1\x82\xbb,\xa0t\x87',
b'\xf1\x82\xd1,\xa0q\x07',
b'\xf1\x82\xd9,\xa0@\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\x00\xfe\xf7\x00\x00',
b'\x01\xfe\xf7\x00\x00',
b'\x01\xfe\xf9\x00\x00',
b'\x01\xfe\xfa\x00\x00',
],
},
CAR.ASCENT_2023: {
(Ecu.abs, 0x7b0, None): [
b'\xa5 #\x03\x00',
],
(Ecu.eps, 0x746, None): [
b'%\xc0\xd0\x11',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x05!\x08\x1dK\x05!\x08\x01/',
],
(Ecu.engine, 0x7a2, None): [
b'\xe5,\xa0P\x07',
],
(Ecu.transmission, 0x7a3, None): [
b'\x04\xfe\xf3\x00\x00',
],
},
CAR.LEGACY: {
(Ecu.abs, 0x7b0, None): [
b'\xa1 \x02\x01',
b'\xa1 \x02\x02',
b'\xa1 \x03\x03',
b'\xa1\\ x04\x01',
],
(Ecu.eps, 0x746, None): [
b'\x9b\xc0\x11\x00',
b'\x9b\xc0\x11\x02',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00e\x80\x00\x1f@ \x19\x00',
b'\x00\x00e\x9a\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\xde"a0\x07',
b'\xde,\xa0@\x07',
b'\xe2"aq\x07',
b'\xe2,\xa0@\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xa5\xf6\x05@\x00',
b'\xa5\xfe\xc7@\x00',
b'\xa7\xf6\x04@\x00',
b'\xa7\xfe\xc4@\x00',
],
},
CAR.IMPREZA: {
(Ecu.abs, 0x7b0, None): [
b'z\x84\x19\x90\x00',
b'z\x94\x08\x90\x00',
b'z\x94\x08\x90\x01',
b'z\x94\x0c\x90\x00',
b'z\x94\x0c\x90\x01',
b'z\x94.\x90\x00',
b'z\x94?\x90\x00',
b'z\x9c\x19\x80\x01',
b'\xa2 \x185\x00',
b'\xa2 \x193\x00',
b'\xa2 \x194\x00',
b'\xa2 \x19`\x00',
b'\xf1\x00\xb2\x06\x04',
],
(Ecu.eps, 0x746, None): [
b'z\xc0\x00\x00',
b'z\xc0\x04\x00',
b'z\xc0\x08\x00',
b'z\xc0\n\x00',
b'z\xc0\x0c\x00',
b'\x8a\xc0\x00\x00',
b'\x8a\xc0\x10\x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00c\xf4\x00\x00\x00\x00',
b'\x00\x00c\xf4\x1f@ \x07',
b'\x00\x00d)\x00\x00\x00\x00',
b'\x00\x00d)\x1f@ \x07',
b'\x00\x00dd\x00\x00\x00\x00',
b'\x00\x00dd\x1f@ \x0e',
b'\x00\x00d\xb5\x1f@ \x0e',
b'\x00\x00d\xdc\x00\x00\x00\x00',
b'\x00\x00d\xdc\x1f@ \x0e',
b'\x00\x00e\x02\x1f@ \x14',
b'\x00\x00e\x1c\x00\x00\x00\x00',
b'\x00\x00e\x1c\x1f@ \x14',
b'\x00\x00e+\x00\x00\x00\x00',
b'\x00\x00e+\x1f@ \x14',
],
(Ecu.engine, 0x7e0, None): [
b'\xaa\x00Bu\x07',
b'\xaa\x01bt\x07',
b'\xaa!`u\x07',
b'\xaa!au\x07',
b'\xaa!av\x07',
b'\xaa!aw\x07',
b'\xaa!dq\x07',
b'\xaa!ds\x07',
b'\xaa!dt\x07',
b'\xaaafs\x07',
b'\xbe!as\x07',
b'\xbe!at\x07',
b'\xbeacr\x07',
b'\xc5!`r\x07',
b'\xc5!`s\x07',
b'\xc5!ap\x07',
b'\xc5!ar\x07',
b'\xc5!as\x07',
b'\xc5!dr\x07',
b'\xc5!ds\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xe3\xd0\x081\x00',
b'\xe3\xd5\x161\x00',
b'\xe3\xe5F1\x00',
b'\xe3\xf5\x06\x00\x00',
b'\xe3\xf5\x07\x00\x00',
b'\xe3\xf5C\x00\x00',
b'\xe3\xf5F\x00\x00',
b'\xe3\xf5G\x00\x00',
b'\xe4\xe5\x061\x00',
b'\xe4\xf5\x02\x00\x00',
b'\xe4\xf5\x07\x00\x00',
b'\xe5\xf5\x04\x00\x00',
b'\xe5\xf5$\x00\x00',
b'\xe5\xf5B\x00\x00',
],
},
CAR.IMPREZA_2020: {
(Ecu.abs, 0x7b0, None): [
b'\xa2 \x193\x00',
b'\xa2 \x194\x00',
b'\xa2 `\x00',
b'\xa2 !3\x00',
b'\xa2 !`\x00',
b'\xa2 !i\x00',
b'\xf1\x00\xb2\x06\x04',
],
(Ecu.eps, 0x746, None): [
b'\n\xc0\x04\x00',
b'\n\xc0\x04\x01',
b'\x9a\xc0\x00\x00',
b'\x9a\xc0\x04\x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00eb\x1f@ "',
b'\x00\x00eq\x00\x00\x00\x00',
b'\x00\x00eq\x1f@ "',
b'\x00\x00e\x8f\x00\x00\x00\x00',
b'\x00\x00e\x8f\x1f@ )',
b'\x00\x00e\x92\x00\x00\x00\x00',
b'\x00\x00e\xa4\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\xca!`0\x07',
b'\xca!`p\x07',
b'\xca!ap\x07',
b'\xca!f@\x07',
b'\xca!fp\x07',
b'\xcc!`p\x07',
b'\xcc!fp\x07',
b'\xcc"f0\x07',
b'\xe6!`@\x07',
b'\xe6!fp\x07',
b'\xe6"f0\x07',
b'\xe6"fp\x07',
b'\xf3"f@\x07',
b'\xf3"fp\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xe6\xf5\x04\x00\x00',
b'\xe6\xf5$\x00\x00',
b'\xe6\xf5D0\x00',
b'\xe7\xf5\x04\x00\x00',
b'\xe7\xf5D0\x00',
b'\xe7\xf6B0\x00',
b'\xe9\xf5"\x00\x00',
b'\xe9\xf5B0\x00',
b'\xe9\xf6B0\x00',
b'\xe9\xf6F0\x00',
b'\xf1\x00\xd7\x10@',
],
},
CAR.CROSSTREK_HYBRID: {
(Ecu.abs, 0x7b0, None): [
b'\xa2 \x19e\x01',
b'\xa2 !e\x01',
],
(Ecu.eps, 0x746, None): [
b'\n\xc2\x01\x00',
b'\x9a\xc2\x01\x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00el\x1f@ #',
],
(Ecu.engine, 0x7e0, None): [
b'\xd7!`@\x07',
b'\xd7!`p\x07',
b'\xf4!`0\x07',
],
},
CAR.FORESTER: {
(Ecu.abs, 0x7b0, None): [
b'\xa3 \x18\x14\x00',
b'\xa3 \x18&\x00',
b'\xa3 \x19\x14\x00',
b'\xa3 \x19&\x00',
b'\xa3 \x14\x00',
b'\xa3 \x14\x01',
b'\xf1\x00\xbb\r\x05',
],
(Ecu.eps, 0x746, None): [
b'\x8d\xc0\x00\x00',
b'\x8d\xc0\x04\x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00e!\x00\x00\x00\x00',
b'\x00\x00e!\x1f@ \x11',
b'\x00\x00e^\x00\x00\x00\x00',
b'\x00\x00e^\x1f@ !',
b'\x00\x00e`\x1f@ ',
b'\x00\x00e\x97\x00\x00\x00\x00',
b'\x00\x00e\x97\x1f@ 0',
b'\xf1\x00\xac\x02\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\xb6"`A\x07',
b'\xb6\xa2`A\x07',
b'\xcb"`@\x07',
b'\xcb"`p\x07',
b'\xcf"`0\x07',
b'\xcf"`p\x07',
b'\xf1\x00\xa2\x10\n',
],
(Ecu.transmission, 0x7e1, None): [
b'\x1a\xe6B1\x00',
b'\x1a\xe6F1\x00',
b'\x1a\xf6B0\x00',
b'\x1a\xf6B`\x00',
b'\x1a\xf6F`\x00',
b'\x1a\xf6b0\x00',
b'\x1a\xf6b`\x00',
],
},
CAR.FORESTER_HYBRID: {
(Ecu.abs, 0x7b0, None): [
b'\xa3 \x19T\x00',
],
(Ecu.eps, 0x746, None): [
b'\x8d\xc2\x00\x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00eY\x1f@ !',
],
(Ecu.engine, 0x7e0, None): [
b'\xd2\xa1`r\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\x1b\xa7@a\x00',
],
},
CAR.FORESTER_PREGLOBAL: {
(Ecu.abs, 0x7b0, None): [
b'm\x97\x14@',
b'}\x97\x14@',
b'\xf1\x00\xbb\x0c\x04',
],
(Ecu.eps, 0x746, None): [
b'm\xc0\x10\x00',
b'}\xc0\x10\x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00c\xe9\x00\x00\x00\x00',
b'\x00\x00c\xe9\x1f@ \x03',
b'\x00\x00d5\x1f@ \t',
b'\x00\x00d\xd3\x1f@ \t',
],
(Ecu.engine, 0x7e0, None): [
b'\xa7"@p\x07',
b'\xa7)\xa0q\x07',
b'\xba"@@\x07',
b'\xba"@p\x07',
b'\xf1\x82\xa7)\xa0q\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\x1a\xf6F`\x00',
b'\xda\xf2`\x80\x00',
b'\xda\xfd\xe0\x80\x00',
b'\xdc\xf2@`\x00',
b'\xdc\xf2``\x00',
b'\xdc\xf2`\x80\x00',
b'\xdc\xf2`\x81\x00',
],
},
CAR.LEGACY_PREGLOBAL: {
(Ecu.abs, 0x7b0, None): [
b'[\x97D\x00',
b'[\xba\xc4\x03',
b'k\x97D\x00',
b'k\x9aD\x00',
b'{\x97D\x00',
],
(Ecu.eps, 0x746, None): [
b'K\xb0\x00\x01',
b'[\xb0\x00\x01',
b'k\xb0\x00\x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00c\x94\x1f@\x10\x08',
b'\x00\x00c\xb7\x1f@\x10\x16',
b'\x00\x00c\xec\x1f@ \x04',
],
(Ecu.engine, 0x7e0, None): [
b'\xa0"@q\x07',
b'\xa0+@p\x07',
b'\xab*@r\x07',
b'\xab+@p\x07',
b'\xb4"@0\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xbd\xf2\x00`\x00',
b'\xbe\xf2\x00p\x00',
b'\xbe\xfb\xc0p\x00',
b'\xbf\xf2\x00\x80\x00',
b'\xbf\xfb\xc0\x80\x00',
],
},
CAR.OUTBACK_PREGLOBAL: {
(Ecu.abs, 0x7b0, None): [
b'[\xba\xac\x03',
b'[\xf7\xac\x00',
b'[\xf7\xac\x03',
b'[\xf7\xbc\x03',
b'k\x97\xac\x00',
b'k\x9a\xac\x00',
b'{\x97\xac\x00',
b'{\x9a\xac\x00',
],
(Ecu.eps, 0x746, None): [
b'K\xb0\x00\x00',
b'K\xb0\x00\x02',
b'[\xb0\x00\x00',
b'k\xb0\x00\x00',
b'{\xb0\x00\x01',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00c\x90\x1f@\x10\x0e',
b'\x00\x00c\x94\x00\x00\x00\x00',
b'\x00\x00c\x94\x1f@\x10\x08',
b'\x00\x00c\xb7\x1f@\x10\x16',
b'\x00\x00c\xd1\x1f@\x10\x17',
b'\x00\x00c\xec\x1f@ \x04',
b'\x00\x00c\xec7@\x04',
b'\xf1\x00\xf0\xe0\x0e',
],
(Ecu.engine, 0x7e0, None): [
b'\xa0"@\x80\x07',
b'\xa0*@q\x07',
b'\xa0*@u\x07',
b'\xa0+@@\x07',
b'\xa0bAq\x07',
b'\xab"@@\x07',
b'\xab"@s\x07',
b'\xab*@@\x07',
b'\xab+@@\x07',
b'\xb4"@0\x07',
b'\xb4"@p\x07',
b'\xb4"@r\x07',
b'\xb4+@p\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xbd\xf2@`\x00',
b'\xbd\xf2@\x81\x00',
b'\xbd\xfb\xe0\x80\x00',
b'\xbe\xf2@p\x00',
b'\xbe\xf2@\x80\x00',
b'\xbe\xfb\xe0p\x00',
b'\xbf\xe2@\x80\x00',
b'\xbf\xf2@\x80\x00',
b'\xbf\xfb\xe0b\x00',
],
},
CAR.OUTBACK_PREGLOBAL_2018: {
(Ecu.abs, 0x7b0, None): [
b'\x8b\x97\xac\x00',
b'\x8b\x97\xbc\x00',
b'\x8b\x99\xac\x00',
b'\x8b\x9a\xac\x00',
b'\x9b\x97\xac\x00',
b'\x9b\x97\xbe\x10',
b'\x9b\x9a\xac\x00',
],
(Ecu.eps, 0x746, None): [
b'{\xb0\x00\x00',
b'{\xb0\x00\x01',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00df\x1f@ \n',
b'\x00\x00d\x95\x00\x00\x00\x00',
b'\x00\x00d\x95\x1f@ \x0f',
b'\x00\x00d\xfe\x00\x00\x00\x00',
b'\x00\x00d\xfe\x1f@ \x15',
b'\x00\x00e\x19\x1f@ \x15',
],
(Ecu.engine, 0x7e0, None): [
b'\xb5"@P\x07',
b'\xb5"@p\x07',
b'\xb5+@@\x07',
b'\xb5b@1\x07',
b'\xb5q\xe0@\x07',
b'\xc4"@0\x07',
b'\xc4+@0\x07',
b'\xc4b@p\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xbb\xf2@`\x00',
b'\xbb\xfb\xe0`\x00',
b'\xbc\xaf\xe0`\x00',
b'\xbc\xe2@\x80\x00',
b'\xbc\xf2@\x80\x00',
b'\xbc\xf2@\x81\x00',
b'\xbc\xfb\xe0`\x00',
b'\xbc\xfb\xe0\x80\x00',
],
},
CAR.OUTBACK: {
(Ecu.abs, 0x7b0, None): [
b'\xa1 \x06\x02',
b'\xa1 \x06\x00',
b'\xa1 \x06\x01',
b'\xa1 \x07\x00',
b'\xa1 \x07\x02',
b'\xa1 \x08\x00',
b'\xa1 \x08\x01',
b'\xa1 \x08\x02',
b'\xa1 "\t\x00',
b'\xa1 "\t\x01',
],
(Ecu.eps, 0x746, None): [
b'\x1b\xc0\x10\x00',
b'\x9b\xc0\x10\x00',
b'\x9b\xc0 \x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00eJ\x00\x00\x00\x00\x00\x00',
b'\x00\x00eJ\x00\x1f@ \x19\x00',
b'\x00\x00e\x80\x00\x1f@ \x19\x00',
b'\x00\x00e\x9a\x00\x00\x00\x00\x00\x00',
b'\x00\x00e\x9a\x00\x1f@ 1\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\xbc"`@\x07',
b'\xbc"`q\x07',
b'\xbc,\xa0q\x07',
b'\xbc,\xa0u\x07',
b'\xde"`0\x07',
b'\xde,\xa0@\x07',
b'\xe2"`0\x07',
b'\xe2"`p\x07',
b'\xe3,\xa0@\x07',
b'\xf1\x82\xbc,\xa0q\x07',
b'\xf1\x82\xe2,\xa0@\x07',
b'\xf1\x82\xe3,\xa0@\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xa5\xf6D@\x00',
b'\xa5\xfe\xf6@\x00',
b'\xa5\xfe\xf7@\x00',
b'\xa5\xfe\xf8@\x00',
b'\xa7\x8e\xf40\x00',
b'\xa7\xf6D@\x00',
b'\xa7\xfe\xf4@\x00',
b'\xf1\x82\xa7\xf6D@\x00',
],
},
CAR.FORESTER_2022: {
(Ecu.abs, 0x7b0, None): [
b'\xa3 !v\x00',
b'\xa3 !x\x00',
b'\xa3 "v\x00',
b'\xa3 "x\x00',
],
(Ecu.eps, 0x746, None): [
b'-\xc0\x040',
b'-\xc0%0',
b'=\xc0%\x02',
b'=\xc04\x02',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x04!\x01\x1eD\x07!\x00\x04,',
b'\x04!\x08\x01.\x07!\x08\x022',
],
(Ecu.engine, 0x7e0, None): [
b'\xd5"`0\x07',
b'\xd5"a0\x07',
b'\xf1"`q\x07',
b'\xf1"aq\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\x1d\x86B0\x00',
b'\x1d\xf6B0\x00',
b'\x1e\x86B0\x00',
b'\x1e\xf6D0\x00',
],
},
CAR.OUTBACK_2023: {
(Ecu.abs, 0x7b0, None): [
b'\xa1 #\x14\x00',
b'\xa1 #\x17\x00',
],
(Ecu.eps, 0x746, None): [
b'+\xc0\x10\x11\x00',
b'+\xc0\x12\x11\x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\t!\x08\x046\x05!\x08\x01/',
],
(Ecu.engine, 0x7a2, None): [
b'\xed,\xa0q\x07',
b'\xed,\xa2q\x07',
],
(Ecu.transmission, 0x7a3, None): [
b'\xa8\x8e\xf41\x00',
b'\xa8\xfe\xf41\x00',
],
},
}

View File

@@ -0,0 +1,153 @@
from cereal import car
from panda import Panda
from openpilot.selfdrive.car import get_safety_config
from openpilot.selfdrive.car.disable_ecu import disable_ecu
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, LKAS_ANGLE, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, SubaruFlags
class CarInterface(CarInterfaceBase):
@staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
ret.carName = "subaru"
ret.radarUnavailable = True
# for HYBRID CARS to be upstreamed, we need:
# - replacement for ES_Distance so we can cancel the cruise control
# - to find the Cruise_Activated bit from the car
# - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc)
ret.dashcamOnly = candidate in (PREGLOBAL_CARS | LKAS_ANGLE | HYBRID_CARS)
ret.autoResumeSng = False
# Detect infotainment message sent from the camera
if candidate not in PREGLOBAL_CARS and 0x323 in fingerprint[2]:
ret.flags |= SubaruFlags.SEND_INFOTAINMENT.value
if candidate in PREGLOBAL_CARS:
ret.enableBsm = 0x25c in fingerprint[0]
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaruPreglobal)]
else:
ret.enableBsm = 0x228 in fingerprint[0]
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaru)]
if candidate in GLOBAL_GEN2:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2
ret.steerLimitTimer = 0.4
ret.steerActuatorDelay = 0.1
if candidate in LKAS_ANGLE:
ret.steerControlType = car.CarParams.SteerControlType.angle
else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if candidate in (CAR.ASCENT, CAR.ASCENT_2023):
ret.mass = 2031.
ret.wheelbase = 2.89
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 13.5
ret.steerActuatorDelay = 0.3 # end-to-end angle controller
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kf = 0.00003
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.0025, 0.1], [0.00025, 0.01]]
elif candidate == CAR.IMPREZA:
ret.mass = 1568.
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 15
ret.steerActuatorDelay = 0.4 # end-to-end angle controller
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kf = 0.00005
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2, 0.3], [0.02, 0.03]]
elif candidate == CAR.IMPREZA_2020:
ret.mass = 1480.
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 17 # learned, 14 stock
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kf = 0.00005
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.045, 0.042, 0.20], [0.04, 0.035, 0.045]]
elif candidate == CAR.CROSSTREK_HYBRID:
ret.mass = 1668.
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 17
ret.steerActuatorDelay = 0.1
elif candidate in (CAR.FORESTER, CAR.FORESTER_2022, CAR.FORESTER_HYBRID):
ret.mass = 1568.
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 17 # learned, 14 stock
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kf = 0.000038
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.01, 0.065, 0.2], [0.001, 0.015, 0.025]]
elif candidate in (CAR.OUTBACK, CAR.LEGACY, CAR.OUTBACK_2023):
ret.mass = 1568.
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 17
ret.steerActuatorDelay = 0.1
elif candidate in (CAR.FORESTER_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018):
ret.safetyConfigs[0].safetyParam = Panda.FLAG_SUBARU_PREGLOBAL_REVERSED_DRIVER_TORQUE # Outback 2018-2019 and Forester have reversed driver torque signal
ret.mass = 1568
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 20 # learned, 14 stock
elif candidate == CAR.LEGACY_PREGLOBAL:
ret.mass = 1568
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 12.5 # 14.5 stock
ret.steerActuatorDelay = 0.15
elif candidate == CAR.OUTBACK_PREGLOBAL:
ret.mass = 1568
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 20 # learned, 14 stock
else:
raise ValueError(f"unknown car: {candidate}")
ret.experimentalLongitudinalAvailable = candidate not in (GLOBAL_GEN2 | PREGLOBAL_CARS | LKAS_ANGLE | HYBRID_CARS)
ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable
if candidate in GLOBAL_GEN2 and ret.openpilotLongitudinalControl:
ret.flags |= SubaruFlags.DISABLE_EYESIGHT.value
if ret.openpilotLongitudinalControl:
ret.longitudinalTuning.kpBP = [0., 5., 35.]
ret.longitudinalTuning.kpV = [0.8, 1.0, 1.5]
ret.longitudinalTuning.kiBP = [0., 35.]
ret.longitudinalTuning.kiV = [0.54, 0.36]
ret.stoppingControl = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_LONG
return ret
# returns a car.CarState
def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam, self.cp_body)
ret.events = self.create_common_events(ret).to_msg()
return ret
@staticmethod
def init(CP, logcan, sendcan):
if CP.flags & SubaruFlags.DISABLE_EYESIGHT:
disable_ecu(logcan, sendcan, bus=2, addr=GLOBAL_ES_ADDR, com_cont_req=b'\x28\x03\x01')
def apply(self, c, now_nanos):
return self.CC.update(c, self.CS, now_nanos)

View File

@@ -0,0 +1,4 @@
from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
class RadarInterface(RadarInterfaceBase):
pass

View File

@@ -0,0 +1,312 @@
from cereal import car
from openpilot.selfdrive.car.subaru.values import CanBus
VisualAlert = car.CarControl.HUDControl.VisualAlert
def create_steering_control(packer, apply_steer, steer_req):
values = {
"LKAS_Output": apply_steer,
"LKAS_Request": steer_req,
"SET_1": 1
}
return packer.make_can_msg("ES_LKAS", 0, values)
def create_steering_status(packer):
return packer.make_can_msg("ES_LKAS_State", 0, {})
def create_es_distance(packer, frame, es_distance_msg, bus, pcm_cancel_cmd, long_enabled = False, brake_cmd = False, cruise_throttle = 0):
values = {s: es_distance_msg[s] for s in [
"CHECKSUM",
"Signal1",
"Cruise_Fault",
"Cruise_Throttle",
"Signal2",
"Car_Follow",
"Low_Speed_Follow",
"Cruise_Soft_Disable",
"Signal7",
"Cruise_Brake_Active",
"Distance_Swap",
"Cruise_EPB",
"Signal4",
"Close_Distance",
"Signal5",
"Cruise_Cancel",
"Cruise_Set",
"Cruise_Resume",
"Signal6",
]}
values["COUNTER"] = frame % 0x10
if long_enabled:
values["Cruise_Throttle"] = cruise_throttle
# Do not disable openpilot on Eyesight Soft Disable, if openpilot is controlling long
values["Cruise_Soft_Disable"] = 0
values["Cruise_Fault"] = 0
values["Cruise_Brake_Active"] = brake_cmd
if pcm_cancel_cmd:
values["Cruise_Cancel"] = 1
values["Cruise_Throttle"] = 1818 # inactive throttle
return packer.make_can_msg("ES_Distance", bus, values)
def create_es_lkas_state(packer, frame, es_lkas_state_msg, enabled, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart):
values = {s: es_lkas_state_msg[s] for s in [
"CHECKSUM",
"LKAS_Alert_Msg",
"Signal1",
"LKAS_ACTIVE",
"LKAS_Dash_State",
"Signal2",
"Backward_Speed_Limit_Menu",
"LKAS_Left_Line_Enable",
"LKAS_Left_Line_Light_Blink",
"LKAS_Right_Line_Enable",
"LKAS_Right_Line_Light_Blink",
"LKAS_Left_Line_Visible",
"LKAS_Right_Line_Visible",
"LKAS_Alert",
"Signal3",
]}
values["COUNTER"] = frame % 0x10
# Filter the stock LKAS "Keep hands on wheel" alert
if values["LKAS_Alert_Msg"] == 1:
values["LKAS_Alert_Msg"] = 0
# Filter the stock LKAS sending an audible alert when it turns off LKAS
if values["LKAS_Alert"] == 27:
values["LKAS_Alert"] = 0
# Filter the stock LKAS sending an audible alert when "Keep hands on wheel" alert is active (2020+ models)
if values["LKAS_Alert"] == 28 and values["LKAS_Alert_Msg"] == 7:
values["LKAS_Alert"] = 0
# Filter the stock LKAS sending an audible alert when "Keep hands on wheel OFF" alert is active (2020+ models)
if values["LKAS_Alert"] == 30:
values["LKAS_Alert"] = 0
# Filter the stock LKAS sending "Keep hands on wheel OFF" alert (2020+ models)
if values["LKAS_Alert_Msg"] == 7:
values["LKAS_Alert_Msg"] = 0
# Show Keep hands on wheel alert for openpilot steerRequired alert
if visual_alert == VisualAlert.steerRequired:
values["LKAS_Alert_Msg"] = 1
# Ensure we don't overwrite potentially more important alerts from stock (e.g. FCW)
if visual_alert == VisualAlert.ldw and values["LKAS_Alert"] == 0:
if left_lane_depart:
values["LKAS_Alert"] = 12 # Left lane departure dash alert
elif right_lane_depart:
values["LKAS_Alert"] = 11 # Right lane departure dash alert
if enabled:
values["LKAS_ACTIVE"] = 1 # Show LKAS lane lines
values["LKAS_Dash_State"] = 2 # Green enabled indicator
else:
values["LKAS_Dash_State"] = 0 # LKAS Not enabled
values["LKAS_Left_Line_Visible"] = int(left_line)
values["LKAS_Right_Line_Visible"] = int(right_line)
return packer.make_can_msg("ES_LKAS_State", CanBus.main, values)
def create_es_dashstatus(packer, frame, dashstatus_msg, enabled, long_enabled, long_active, lead_visible):
values = {s: dashstatus_msg[s] for s in [
"CHECKSUM",
"PCB_Off",
"LDW_Off",
"Signal1",
"Cruise_State_Msg",
"LKAS_State_Msg",
"Signal2",
"Cruise_Soft_Disable",
"Cruise_Status_Msg",
"Signal3",
"Cruise_Distance",
"Signal4",
"Conventional_Cruise",
"Signal5",
"Cruise_Disengaged",
"Cruise_Activated",
"Signal6",
"Cruise_Set_Speed",
"Cruise_Fault",
"Cruise_On",
"Display_Own_Car",
"Brake_Lights",
"Car_Follow",
"Signal7",
"Far_Distance",
"Cruise_State",
]}
values["COUNTER"] = frame % 0x10
if long_enabled:
values["Cruise_State"] = 0
values["Cruise_Activated"] = enabled
values["Cruise_Disengaged"] = 0
values["Car_Follow"] = int(lead_visible)
values["PCB_Off"] = 1 # AEB is not presevered, so show the PCB_Off on dash
values["LDW_Off"] = 0
values["Cruise_Fault"] = 0
# Filter stock LKAS disabled and Keep hands on steering wheel OFF alerts
if values["LKAS_State_Msg"] in (2, 3):
values["LKAS_State_Msg"] = 0
return packer.make_can_msg("ES_DashStatus", CanBus.main, values)
def create_es_brake(packer, frame, es_brake_msg, long_enabled, long_active, brake_value):
values = {s: es_brake_msg[s] for s in [
"CHECKSUM",
"Signal1",
"Brake_Pressure",
"AEB_Status",
"Cruise_Brake_Lights",
"Cruise_Brake_Fault",
"Cruise_Brake_Active",
"Cruise_Activated",
"Signal3",
]}
values["COUNTER"] = frame % 0x10
if long_enabled:
values["Cruise_Brake_Fault"] = 0
values["Cruise_Activated"] = long_active
values["Brake_Pressure"] = brake_value
values["Cruise_Brake_Active"] = brake_value > 0
values["Cruise_Brake_Lights"] = brake_value >= 70
return packer.make_can_msg("ES_Brake", CanBus.main, values)
def create_es_status(packer, frame, es_status_msg, long_enabled, long_active, cruise_rpm):
values = {s: es_status_msg[s] for s in [
"CHECKSUM",
"Signal1",
"Cruise_Fault",
"Cruise_RPM",
"Cruise_Activated",
"Brake_Lights",
"Cruise_Hold",
"Signal3",
]}
values["COUNTER"] = frame % 0x10
if long_enabled:
values["Cruise_RPM"] = cruise_rpm
values["Cruise_Fault"] = 0
values["Cruise_Activated"] = long_active
return packer.make_can_msg("ES_Status", CanBus.main, values)
def create_es_infotainment(packer, frame, es_infotainment_msg, visual_alert):
# Filter stock LKAS disabled and Keep hands on steering wheel OFF alerts
values = {s: es_infotainment_msg[s] for s in [
"CHECKSUM",
"LKAS_State_Infotainment",
"LKAS_Blue_Lines",
"Signal1",
"Signal2",
]}
values["COUNTER"] = frame % 0x10
if values["LKAS_State_Infotainment"] in (3, 4):
values["LKAS_State_Infotainment"] = 0
# Show Keep hands on wheel alert for openpilot steerRequired alert
if visual_alert == VisualAlert.steerRequired:
values["LKAS_State_Infotainment"] = 3
# Show Obstacle Detected for fcw
if visual_alert == VisualAlert.fcw:
values["LKAS_State_Infotainment"] = 2
return packer.make_can_msg("ES_Infotainment", CanBus.main, values)
def create_es_highbeamassist(packer):
values = {
"HBA_Available": False,
}
return packer.make_can_msg("ES_HighBeamAssist", CanBus.main, values)
def create_es_static_1(packer):
values = {
"SET_3": 3,
}
return packer.make_can_msg("ES_STATIC_1", CanBus.main, values)
def create_es_static_2(packer):
values = {
"SET_3": 3,
}
return packer.make_can_msg("ES_STATIC_2", CanBus.main, values)
# *** Subaru Pre-global ***
def subaru_preglobal_checksum(packer, values, addr, checksum_byte=7):
dat = packer.make_can_msg(addr, 0, values)[2]
return (sum(dat[:checksum_byte]) + sum(dat[checksum_byte+1:])) % 256
def create_preglobal_steering_control(packer, frame, apply_steer, steer_req):
values = {
"COUNTER": frame % 0x08,
"LKAS_Command": apply_steer,
"LKAS_Active": steer_req,
}
values["Checksum"] = subaru_preglobal_checksum(packer, values, "ES_LKAS")
return packer.make_can_msg("ES_LKAS", CanBus.main, values)
def create_preglobal_es_distance(packer, cruise_button, es_distance_msg):
values = {s: es_distance_msg[s] for s in [
"Cruise_Throttle",
"Signal1",
"Car_Follow",
"Signal2",
"Cruise_Brake_Active",
"Distance_Swap",
"Standstill",
"Signal3",
"Close_Distance",
"Signal4",
"Standstill_2",
"Cruise_Fault",
"Signal5",
"COUNTER",
"Signal6",
"Cruise_Button",
"Signal7",
]}
values["Cruise_Button"] = cruise_button
values["Checksum"] = subaru_preglobal_checksum(packer, values, "ES_Distance")
return packer.make_can_msg("ES_Distance", CanBus.main, values)

View File

@@ -0,0 +1,203 @@
from dataclasses import dataclass, field
from enum import Enum, IntFlag, StrEnum
from typing import Dict, List, Union
from cereal import car
from panda.python import uds
from openpilot.selfdrive.car import dbc_dict
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Tool, Column
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
Ecu = car.CarParams.Ecu
class CarControllerParams:
def __init__(self, CP):
self.STEER_STEP = 2 # how often we update the steer cmd
self.STEER_DELTA_UP = 50 # torque increase per refresh, 0.8s to max
self.STEER_DELTA_DOWN = 70 # torque decrease per refresh
self.STEER_DRIVER_ALLOWANCE = 60 # allowed driver torque before start limiting
self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily
self.STEER_DRIVER_FACTOR = 1 # from dbc
if CP.carFingerprint in GLOBAL_GEN2:
self.STEER_MAX = 1000
self.STEER_DELTA_UP = 40
self.STEER_DELTA_DOWN = 40
elif CP.carFingerprint == CAR.IMPREZA_2020:
self.STEER_MAX = 1439
else:
self.STEER_MAX = 2047
THROTTLE_MIN = 808
THROTTLE_MAX = 3400
THROTTLE_INACTIVE = 1818 # corresponds to zero acceleration
THROTTLE_ENGINE_BRAKE = 808 # while braking, eyesight sets throttle to this, probably for engine braking
BRAKE_MIN = 0
BRAKE_MAX = 600 # about -3.5m/s2 from testing
RPM_MIN = 0
RPM_MAX = 2400
RPM_INACTIVE = 600 # a good base rpm for zero acceleration
THROTTLE_LOOKUP_BP = [0, 2]
THROTTLE_LOOKUP_V = [THROTTLE_INACTIVE, THROTTLE_MAX]
RPM_LOOKUP_BP = [0, 2]
RPM_LOOKUP_V = [RPM_INACTIVE, RPM_MAX]
BRAKE_LOOKUP_BP = [-3.5, 0]
BRAKE_LOOKUP_V = [BRAKE_MAX, BRAKE_MIN]
class SubaruFlags(IntFlag):
SEND_INFOTAINMENT = 1
DISABLE_EYESIGHT = 2
GLOBAL_ES_ADDR = 0x787
GEN2_ES_BUTTONS_DID = b'\x11\x30'
class CanBus:
main = 0
alt = 1
camera = 2
class CAR(StrEnum):
# Global platform
ASCENT = "SUBARU ASCENT LIMITED 2019"
ASCENT_2023 = "SUBARU ASCENT 2023"
IMPREZA = "SUBARU IMPREZA LIMITED 2019"
IMPREZA_2020 = "SUBARU IMPREZA SPORT 2020"
FORESTER = "SUBARU FORESTER 2019"
OUTBACK = "SUBARU OUTBACK 6TH GEN"
CROSSTREK_HYBRID = "SUBARU CROSSTREK HYBRID 2020"
FORESTER_HYBRID = "SUBARU FORESTER HYBRID 2020"
LEGACY = "SUBARU LEGACY 7TH GEN"
FORESTER_2022 = "SUBARU FORESTER 2022"
OUTBACK_2023 = "SUBARU OUTBACK 7TH GEN"
# Pre-global
FORESTER_PREGLOBAL = "SUBARU FORESTER 2017 - 2018"
LEGACY_PREGLOBAL = "SUBARU LEGACY 2015 - 2018"
OUTBACK_PREGLOBAL = "SUBARU OUTBACK 2015 - 2017"
OUTBACK_PREGLOBAL_2018 = "SUBARU OUTBACK 2018 - 2019"
class Footnote(Enum):
GLOBAL = CarFootnote(
"In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance.",
Column.PACKAGE)
EXP_LONG = CarFootnote(
"Enabling longitudinal control (alpha) will disable all EyeSight functionality, including AEB, LDW, and RAB.",
Column.LONGITUDINAL)
@dataclass
class SubaruCarInfo(CarInfo):
package: str = "EyeSight Driver Assistance"
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.subaru_a]))
footnotes: List[Enum] = field(default_factory=lambda: [Footnote.GLOBAL])
def init_make(self, CP: car.CarParams):
self.car_parts.parts.extend([Tool.socket_8mm_deep, Tool.pry_tool])
if CP.experimentalLongitudinalAvailable:
self.footnotes.append(Footnote.EXP_LONG)
CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = {
CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-21", "All"),
CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
CAR.LEGACY: SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
CAR.IMPREZA: [
SubaruCarInfo("Subaru Impreza 2017-19"),
SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
SubaruCarInfo("Subaru XV 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
],
CAR.IMPREZA_2020: [
SubaruCarInfo("Subaru Impreza 2020-22"),
SubaruCarInfo("Subaru Crosstrek 2020-23"),
SubaruCarInfo("Subaru XV 2020-21"),
],
# TODO: is there an XV and Impreza too?
CAR.CROSSTREK_HYBRID: SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])),
CAR.FORESTER_HYBRID: SubaruCarInfo("Subaru Forester Hybrid 2020"),
CAR.FORESTER: SubaruCarInfo("Subaru Forester 2019-21", "All"),
CAR.FORESTER_PREGLOBAL: SubaruCarInfo("Subaru Forester 2017-18"),
CAR.LEGACY_PREGLOBAL: SubaruCarInfo("Subaru Legacy 2015-18"),
CAR.OUTBACK_PREGLOBAL: SubaruCarInfo("Subaru Outback 2015-17"),
CAR.OUTBACK_PREGLOBAL_2018: SubaruCarInfo("Subaru Outback 2018-19"),
CAR.FORESTER_2022: SubaruCarInfo("Subaru Forester 2022-23", "All", car_parts=CarParts.common([CarHarness.subaru_c])),
CAR.OUTBACK_2023: SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
CAR.ASCENT_2023: SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
}
SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
FW_QUERY_CONFIG = FwQueryConfig(
requests=[
Request(
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
),
# Some Eyesight modules fail on TESTER_PRESENT_REQUEST
# TODO: check if this resolves the fingerprinting issue for the 2023 Ascent and other new Subaru cars
Request(
[SUBARU_VERSION_REQUEST],
[SUBARU_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdCamera],
),
# Non-OBD requests
Request(
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
bus=0,
logging=True,
),
Request(
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
bus=1,
logging=True,
obd_multiplexing=False,
),
],
)
DBC = {
CAR.ASCENT: dbc_dict('subaru_global_2017_generated', None),
CAR.ASCENT_2023: dbc_dict('subaru_global_2017_generated', None),
CAR.IMPREZA: dbc_dict('subaru_global_2017_generated', None),
CAR.IMPREZA_2020: dbc_dict('subaru_global_2017_generated', None),
CAR.FORESTER: dbc_dict('subaru_global_2017_generated', None),
CAR.FORESTER_2022: dbc_dict('subaru_global_2017_generated', None),
CAR.OUTBACK: dbc_dict('subaru_global_2017_generated', None),
CAR.FORESTER_HYBRID: dbc_dict('subaru_global_2020_hybrid_generated', None),
CAR.CROSSTREK_HYBRID: dbc_dict('subaru_global_2020_hybrid_generated', None),
CAR.OUTBACK_2023: dbc_dict('subaru_global_2017_generated', None),
CAR.LEGACY: dbc_dict('subaru_global_2017_generated', None),
CAR.FORESTER_PREGLOBAL: dbc_dict('subaru_forester_2017_generated', None),
CAR.LEGACY_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None),
CAR.OUTBACK_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None),
CAR.OUTBACK_PREGLOBAL_2018: dbc_dict('subaru_outback_2019_generated', None),
}
LKAS_ANGLE = {CAR.FORESTER_2022, CAR.OUTBACK_2023, CAR.ASCENT_2023}
GLOBAL_GEN2 = {CAR.OUTBACK, CAR.LEGACY, CAR.OUTBACK_2023, CAR.ASCENT_2023}
PREGLOBAL_CARS = {CAR.FORESTER_PREGLOBAL, CAR.LEGACY_PREGLOBAL, CAR.OUTBACK_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018}
HYBRID_CARS = {CAR.CROSSTREK_HYBRID, CAR.FORESTER_HYBRID}
# Cars that temporarily fault when steering angle rate is greater than some threshold.
# Appears to be all torque-based cars produced around 2019 - present
STEER_RATE_LIMITED = GLOBAL_GEN2 | {CAR.IMPREZA_2020, CAR.FORESTER}