Subaru: 2020 Outback support (#25248)

* Subaru: 2020 Outback support

* bump panda

* bump panda

* update refs

* update refs
old-commit-hash: efa326e208
taco
Adeeb Shihadeh 3 years ago committed by GitHub
parent 6e5393367d
commit b34f0129e2
  1. 1
      RELEASES.md
  2. 3
      docs/CARS.md
  3. 2
      panda
  4. 20
      selfdrive/car/subaru/carcontroller.py
  5. 165
      selfdrive/car/subaru/carstate.py
  6. 22
      selfdrive/car/subaru/interface.py
  7. 28
      selfdrive/car/subaru/subarucan.py
  8. 66
      selfdrive/car/subaru/values.py
  9. 6
      selfdrive/car/tests/routes.py
  10. 1
      selfdrive/car/torque_data/override.yaml
  11. 2
      selfdrive/test/process_replay/ref_commit

@ -1,5 +1,6 @@
Version 0.8.16 (2022-XX-XX) Version 0.8.16 (2022-XX-XX)
======================== ========================
* Subaru Outback 2020-22 support
Version 0.8.15 (2022-07-20) Version 0.8.15 (2022-07-20)
======================== ========================

@ -19,7 +19,7 @@ A supported vehicle is one that just works when you install a comma device. Ever
- [![star](assets/icon-star-empty.svg)](##) - Limited ability to make tighter turns. - [![star](assets/icon-star-empty.svg)](##) - Limited ability to make tighter turns.
# 196 Supported Cars # 197 Supported Cars
|Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque| |Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|
|---|---|---|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|
@ -139,6 +139,7 @@ A supported vehicle is one that just works when you install a comma device. Ever
|Subaru|Forester 2019-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| |Subaru|Forester 2019-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
|Subaru|Impreza 2017-19|EyeSight|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| |Subaru|Impreza 2017-19|EyeSight|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
|Subaru|Impreza 2020-22|EyeSight|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| |Subaru|Impreza 2020-22|EyeSight|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
|Subaru|Outback 2020-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
|Subaru|XV 2018-19|EyeSight|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| |Subaru|XV 2018-19|EyeSight|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
|Subaru|XV 2020-21|EyeSight|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| |Subaru|XV 2020-21|EyeSight|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
|Škoda|Kamiq 2021[<sup>6</sup>](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| |Škoda|Kamiq 2021[<sup>6</sup>](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|

@ -1 +1 @@
Subproject commit a1686ca3ca55b004179f10bfc45af5fbd701ca64 Subproject commit 4b86b83991e6be699c1eba79ea82846533f55467

@ -1,17 +1,20 @@
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car import apply_std_steer_torque_limits
from selfdrive.car.subaru import subarucan from selfdrive.car.subaru import subarucan
from selfdrive.car.subaru.values import DBC, PREGLOBAL_CARS, CarControllerParams from selfdrive.car.subaru.values import DBC, GLOBAL_GEN2, PREGLOBAL_CARS, CarControllerParams
class CarController: class CarController:
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.apply_steer_last = 0 self.apply_steer_last = 0
self.es_distance_cnt = -1 self.frame = 0
self.es_lkas_cnt = -1 self.es_lkas_cnt = -1
self.es_distance_cnt = -1
self.es_dashstatus_cnt = -1
self.cruise_button_prev = 0 self.cruise_button_prev = 0
self.frame = 0 self.last_cancel_frame = 0
self.p = CarControllerParams(CP) self.p = CarControllerParams(CP)
self.packer = CANPacker(DBC[CP.carFingerprint]['pt']) self.packer = CANPacker(DBC[CP.carFingerprint]['pt'])
@ -67,9 +70,14 @@ class CarController:
self.es_distance_cnt = CS.es_distance_msg["COUNTER"] self.es_distance_cnt = CS.es_distance_msg["COUNTER"]
else: else:
if self.es_distance_cnt != CS.es_distance_msg["COUNTER"]: if pcm_cancel_cmd and (self.frame - self.last_cancel_frame) > 0.2:
can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg, pcm_cancel_cmd)) bus = 1 if self.CP.carFingerprint in GLOBAL_GEN2 else 0
self.es_distance_cnt = CS.es_distance_msg["COUNTER"] can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg, bus, pcm_cancel_cmd))
self.last_cancel_frame = self.frame
if self.es_dashstatus_cnt != CS.es_dashstatus_msg["COUNTER"]:
can_sends.append(subarucan.create_es_dashstatus(self.packer, CS.es_dashstatus_msg))
self.es_dashstatus_cnt = CS.es_dashstatus_msg["COUNTER"]
if self.es_lkas_cnt != CS.es_lkas_msg["COUNTER"]: if self.es_lkas_cnt != CS.es_lkas_msg["COUNTER"]:
can_sends.append(subarucan.create_es_lkas(self.packer, CS.es_lkas_msg, CC.enabled, hud_control.visualAlert, can_sends.append(subarucan.create_es_lkas(self.packer, CS.es_lkas_msg, CC.enabled, hud_control.visualAlert,

@ -4,7 +4,7 @@ from opendbc.can.can_define import CANDefine
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.car.interfaces import CarStateBase from selfdrive.car.interfaces import CarStateBase
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from selfdrive.car.subaru.values import DBC, STEER_THRESHOLD, CAR, PREGLOBAL_CARS from selfdrive.car.subaru.values import DBC, STEER_THRESHOLD, CAR, GLOBAL_GEN2, PREGLOBAL_CARS
class CarState(CarStateBase): class CarState(CarStateBase):
@ -13,7 +13,7 @@ class CarState(CarStateBase):
can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
self.shifter_values = can_define.dv["Transmission"]["Gear"] self.shifter_values = can_define.dv["Transmission"]["Gear"]
def update(self, cp, cp_cam): def update(self, cp, cp_cam, cp_body):
ret = car.CarState.new_message() ret = car.CarState.new_message()
ret.gas = cp.vl["Throttle"]["Throttle_Pedal"] / 255. ret.gas = cp.vl["Throttle"]["Throttle_Pedal"] / 255.
@ -21,13 +21,15 @@ class CarState(CarStateBase):
if self.car_fingerprint in PREGLOBAL_CARS: if self.car_fingerprint in PREGLOBAL_CARS:
ret.brakePressed = cp.vl["Brake_Pedal"]["Brake_Pedal"] > 2 ret.brakePressed = cp.vl["Brake_Pedal"]["Brake_Pedal"] > 2
else: else:
ret.brakePressed = cp.vl["Brake_Status"]["Brake"] == 1 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( ret.wheelSpeeds = self.get_wheel_speeds(
cp.vl["Wheel_Speeds"]["FL"], cp_wheels.vl["Wheel_Speeds"]["FL"],
cp.vl["Wheel_Speeds"]["FR"], cp_wheels.vl["Wheel_Speeds"]["FR"],
cp.vl["Wheel_Speeds"]["RL"], cp_wheels.vl["Wheel_Speeds"]["RL"],
cp.vl["Wheel_Speeds"]["RR"], cp_wheels.vl["Wheel_Speeds"]["RR"],
) )
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4. ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
# Kalman filter, even though Subaru raw wheel speed is heaviliy filtered by default # Kalman filter, even though Subaru raw wheel speed is heaviliy filtered by default
@ -35,8 +37,8 @@ class CarState(CarStateBase):
ret.standstill = ret.vEgoRaw < 0.01 ret.standstill = ret.vEgoRaw < 0.01
# continuous blinker signals for assisted lane change # continuous blinker signals for assisted lane change
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp( ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["Dashlights"]["LEFT_BLINKER"],
50, cp.vl["Dashlights"]["LEFT_BLINKER"], cp.vl["Dashlights"]["RIGHT_BLINKER"]) cp.vl["Dashlights"]["RIGHT_BLINKER"])
if self.CP.enableBsm: if self.CP.enableBsm:
ret.leftBlindspot = (cp.vl["BSD_RCTA"]["L_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["L_APPROACHING"] == 1) ret.leftBlindspot = (cp.vl["BSD_RCTA"]["L_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["L_APPROACHING"] == 1)
@ -50,8 +52,9 @@ class CarState(CarStateBase):
ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"] ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"]
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD[self.car_fingerprint] ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD[self.car_fingerprint]
ret.cruiseState.enabled = cp.vl["CruiseControl"]["Cruise_Activated"] != 0 cp_cruise = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp
ret.cruiseState.available = cp.vl["CruiseControl"]["Cruise_On"] != 0 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 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 \ if (self.car_fingerprint in PREGLOBAL_CARS and cp.vl["Dash_State2"]["UNITS"] == 1) or \
@ -72,10 +75,59 @@ class CarState(CarStateBase):
ret.steerFaultTemporary = cp.vl["Steering_Torque"]["Steer_Warning"] == 1 ret.steerFaultTemporary = cp.vl["Steering_Torque"]["Steer_Warning"] == 1
ret.cruiseState.nonAdaptive = cp_cam.vl["ES_DashStatus"]["Conventional_Cruise"] == 1 ret.cruiseState.nonAdaptive = cp_cam.vl["ES_DashStatus"]["Conventional_Cruise"] == 1
self.es_lkas_msg = copy.copy(cp_cam.vl["ES_LKAS_State"]) self.es_lkas_msg = copy.copy(cp_cam.vl["ES_LKAS_State"])
self.es_distance_msg = copy.copy(cp_cam.vl["ES_Distance"])
cp_es_distance = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam
self.es_distance_msg = copy.copy(cp_es_distance.vl["ES_Distance"])
self.es_dashstatus_msg = copy.copy(cp_cam.vl["ES_DashStatus"])
return ret return ret
@staticmethod
def get_common_global_signals():
signals = [
("Cruise_On", "CruiseControl"),
("Cruise_Activated", "CruiseControl"),
("FL", "Wheel_Speeds"),
("FR", "Wheel_Speeds"),
("RL", "Wheel_Speeds"),
("RR", "Wheel_Speeds"),
("Brake", "Brake_Status"),
]
checks = [
("CruiseControl", 20),
("Wheel_Speeds", 50),
("Brake_Status", 50),
]
return signals, checks
@staticmethod
def get_global_es_distance_signals():
signals = [
("COUNTER", "ES_Distance"),
("Signal1", "ES_Distance"),
("Cruise_Fault", "ES_Distance"),
("Cruise_Throttle", "ES_Distance"),
("Signal2", "ES_Distance"),
("Car_Follow", "ES_Distance"),
("Signal3", "ES_Distance"),
("Cruise_Brake_Active", "ES_Distance"),
("Distance_Swap", "ES_Distance"),
("Cruise_EPB", "ES_Distance"),
("Signal4", "ES_Distance"),
("Close_Distance", "ES_Distance"),
("Signal5", "ES_Distance"),
("Cruise_Cancel", "ES_Distance"),
("Cruise_Set", "ES_Distance"),
("Cruise_Resume", "ES_Distance"),
("Signal6", "ES_Distance"),
]
checks = [
("ES_Distance", 20),
]
return signals, checks
@staticmethod @staticmethod
def get_can_parser(CP): def get_can_parser(CP):
signals = [ signals = [
@ -84,17 +136,11 @@ class CarState(CarStateBase):
("Steer_Torque_Output", "Steering_Torque"), ("Steer_Torque_Output", "Steering_Torque"),
("Steering_Angle", "Steering_Torque"), ("Steering_Angle", "Steering_Torque"),
("Steer_Error_1", "Steering_Torque"), ("Steer_Error_1", "Steering_Torque"),
("Cruise_On", "CruiseControl"),
("Cruise_Activated", "CruiseControl"),
("Brake_Pedal", "Brake_Pedal"), ("Brake_Pedal", "Brake_Pedal"),
("Throttle_Pedal", "Throttle"), ("Throttle_Pedal", "Throttle"),
("LEFT_BLINKER", "Dashlights"), ("LEFT_BLINKER", "Dashlights"),
("RIGHT_BLINKER", "Dashlights"), ("RIGHT_BLINKER", "Dashlights"),
("SEATBELT_FL", "Dashlights"), ("SEATBELT_FL", "Dashlights"),
("FL", "Wheel_Speeds"),
("FR", "Wheel_Speeds"),
("RL", "Wheel_Speeds"),
("RR", "Wheel_Speeds"),
("DOOR_OPEN_FR", "BodyInfo"), ("DOOR_OPEN_FR", "BodyInfo"),
("DOOR_OPEN_FL", "BodyInfo"), ("DOOR_OPEN_FL", "BodyInfo"),
("DOOR_OPEN_RR", "BodyInfo"), ("DOOR_OPEN_RR", "BodyInfo"),
@ -107,7 +153,6 @@ class CarState(CarStateBase):
("Throttle", 100), ("Throttle", 100),
("Dashlights", 10), ("Dashlights", 10),
("Brake_Pedal", 50), ("Brake_Pedal", 50),
("Wheel_Speeds", 50),
("Transmission", 100), ("Transmission", 100),
("Steering_Torque", 50), ("Steering_Torque", 50),
("BodyInfo", 1), ("BodyInfo", 1),
@ -123,22 +168,33 @@ class CarState(CarStateBase):
checks.append(("BSD_RCTA", 17)) checks.append(("BSD_RCTA", 17))
if CP.carFingerprint not in PREGLOBAL_CARS: if CP.carFingerprint not in PREGLOBAL_CARS:
if CP.carFingerprint not in GLOBAL_GEN2:
signals += CarState.get_common_global_signals()[0]
checks += CarState.get_common_global_signals()[1]
signals += [ signals += [
("Steer_Warning", "Steering_Torque"), ("Steer_Warning", "Steering_Torque"),
("Brake", "Brake_Status"),
("UNITS", "Dashlights"), ("UNITS", "Dashlights"),
] ]
checks += [ checks += [
("Dashlights", 10), ("Dashlights", 10),
("BodyInfo", 10), ("BodyInfo", 10),
("Brake_Status", 50),
("CruiseControl", 20),
] ]
else: else:
signals.append(("UNITS", "Dash_State2")) signals += [
("FL", "Wheel_Speeds"),
checks.append(("Dash_State2", 1)) ("FR", "Wheel_Speeds"),
("RL", "Wheel_Speeds"),
("RR", "Wheel_Speeds"),
("UNITS", "Dash_State2"),
("Cruise_On", "CruiseControl"),
("Cruise_Activated", "CruiseControl"),
]
checks += [
("Wheel_Speeds", 50),
("Dash_State2", 1),
]
if CP.carFingerprint == CAR.FORESTER_PREGLOBAL: if CP.carFingerprint == CAR.FORESTER_PREGLOBAL:
checks += [ checks += [
@ -187,26 +243,32 @@ class CarState(CarStateBase):
] ]
else: else:
signals = [ signals = [
("Cruise_Set_Speed", "ES_DashStatus"), ("Counter", "ES_DashStatus"),
("PCB_Off", "ES_DashStatus"),
("LDW_Off", "ES_DashStatus"),
("Signal1", "ES_DashStatus"),
("Cruise_State_Msg", "ES_DashStatus"),
("LKAS_State_Msg", "ES_DashStatus"),
("Signal2", "ES_DashStatus"),
("Cruise_Soft_Disable", "ES_DashStatus"),
("EyeSight_Status_Msg", "ES_DashStatus"),
("Signal3", "ES_DashStatus"),
("Cruise_Distance", "ES_DashStatus"),
("Signal4", "ES_DashStatus"),
("Conventional_Cruise", "ES_DashStatus"), ("Conventional_Cruise", "ES_DashStatus"),
("Signal5", "ES_DashStatus"),
("COUNTER", "ES_Distance"), ("Cruise_Disengaged", "ES_DashStatus"),
("Signal1", "ES_Distance"), ("Cruise_Activated", "ES_DashStatus"),
("Cruise_Fault", "ES_Distance"), ("Signal6", "ES_DashStatus"),
("Cruise_Throttle", "ES_Distance"), ("Cruise_Set_Speed", "ES_DashStatus"),
("Signal2", "ES_Distance"), ("Cruise_Fault", "ES_DashStatus"),
("Car_Follow", "ES_Distance"), ("Cruise_On", "ES_DashStatus"),
("Signal3", "ES_Distance"), ("Display_Own_Car", "ES_DashStatus"),
("Cruise_Brake_Active", "ES_Distance"), ("Brake_Lights", "ES_DashStatus"),
("Distance_Swap", "ES_Distance"), ("Car_Follow", "ES_DashStatus"),
("Cruise_EPB", "ES_Distance"), ("Signal7", "ES_DashStatus"),
("Signal4", "ES_Distance"), ("Far_Distance", "ES_DashStatus"),
("Close_Distance", "ES_Distance"), ("Cruise_State", "ES_DashStatus"),
("Signal5", "ES_Distance"),
("Cruise_Cancel", "ES_Distance"),
("Cruise_Set", "ES_Distance"),
("Cruise_Resume", "ES_Distance"),
("Signal6", "ES_Distance"),
("COUNTER", "ES_LKAS_State"), ("COUNTER", "ES_LKAS_State"),
("LKAS_Alert_Msg", "ES_LKAS_State"), ("LKAS_Alert_Msg", "ES_LKAS_State"),
@ -227,8 +289,21 @@ class CarState(CarStateBase):
checks = [ checks = [
("ES_DashStatus", 10), ("ES_DashStatus", 10),
("ES_Distance", 20),
("ES_LKAS_State", 10), ("ES_LKAS_State", 10),
] ]
if CP.carFingerprint not in GLOBAL_GEN2:
signals += CarState.get_global_es_distance_signals()[0]
checks += CarState.get_global_es_distance_signals()[1]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 2) return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 2)
@staticmethod
def get_body_can_parser(CP):
if CP.carFingerprint in GLOBAL_GEN2:
signals, checks = CarState.get_common_global_signals()
signals += CarState.get_global_es_distance_signals()[0]
checks += CarState.get_global_es_distance_signals()[1]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 1)
return None

@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal import car
from panda import Panda
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.subaru.values import CAR, PREGLOBAL_CARS from selfdrive.car.subaru.values import CAR, GLOBAL_GEN2, PREGLOBAL_CARS
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@ -16,11 +17,13 @@ class CarInterface(CarInterfaceBase):
ret.dashcamOnly = candidate in PREGLOBAL_CARS ret.dashcamOnly = candidate in PREGLOBAL_CARS
if candidate in PREGLOBAL_CARS: if candidate in PREGLOBAL_CARS:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaruLegacy)]
ret.enableBsm = 0x25c in fingerprint[0] ret.enableBsm = 0x25c in fingerprint[0]
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaruLegacy)]
else: else:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaru)]
ret.enableBsm = 0x228 in fingerprint[0] 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.steerLimitTimer = 0.4
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
@ -68,6 +71,14 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]] 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]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.01, 0.065, 0.2], [0.001, 0.015, 0.025]]
elif candidate == CAR.OUTBACK:
ret.mass = 1568. + STD_CARGO_KG
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 17
ret.steerActuatorDelay = 0.1
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (CAR.FORESTER_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018): elif candidate in (CAR.FORESTER_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018):
ret.safetyConfigs[0].safetyParam = 1 # Outback 2018-2019 and Forester have reversed driver torque signal ret.safetyConfigs[0].safetyParam = 1 # Outback 2018-2019 and Forester have reversed driver torque signal
ret.mass = 1568 + STD_CARGO_KG ret.mass = 1568 + STD_CARGO_KG
@ -88,6 +99,9 @@ class CarInterface(CarInterfaceBase):
ret.centerToFront = ret.wheelbase * 0.5 ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 20 # learned, 14 stock ret.steerRatio = 20 # learned, 14 stock
else:
raise ValueError(f"unknown car: {candidate}")
# TODO: get actual value, for now starting with reasonable value for # TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase # civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase) ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
@ -101,7 +115,7 @@ class CarInterface(CarInterfaceBase):
# returns a car.CarState # returns a car.CarState
def _update(self, c): def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam) ret = self.CS.update(self.cp, self.cp_cam, self.cp_body)
ret.events = self.create_common_events(ret).to_msg() ret.events = self.create_common_events(ret).to_msg()

@ -14,13 +14,12 @@ def create_steering_control(packer, apply_steer):
def create_steering_status(packer): def create_steering_status(packer):
return packer.make_can_msg("ES_LKAS_State", 0, {}) return packer.make_can_msg("ES_LKAS_State", 0, {})
def create_es_distance(packer, es_distance_msg, pcm_cancel_cmd): def create_es_distance(packer, es_distance_msg, bus, pcm_cancel_cmd):
values = copy.copy(es_distance_msg) values = copy.copy(es_distance_msg)
values["COUNTER"] = (values["COUNTER"] + 1) % 0x10
if pcm_cancel_cmd: if pcm_cancel_cmd:
values["Cruise_Cancel"] = 1 values["Cruise_Cancel"] = 1
return packer.make_can_msg("ES_Distance", bus, values)
return packer.make_can_msg("ES_Distance", 0, values)
def create_es_lkas(packer, es_lkas_msg, enabled, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart): def create_es_lkas(packer, es_lkas_msg, enabled, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart):
@ -34,6 +33,18 @@ def create_es_lkas(packer, es_lkas_msg, enabled, visual_alert, left_line, right_
if values["LKAS_Alert"] == 27: if values["LKAS_Alert"] == 27:
values["LKAS_Alert"] = 0 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 # Show Keep hands on wheel alert for openpilot steerRequired alert
if visual_alert == VisualAlert.steerRequired: if visual_alert == VisualAlert.steerRequired:
values["LKAS_Alert_Msg"] = 1 values["LKAS_Alert_Msg"] = 1
@ -56,6 +67,15 @@ def create_es_lkas(packer, es_lkas_msg, enabled, visual_alert, left_line, right_
return packer.make_can_msg("ES_LKAS_State", 0, values) return packer.make_can_msg("ES_LKAS_State", 0, values)
def create_es_dashstatus(packer, dashstatus_msg):
values = copy.copy(dashstatus_msg)
# 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", 0, values)
# *** Subaru Pre-global *** # *** Subaru Pre-global ***
def subaru_preglobal_checksum(packer, values, addr): def subaru_preglobal_checksum(packer, values, addr):

@ -11,23 +11,32 @@ Ecu = car.CarParams.Ecu
class CarControllerParams: class CarControllerParams:
def __init__(self, CP): def __init__(self, CP):
if CP.carFingerprint == CAR.IMPREZA_2020:
self.STEER_MAX = 1439
else:
self.STEER_MAX = 2047
self.STEER_STEP = 2 # how often we update the steer cmd 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_UP = 50 # torque increase per refresh, 0.8s to max
self.STEER_DELTA_DOWN = 70 # torque decrease per refresh self.STEER_DELTA_DOWN = 70 # torque decrease per refresh
self.STEER_DRIVER_ALLOWANCE = 60 # allowed driver torque before start limiting self.STEER_DRIVER_ALLOWANCE = 60 # allowed driver torque before start limiting
self.STEER_DRIVER_MULTIPLIER = 10 # weight driver torque heavily self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily
self.STEER_DRIVER_FACTOR = 1 # from dbc 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
class CAR: class CAR:
# Global platform
ASCENT = "SUBARU ASCENT LIMITED 2019" ASCENT = "SUBARU ASCENT LIMITED 2019"
IMPREZA = "SUBARU IMPREZA LIMITED 2019" IMPREZA = "SUBARU IMPREZA LIMITED 2019"
IMPREZA_2020 = "SUBARU IMPREZA SPORT 2020" IMPREZA_2020 = "SUBARU IMPREZA SPORT 2020"
FORESTER = "SUBARU FORESTER 2019" FORESTER = "SUBARU FORESTER 2019"
OUTBACK = "SUBARU OUTBACK 6TH GEN"
# Pre-global
FORESTER_PREGLOBAL = "SUBARU FORESTER 2017 - 2018" FORESTER_PREGLOBAL = "SUBARU FORESTER 2017 - 2018"
LEGACY_PREGLOBAL = "SUBARU LEGACY 2015 - 2018" LEGACY_PREGLOBAL = "SUBARU LEGACY 2015 - 2018"
OUTBACK_PREGLOBAL = "SUBARU OUTBACK 2015 - 2017" OUTBACK_PREGLOBAL = "SUBARU OUTBACK 2015 - 2017"
@ -42,6 +51,7 @@ class SubaruCarInfo(CarInfo):
CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = { CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = {
CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-21", "All"), CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-21", "All"),
CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All"),
CAR.IMPREZA: [ CAR.IMPREZA: [
SubaruCarInfo("Subaru Impreza 2017-19"), SubaruCarInfo("Subaru Impreza 2017-19"),
SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
@ -396,6 +406,47 @@ FW_VERSIONS = {
b'\xbb\xfb\xe0`\000', b'\xbb\xfb\xe0`\000',
], ],
}, },
CAR.OUTBACK: {
(Ecu.esp, 0x7b0, None): [
b'\xa1 \x06\x01',
b'\xa1 \a\x00',
b'\xa1 \b\001',
b'\xa1 \x06\x00',
b'\xa1 "\t\x01',
b'\xa1 \x08\x02',
b'\xa1 \x06\x02',
b'\xa1 \x08\x00',
],
(Ecu.eps, 0x746, None): [
b'\x9b\xc0\x10\x00',
b'\x9b\xc0\x20\x00',
b'\x1b\xc0\x10\x00',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x00\x00eJ\x00\x1f@ \x19\x00',
b'\000\000e\x80\000\037@ \031\000',
b'\x00\x00e\x9a\x00\x1f@ 1\x00',
b'\x00\x00eJ\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\xbc,\xa0q\x07',
b'\xbc\"`@\a',
b'\xde"`0\a',
b'\xf1\x82\xbc,\xa0q\a',
b'\xf1\x82\xe3,\xa0@\x07',
b'\xe2"`p\x07',
b'\xf1\x82\xe2,\xa0@\x07',
b'\xbc"`q\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xa5\xfe\xf7@\x00',
b'\xa5\xf6D@\x00',
b'\xa5\xfe\xf6@\x00',
b'\xa7\x8e\xf40\x00',
b'\xf1\x82\xa7\xf6D@\x00',
b'\xa7\xfe\xf4@\x00',
],
},
} }
STEER_THRESHOLD = { STEER_THRESHOLD = {
@ -403,6 +454,7 @@ STEER_THRESHOLD = {
CAR.IMPREZA: 80, CAR.IMPREZA: 80,
CAR.IMPREZA_2020: 80, CAR.IMPREZA_2020: 80,
CAR.FORESTER: 80, CAR.FORESTER: 80,
CAR.OUTBACK: 80,
CAR.FORESTER_PREGLOBAL: 75, CAR.FORESTER_PREGLOBAL: 75,
CAR.LEGACY_PREGLOBAL: 75, CAR.LEGACY_PREGLOBAL: 75,
CAR.OUTBACK_PREGLOBAL: 75, CAR.OUTBACK_PREGLOBAL: 75,
@ -414,10 +466,12 @@ DBC = {
CAR.IMPREZA: 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.IMPREZA_2020: dbc_dict('subaru_global_2017_generated', None),
CAR.FORESTER: dbc_dict('subaru_global_2017_generated', None), CAR.FORESTER: dbc_dict('subaru_global_2017_generated', None),
CAR.OUTBACK: dbc_dict('subaru_global_2017_generated', None),
CAR.FORESTER_PREGLOBAL: dbc_dict('subaru_forester_2017_generated', None), CAR.FORESTER_PREGLOBAL: dbc_dict('subaru_forester_2017_generated', None),
CAR.LEGACY_PREGLOBAL: dbc_dict('subaru_outback_2015_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: dbc_dict('subaru_outback_2015_generated', None),
CAR.OUTBACK_PREGLOBAL_2018: dbc_dict('subaru_outback_2019_generated', None), CAR.OUTBACK_PREGLOBAL_2018: dbc_dict('subaru_outback_2019_generated', None),
} }
PREGLOBAL_CARS = [CAR.FORESTER_PREGLOBAL, CAR.LEGACY_PREGLOBAL, CAR.OUTBACK_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018] GLOBAL_GEN2 = (CAR.OUTBACK, )
PREGLOBAL_CARS = (CAR.FORESTER_PREGLOBAL, CAR.LEGACY_PREGLOBAL, CAR.OUTBACK_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018)

@ -187,13 +187,11 @@ routes = [
TestRoute("c321c6b697c5a5ff|2020-06-23--11-04-33", SUBARU.FORESTER), TestRoute("c321c6b697c5a5ff|2020-06-23--11-04-33", SUBARU.FORESTER),
TestRoute("791340bc01ed993d|2019-03-10--16-28-08", SUBARU.IMPREZA), TestRoute("791340bc01ed993d|2019-03-10--16-28-08", SUBARU.IMPREZA),
TestRoute("8bf7e79a3ce64055|2021-05-24--09-36-27", SUBARU.IMPREZA_2020), TestRoute("8bf7e79a3ce64055|2021-05-24--09-36-27", SUBARU.IMPREZA_2020),
# Dashcam TestRoute("1bbe6bf2d62f58a8|2022-07-14--17-11-43", SUBARU.OUTBACK, segment=3),
# Pre-global, dashcam
TestRoute("95441c38ae8c130e|2020-06-08--12-10-17", SUBARU.FORESTER_PREGLOBAL), TestRoute("95441c38ae8c130e|2020-06-08--12-10-17", SUBARU.FORESTER_PREGLOBAL),
# Dashcam
TestRoute("df5ca7660000fba8|2020-06-16--17-37-19", SUBARU.LEGACY_PREGLOBAL), TestRoute("df5ca7660000fba8|2020-06-16--17-37-19", SUBARU.LEGACY_PREGLOBAL),
# Dashcam
TestRoute("5ab784f361e19b78|2020-06-08--16-30-41", SUBARU.OUTBACK_PREGLOBAL), TestRoute("5ab784f361e19b78|2020-06-08--16-30-41", SUBARU.OUTBACK_PREGLOBAL),
# Dashcam
TestRoute("e19eb5d5353b1ac1|2020-08-09--14-37-56", SUBARU.OUTBACK_PREGLOBAL_2018), TestRoute("e19eb5d5353b1ac1|2020-08-09--14-37-56", SUBARU.OUTBACK_PREGLOBAL_2018),
TestRoute("fbbfa6af821552b9|2020-03-03--08-09-43", NISSAN.XTRAIL), TestRoute("fbbfa6af821552b9|2020-03-03--08-09-43", NISSAN.XTRAIL),

@ -22,6 +22,7 @@ COMMA BODY: [.nan, 1000, .nan]
# Totally new cars # Totally new cars
KIA EV6 2022: [3.5, 2.5, 0.0] KIA EV6 2022: [3.5, 2.5, 0.0]
RAM 1500 5TH GEN: [2.0, 2.0, 0.0] RAM 1500 5TH GEN: [2.0, 2.0, 0.0]
SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11]
# Dashcam or fallback configured as ideal car # Dashcam or fallback configured as ideal car
mock: [10.0, 10, 0.0] mock: [10.0, 10, 0.0]

@ -1 +1 @@
912413daa4c36a788cf2c801fc49829d46ae3072 01e23eec1394b07d2c537f0e28493b10e05923c2
Loading…
Cancel
Save