values.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. from dataclasses import dataclass, field
  2. from enum import Enum, IntFlag
  3. from cereal import car
  4. from panda.python import uds
  5. from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
  6. from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Tool, Column
  7. from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
  8. Ecu = car.CarParams.Ecu
  9. class CarControllerParams:
  10. def __init__(self, CP):
  11. self.STEER_STEP = 2 # how often we update the steer cmd
  12. self.STEER_DELTA_UP = 50 # torque increase per refresh, 0.8s to max
  13. self.STEER_DELTA_DOWN = 70 # torque decrease per refresh
  14. self.STEER_DRIVER_ALLOWANCE = 60 # allowed driver torque before start limiting
  15. self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily
  16. self.STEER_DRIVER_FACTOR = 1 # from dbc
  17. if CP.flags & SubaruFlags.GLOBAL_GEN2:
  18. self.STEER_MAX = 1000
  19. self.STEER_DELTA_UP = 40
  20. self.STEER_DELTA_DOWN = 40
  21. elif CP.carFingerprint == CAR.IMPREZA_2020:
  22. self.STEER_MAX = 1439
  23. else:
  24. self.STEER_MAX = 2047
  25. THROTTLE_MIN = 808
  26. THROTTLE_MAX = 3400
  27. THROTTLE_INACTIVE = 1818 # corresponds to zero acceleration
  28. THROTTLE_ENGINE_BRAKE = 808 # while braking, eyesight sets throttle to this, probably for engine braking
  29. BRAKE_MIN = 0
  30. BRAKE_MAX = 600 # about -3.5m/s2 from testing
  31. RPM_MIN = 0
  32. RPM_MAX = 2400
  33. RPM_INACTIVE = 600 # a good base rpm for zero acceleration
  34. THROTTLE_LOOKUP_BP = [0, 2]
  35. THROTTLE_LOOKUP_V = [THROTTLE_INACTIVE, THROTTLE_MAX]
  36. RPM_LOOKUP_BP = [0, 2]
  37. RPM_LOOKUP_V = [RPM_INACTIVE, RPM_MAX]
  38. BRAKE_LOOKUP_BP = [-3.5, 0]
  39. BRAKE_LOOKUP_V = [BRAKE_MAX, BRAKE_MIN]
  40. class SubaruFlags(IntFlag):
  41. # Detected flags
  42. SEND_INFOTAINMENT = 1
  43. DISABLE_EYESIGHT = 2
  44. # Static flags
  45. GLOBAL_GEN2 = 4
  46. # Cars that temporarily fault when steering angle rate is greater than some threshold.
  47. # Appears to be all torque-based cars produced around 2019 - present
  48. STEER_RATE_LIMITED = 8
  49. PREGLOBAL = 16
  50. HYBRID = 32
  51. LKAS_ANGLE = 64
  52. GLOBAL_ES_ADDR = 0x787
  53. GEN2_ES_BUTTONS_DID = b'\x11\x30'
  54. class CanBus:
  55. main = 0
  56. alt = 1
  57. camera = 2
  58. class Footnote(Enum):
  59. GLOBAL = CarFootnote(
  60. "In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance.",
  61. Column.PACKAGE)
  62. EXP_LONG = CarFootnote(
  63. "Enabling longitudinal control (alpha) will disable all EyeSight functionality, including AEB, LDW, and RAB.",
  64. Column.LONGITUDINAL)
  65. @dataclass
  66. class SubaruCarInfo(CarInfo):
  67. package: str = "EyeSight Driver Assistance"
  68. car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.subaru_a]))
  69. footnotes: list[Enum] = field(default_factory=lambda: [Footnote.GLOBAL])
  70. def init_make(self, CP: car.CarParams):
  71. self.car_parts.parts.extend([Tool.socket_8mm_deep, Tool.pry_tool])
  72. if CP.experimentalLongitudinalAvailable:
  73. self.footnotes.append(Footnote.EXP_LONG)
  74. @dataclass
  75. class SubaruPlatformConfig(PlatformConfig):
  76. dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('subaru_global_2017_generated', None))
  77. def init(self):
  78. if self.flags & SubaruFlags.HYBRID:
  79. self.dbc_dict = dbc_dict('subaru_global_2020_hybrid_generated', None)
  80. @dataclass
  81. class SubaruGen2PlatformConfig(SubaruPlatformConfig):
  82. def init(self):
  83. super().init()
  84. self.flags |= SubaruFlags.GLOBAL_GEN2
  85. if not (self.flags & SubaruFlags.LKAS_ANGLE):
  86. self.flags |= SubaruFlags.STEER_RATE_LIMITED
  87. class CAR(Platforms):
  88. # Global platform
  89. ASCENT = SubaruPlatformConfig(
  90. "SUBARU ASCENT LIMITED 2019",
  91. SubaruCarInfo("Subaru Ascent 2019-21", "All"),
  92. CarSpecs(mass=2031, wheelbase=2.89, steerRatio=13.5),
  93. )
  94. OUTBACK = SubaruGen2PlatformConfig(
  95. "SUBARU OUTBACK 6TH GEN",
  96. SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
  97. CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
  98. )
  99. LEGACY = SubaruGen2PlatformConfig(
  100. "SUBARU LEGACY 7TH GEN",
  101. SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
  102. OUTBACK.specs,
  103. )
  104. IMPREZA = SubaruPlatformConfig(
  105. "SUBARU IMPREZA LIMITED 2019",
  106. [
  107. SubaruCarInfo("Subaru Impreza 2017-19"),
  108. SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
  109. SubaruCarInfo("Subaru XV 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
  110. ],
  111. CarSpecs(mass=1568, wheelbase=2.67, steerRatio=15),
  112. )
  113. IMPREZA_2020 = SubaruPlatformConfig(
  114. "SUBARU IMPREZA SPORT 2020",
  115. [
  116. SubaruCarInfo("Subaru Impreza 2020-22"),
  117. SubaruCarInfo("Subaru Crosstrek 2020-23"),
  118. SubaruCarInfo("Subaru XV 2020-21"),
  119. ],
  120. CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17),
  121. flags=SubaruFlags.STEER_RATE_LIMITED,
  122. )
  123. # TODO: is there an XV and Impreza too?
  124. CROSSTREK_HYBRID = SubaruPlatformConfig(
  125. "SUBARU CROSSTREK HYBRID 2020",
  126. SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])),
  127. CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17),
  128. flags=SubaruFlags.HYBRID,
  129. )
  130. FORESTER = SubaruPlatformConfig(
  131. "SUBARU FORESTER 2019",
  132. SubaruCarInfo("Subaru Forester 2019-21", "All"),
  133. CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
  134. flags=SubaruFlags.STEER_RATE_LIMITED,
  135. )
  136. FORESTER_HYBRID = SubaruPlatformConfig(
  137. "SUBARU FORESTER HYBRID 2020",
  138. SubaruCarInfo("Subaru Forester Hybrid 2020"),
  139. FORESTER.specs,
  140. flags=SubaruFlags.HYBRID,
  141. )
  142. # Pre-global
  143. FORESTER_PREGLOBAL = SubaruPlatformConfig(
  144. "SUBARU FORESTER 2017 - 2018",
  145. SubaruCarInfo("Subaru Forester 2017-18"),
  146. CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20),
  147. dbc_dict('subaru_forester_2017_generated', None),
  148. flags=SubaruFlags.PREGLOBAL,
  149. )
  150. LEGACY_PREGLOBAL = SubaruPlatformConfig(
  151. "SUBARU LEGACY 2015 - 2018",
  152. SubaruCarInfo("Subaru Legacy 2015-18"),
  153. CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5),
  154. dbc_dict('subaru_outback_2015_generated', None),
  155. flags=SubaruFlags.PREGLOBAL,
  156. )
  157. OUTBACK_PREGLOBAL = SubaruPlatformConfig(
  158. "SUBARU OUTBACK 2015 - 2017",
  159. SubaruCarInfo("Subaru Outback 2015-17"),
  160. FORESTER_PREGLOBAL.specs,
  161. dbc_dict('subaru_outback_2015_generated', None),
  162. flags=SubaruFlags.PREGLOBAL,
  163. )
  164. OUTBACK_PREGLOBAL_2018 = SubaruPlatformConfig(
  165. "SUBARU OUTBACK 2018 - 2019",
  166. SubaruCarInfo("Subaru Outback 2018-19"),
  167. FORESTER_PREGLOBAL.specs,
  168. dbc_dict('subaru_outback_2019_generated', None),
  169. flags=SubaruFlags.PREGLOBAL,
  170. )
  171. # Angle LKAS
  172. FORESTER_2022 = SubaruPlatformConfig(
  173. "SUBARU FORESTER 2022",
  174. SubaruCarInfo("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c])),
  175. FORESTER.specs,
  176. flags=SubaruFlags.LKAS_ANGLE,
  177. )
  178. OUTBACK_2023 = SubaruGen2PlatformConfig(
  179. "SUBARU OUTBACK 7TH GEN",
  180. SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
  181. OUTBACK.specs,
  182. flags=SubaruFlags.LKAS_ANGLE,
  183. )
  184. ASCENT_2023 = SubaruGen2PlatformConfig(
  185. "SUBARU ASCENT 2023",
  186. SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
  187. ASCENT.specs,
  188. flags=SubaruFlags.LKAS_ANGLE,
  189. )
  190. SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
  191. p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
  192. SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
  193. p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
  194. # The EyeSight ECU takes 10s to respond to SUBARU_VERSION_REQUEST properly,
  195. # log this alternate manufacturer-specific query
  196. SUBARU_ALT_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
  197. p16(0xf100)
  198. SUBARU_ALT_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
  199. p16(0xf100)
  200. FW_QUERY_CONFIG = FwQueryConfig(
  201. requests=[
  202. Request(
  203. [StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
  204. [StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
  205. whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
  206. logging=True,
  207. ),
  208. # Non-OBD requests
  209. # Some Eyesight modules fail on TESTER_PRESENT_REQUEST
  210. # TODO: check if this resolves the fingerprinting issue for the 2023 Ascent and other new Subaru cars
  211. Request(
  212. [SUBARU_VERSION_REQUEST],
  213. [SUBARU_VERSION_RESPONSE],
  214. whitelist_ecus=[Ecu.fwdCamera],
  215. bus=0,
  216. ),
  217. Request(
  218. [SUBARU_ALT_VERSION_REQUEST],
  219. [SUBARU_ALT_VERSION_RESPONSE],
  220. whitelist_ecus=[Ecu.fwdCamera],
  221. bus=0,
  222. logging=True,
  223. ),
  224. Request(
  225. [StdQueries.DEFAULT_DIAGNOSTIC_REQUEST, StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
  226. [StdQueries.DEFAULT_DIAGNOSTIC_RESPONSE, StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
  227. whitelist_ecus=[Ecu.fwdCamera],
  228. bus=0,
  229. logging=True,
  230. ),
  231. Request(
  232. [StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
  233. [StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
  234. whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
  235. bus=0,
  236. ),
  237. # GEN2 powertrain bus query
  238. Request(
  239. [StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
  240. [StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
  241. whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
  242. bus=1,
  243. obd_multiplexing=False,
  244. ),
  245. ],
  246. # We don't get the EPS from non-OBD queries on GEN2 cars. Note that we still attempt to match when it exists
  247. non_essential_ecus={
  248. Ecu.eps: list(CAR.with_flags(SubaruFlags.GLOBAL_GEN2)),
  249. }
  250. )
  251. DBC = CAR.create_dbc_map()
  252. if __name__ == "__main__":
  253. CAR.print_debug(SubaruFlags)