Tesla AP2 port (#20074)

* squashed the PR

* remove AP passthrough

* disengage on steer override

* bump panda

* add test routes

* bump panda

* tesla is still dashcam only

* dashcam only

* rerun CI

* add to release files
pull/21385/head
robbederks 4 years ago committed by GitHub
parent 7e6a9ce9a5
commit cdfba105ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      panda
  2. 9
      release/files_common
  3. 0
      selfdrive/car/tesla/__init__.py
  4. 53
      selfdrive/car/tesla/carcontroller.py
  5. 181
      selfdrive/car/tesla/carstate.py
  6. 62
      selfdrive/car/tesla/interface.py
  7. 5
      selfdrive/car/tesla/radar_interface.py
  8. 41
      selfdrive/car/tesla/teslacan.py
  9. 53
      selfdrive/car/tesla/values.py
  10. 3
      selfdrive/test/process_replay/test_processes.py
  11. 3
      selfdrive/test/test_routes.py

@ -1 +1 @@
Subproject commit d7b3ae02824fe67f88ee408e0d7540c1ec5bceab
Subproject commit d8385413c3ecc59799f0909c7384f385a71c4431

@ -170,6 +170,13 @@ selfdrive/car/mazda/radar_interface.py
selfdrive/car/mazda/values.py
selfdrive/car/mazda/carcontroller.py
selfdrive/car/mazda/mazdacan.py
selfdrive/car/tesla/__init__.py
selfdrive/car/tesla/teslacan.py
selfdrive/car/tesla/carcontroller.py
selfdrive/car/tesla/radar_interface.py
selfdrive/car/tesla/values.py
selfdrive/car/tesla/carstate.py
selfdrive/car/tesla/interface.py
selfdrive/car/mock/*.py
selfdrive/clocksd/.gitignore
@ -611,3 +618,5 @@ opendbc/toyota_adas.dbc
opendbc/toyota_tss2_adas.dbc
opendbc/vw_mqb_2010.dbc
opendbc/tesla_can.dbc

@ -0,0 +1,53 @@
from common.numpy_fast import clip, interp
from selfdrive.car.tesla.teslacan import TeslaCAN
from opendbc.can.packer import CANPacker
from selfdrive.car.tesla.values import CANBUS, CarControllerParams
class CarController():
def __init__(self, dbc_name, CP, VM):
self.CP = CP
self.last_angle = 0
self.packer = CANPacker(dbc_name)
self.tesla_can = TeslaCAN(dbc_name, self.packer)
def update(self, enabled, CS, frame, actuators, cruise_cancel):
can_sends = []
# Temp disable steering on a hands_on_fault, and allow for user override
hands_on_fault = (CS.steer_warning == "EAC_ERROR_HANDS_ON" and CS.hands_on_level >= 3)
lkas_enabled = enabled and (not hands_on_fault)
if lkas_enabled:
apply_angle = actuators.steeringAngleDeg
# Angular rate limit based on speed
steer_up = (self.last_angle * apply_angle > 0. and abs(apply_angle) > abs(self.last_angle))
rate_limit = CarControllerParams.RATE_LIMIT_UP if steer_up else CarControllerParams.RATE_LIMIT_DOWN
max_angle_diff = interp(CS.out.vEgo, rate_limit.speed_points, rate_limit.max_angle_diff_points)
apply_angle = clip(apply_angle, (self.last_angle - max_angle_diff), (self.last_angle + max_angle_diff))
# To not fault the EPS
apply_angle = clip(apply_angle, (CS.out.steeringAngleDeg - 20), (CS.out.steeringAngleDeg + 20))
else:
apply_angle = CS.out.steeringAngleDeg
self.last_angle = apply_angle
can_sends.append(self.tesla_can.create_steering_control(apply_angle, lkas_enabled, frame))
# Cancel on user steering override, since there is no steering torque blending
if hands_on_fault:
cruise_cancel = True
# Cancel when openpilot is not enabled anymore
if not enabled and bool(CS.out.cruiseState.enabled):
cruise_cancel = True
if ((frame % 10) == 0 and cruise_cancel):
# Spam every possible counter value, otherwise it might not be accepted
for counter in range(16):
can_sends.append(self.tesla_can.create_action_request(CS.msg_stw_actn_req, cruise_cancel, CANBUS.chassis, counter))
can_sends.append(self.tesla_can.create_action_request(CS.msg_stw_actn_req, cruise_cancel, CANBUS.autopilot, counter))
# TODO: HUD control
return can_sends

@ -0,0 +1,181 @@
import copy
from cereal import car
from selfdrive.car.tesla.values import DBC, CANBUS, GEAR_MAP, DOORS, BUTTONS
from selfdrive.car.interfaces import CarStateBase
from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine
from selfdrive.config import Conversions as CV
class CarState(CarStateBase):
def __init__(self, CP):
super().__init__(CP)
self.button_states = {button.event_type: False for button in BUTTONS}
self.can_define = CANDefine(DBC[CP.carFingerprint]['chassis'])
# Needed by carcontroller
self.msg_stw_actn_req = None
self.hands_on_level = 0
self.steer_warning = None
def update(self, cp, cp_cam):
ret = car.CarState.new_message()
# Vehicle speed
ret.vEgoRaw = cp.vl["ESP_B"]["ESP_vehicleSpeed"] * CV.KPH_TO_MS
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = (ret.vEgo < 0.1)
# Gas pedal
ret.gas = cp.vl["DI_torque1"]["DI_pedalPos"] / 100.0
ret.gasPressed = (ret.gas > 0)
# Brake pedal
ret.brake = 0
ret.brakePressed = bool(cp.vl["BrakeMessage"]["driverBrakeStatus"] != 1)
# Steering wheel
self.hands_on_level = cp.vl["EPAS_sysStatus"]["EPAS_handsOnLevel"]
self.steer_warning = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacErrorCode"].get(int(cp.vl["EPAS_sysStatus"]["EPAS_eacErrorCode"]), None)
steer_status = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacStatus"].get(int(cp.vl["EPAS_sysStatus"]["EPAS_eacStatus"]), None)
ret.steeringAngleDeg = -cp.vl["EPAS_sysStatus"]["EPAS_internalSAS"]
ret.steeringRateDeg = -cp.vl["STW_ANGLHP_STAT"]["StW_AnglHP_Spd"] # This is from a different angle sensor, and at different rate
ret.steeringTorque = -cp.vl["EPAS_sysStatus"]["EPAS_torsionBarTorque"]
ret.steeringPressed = (self.hands_on_level > 0)
ret.steerError = steer_status in ["EAC_FAULT", "EAC_INHIBITED"]
ret.steerWarning = self.steer_warning in ["EAC_ERROR_MAX_SPEED", "EAC_ERROR_MIN_SPEED", "EAC_ERROR_TMP_FAULT", "SNA"] # TODO: not sure if this list is complete
# Cruise state
cruise_state = self.can_define.dv["DI_state"]["DI_cruiseState"].get(int(cp.vl["DI_state"]["DI_cruiseState"]), None)
speed_units = self.can_define.dv["DI_state"]["DI_speedUnits"].get(int(cp.vl["DI_state"]["DI_speedUnits"]), None)
acc_enabled = (cruise_state in ["ENABLED", "STANDSTILL", "OVERRIDE", "PRE_FAULT", "PRE_CANCEL"])
ret.cruiseState.enabled = acc_enabled
if speed_units == "KPH":
ret.cruiseState.speed = cp.vl["DI_state"]["DI_digitalSpeed"] * CV.KPH_TO_MS
elif speed_units == "MPH":
ret.cruiseState.speed = cp.vl["DI_state"]["DI_digitalSpeed"] * CV.MPH_TO_MS
ret.cruiseState.available = ((cruise_state == "STANDBY") or ret.cruiseState.enabled)
ret.cruiseState.standstill = (cruise_state == "STANDSTILL")
# Gear
ret.gearShifter = GEAR_MAP[self.can_define.dv["DI_torque2"]["DI_gear"].get(int(cp.vl["DI_torque2"]["DI_gear"]), "DI_GEAR_INVALID")]
# Buttons
buttonEvents = []
for button in BUTTONS:
state = (cp.vl[button.can_addr][button.can_msg] in button.values)
if self.button_states[button.event_type] != state:
event = car.CarState.ButtonEvent.new_message()
event.type = button.event_type
event.pressed = state
buttonEvents.append(event)
self.button_states[button.event_type] = state
ret.buttonEvents = buttonEvents
# Doors
ret.doorOpen = any([(self.can_define.dv["GTW_carState"][door].get(int(cp.vl["GTW_carState"][door]), "OPEN") == "OPEN") for door in DOORS])
# Blinkers
ret.leftBlinker = (cp.vl["GTW_carState"]["BC_indicatorLStatus"] == 1)
ret.rightBlinker = (cp.vl["GTW_carState"]["BC_indicatorRStatus"] == 1)
# Seatbelt
ret.seatbeltUnlatched = (cp.vl["SDM1"]["SDM_bcklDrivStatus"] != 1)
# TODO: blindspot
# Messages needed by carcontroller
self.msg_stw_actn_req = copy.copy(cp.vl["STW_ACTN_RQ"])
return ret
@staticmethod
def get_can_parser(CP):
signals = [
# sig_name, sig_address, default
("ESP_vehicleSpeed", "ESP_B", 0),
("DI_pedalPos", "DI_torque1", 0),
("DI_brakePedal", "DI_torque2", 0),
("StW_AnglHP", "STW_ANGLHP_STAT", 0),
("StW_AnglHP_Spd", "STW_ANGLHP_STAT", 0),
("EPAS_handsOnLevel", "EPAS_sysStatus", 0),
("EPAS_torsionBarTorque", "EPAS_sysStatus", 0),
("EPAS_internalSAS", "EPAS_sysStatus", 0),
("EPAS_eacStatus", "EPAS_sysStatus", 1),
("EPAS_eacErrorCode", "EPAS_sysStatus", 0),
("DI_cruiseState", "DI_state", 0),
("DI_digitalSpeed", "DI_state", 0),
("DI_speedUnits", "DI_state", 0),
("DI_gear", "DI_torque2", 0),
("DOOR_STATE_FL", "GTW_carState", 1),
("DOOR_STATE_FR", "GTW_carState", 1),
("DOOR_STATE_RL", "GTW_carState", 1),
("DOOR_STATE_RR", "GTW_carState", 1),
("DOOR_STATE_FrontTrunk", "GTW_carState", 1),
("BOOT_STATE", "GTW_carState", 1),
("BC_indicatorLStatus", "GTW_carState", 1),
("BC_indicatorRStatus", "GTW_carState", 1),
("SDM_bcklDrivStatus", "SDM1", 0),
("driverBrakeStatus", "BrakeMessage", 0),
# We copy this whole message when spamming cancel
("SpdCtrlLvr_Stat", "STW_ACTN_RQ", 0),
("VSL_Enbl_Rq", "STW_ACTN_RQ", 0),
("SpdCtrlLvrStat_Inv", "STW_ACTN_RQ", 0),
("DTR_Dist_Rq", "STW_ACTN_RQ", 0),
("TurnIndLvr_Stat", "STW_ACTN_RQ", 0),
("HiBmLvr_Stat", "STW_ACTN_RQ", 0),
("WprWashSw_Psd", "STW_ACTN_RQ", 0),
("WprWash_R_Sw_Posn_V2", "STW_ACTN_RQ", 0),
("StW_Lvr_Stat", "STW_ACTN_RQ", 0),
("StW_Cond_Flt", "STW_ACTN_RQ", 0),
("StW_Cond_Psd", "STW_ACTN_RQ", 0),
("HrnSw_Psd", "STW_ACTN_RQ", 0),
("StW_Sw00_Psd", "STW_ACTN_RQ", 0),
("StW_Sw01_Psd", "STW_ACTN_RQ", 0),
("StW_Sw02_Psd", "STW_ACTN_RQ", 0),
("StW_Sw03_Psd", "STW_ACTN_RQ", 0),
("StW_Sw04_Psd", "STW_ACTN_RQ", 0),
("StW_Sw05_Psd", "STW_ACTN_RQ", 0),
("StW_Sw06_Psd", "STW_ACTN_RQ", 0),
("StW_Sw07_Psd", "STW_ACTN_RQ", 0),
("StW_Sw08_Psd", "STW_ACTN_RQ", 0),
("StW_Sw09_Psd", "STW_ACTN_RQ", 0),
("StW_Sw10_Psd", "STW_ACTN_RQ", 0),
("StW_Sw11_Psd", "STW_ACTN_RQ", 0),
("StW_Sw12_Psd", "STW_ACTN_RQ", 0),
("StW_Sw13_Psd", "STW_ACTN_RQ", 0),
("StW_Sw14_Psd", "STW_ACTN_RQ", 0),
("StW_Sw15_Psd", "STW_ACTN_RQ", 0),
("WprSw6Posn", "STW_ACTN_RQ", 0),
("MC_STW_ACTN_RQ", "STW_ACTN_RQ", 0),
("CRC_STW_ACTN_RQ", "STW_ACTN_RQ", 0),
]
checks = [
# sig_address, frequency
("ESP_B", 50),
("DI_torque1", 100),
("DI_torque2", 100),
("STW_ANGLHP_STAT", 100),
("EPAS_sysStatus", 25),
("DI_state", 10),
("STW_ACTN_RQ", 10),
("GTW_carState", 10),
("SDM1", 10),
("BrakeMessage", 50),
]
return CANParser(DBC[CP.carFingerprint]['chassis'], signals, checks, CANBUS.chassis)
@staticmethod
def get_cam_can_parser(CP):
signals = [
# sig_name, sig_address, default
]
checks = [
# sig_address, frequency
]
return CANParser(DBC[CP.carFingerprint]['chassis'], signals, checks, CANBUS.autopilot)

@ -0,0 +1,62 @@
#!/usr/bin/env python3
from cereal import car
from selfdrive.car.tesla.values import CAR
from selfdrive.car import STD_CARGO_KG, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness
from selfdrive.car.interfaces import CarInterfaceBase
class CarInterface(CarInterfaceBase):
@staticmethod
def compute_gb(accel, speed):
# TODO: is this correct?
return accel
@staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "tesla"
ret.safetyModel = car.CarParams.SafetyModel.tesla
# There is no safe way to do steer blending with user torque,
# so the steering behaves like autopilot. This is not
# how openpilot should be, hence dashcamOnly
ret.dashcamOnly = True
ret.steerControlType = car.CarParams.SteerControlType.angle
ret.enableCamera = True
ret.openpilotLongitudinalControl = False
ret.communityFeature = True
ret.steerActuatorDelay = 0.1
ret.steerRateCost = 0.5
if candidate == CAR.AP2_MODELS:
ret.mass = 2100. + STD_CARGO_KG
ret.wheelbase = 2.959
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 13.5
else:
raise ValueError(f"Unsupported car: {candidate}")
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront)
return ret
def update(self, c, can_strings):
self.cp.update_strings(can_strings)
self.cp_cam.update_strings(can_strings)
ret = self.CS.update(self.cp, self.cp_cam)
ret.canValid = self.cp.can_valid and self.cp_cam.can_valid
events = self.create_common_events(ret)
ret.events = events.to_msg()
self.CS.out = ret.as_reader()
return self.CS.out
def apply(self, c):
can_sends = self.CC.update(c.enabled, self.CS, self.frame, c.actuators, c.cruiseControl.cancel)
self.frame += 1
return can_sends

@ -0,0 +1,5 @@
#!/usr/bin/env python3
from selfdrive.car.interfaces import RadarInterfaceBase
class RadarInterface(RadarInterfaceBase):
pass

@ -0,0 +1,41 @@
import copy
import crcmod
from opendbc.can.can_define import CANDefine
from selfdrive.car.tesla.values import CANBUS
class TeslaCAN:
def __init__(self, dbc_name, packer):
self.can_define = CANDefine(dbc_name)
self.packer = packer
self.crc = crcmod.mkCrcFun(0x11d, initCrc=0x00, rev=False, xorOut=0xff)
@staticmethod
def checksum(msg_id, dat):
# TODO: get message ID from name instead
ret = (msg_id & 0xFF) + ((msg_id >> 8) & 0xFF)
ret += sum(dat)
return ret & 0xFF
def create_steering_control(self, angle, enabled, frame):
values = {
"DAS_steeringAngleRequest": -angle,
"DAS_steeringHapticRequest": 0,
"DAS_steeringControlType": 1 if enabled else 0,
"DAS_steeringControlCounter": (frame % 16),
}
data = self.packer.make_can_msg("DAS_steeringControl", CANBUS.chassis, values)[2]
values["DAS_steeringControlChecksum"] = self.checksum(0x488, data[:3])
return self.packer.make_can_msg("DAS_steeringControl", CANBUS.chassis, values)
def create_action_request(self, msg_stw_actn_req, cancel, bus, counter):
values = copy.copy(msg_stw_actn_req)
if cancel:
values["SpdCtrlLvr_Stat"] = 1
values["MC_STW_ACTN_RQ"] = counter
data = self.packer.make_can_msg("STW_ACTN_RQ", bus, values)[2]
values["CRC_STW_ACTN_RQ"] = self.crc(data[:7])
return self.packer.make_can_msg("STW_ACTN_RQ", bus, values)

@ -0,0 +1,53 @@
# flake8: noqa
from collections import namedtuple
from selfdrive.car import dbc_dict
from cereal import car
Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values'])
AngleRateLimit = namedtuple('AngleRateLimit', ['speed_points', 'max_angle_diff_points'])
class CAR:
AP2_MODELS = 'TESLA AP2 MODEL S'
FINGERPRINTS = {
CAR.AP2_MODELS: [
{
1: 8, 3: 8, 14: 8, 21: 4, 69: 8, 109: 4, 257: 3, 264: 8, 277: 6, 280: 6, 293: 4, 296: 4, 309: 5, 325: 8, 328: 5, 336: 8, 341: 8, 360: 7, 373: 8, 389: 8, 415: 8, 513: 5, 516: 8, 518: 8, 520: 4, 522: 8, 524: 8, 526: 8, 532: 3, 536: 8, 537: 3, 542: 8, 551: 5, 552: 2, 556: 8, 558: 8, 568: 8, 569: 8, 574: 8, 577: 8, 582: 5, 583: 8, 584: 4, 585: 8, 590: 8, 601: 8, 606: 8, 608: 1, 622: 8, 627: 6, 638: 8, 641: 8, 643: 8, 692: 8, 693: 8, 695: 8, 696: 8, 697: 8, 699: 8, 700: 8, 701: 8, 702: 8, 703: 8, 704: 8, 708: 8, 709: 8, 710: 8, 711: 8, 712: 8, 728: 8, 744: 8, 760: 8, 772: 8, 775: 8, 776: 8, 777: 8, 778: 8, 782: 8, 788: 8, 791: 8, 792: 8, 796: 2, 797: 8, 798: 6, 799: 8, 804: 8, 805: 8, 807: 8, 808: 1, 811: 8, 812: 8, 813: 8, 814: 5, 815: 8, 820: 8, 823: 8, 824: 8, 829: 8, 830: 5, 836: 8, 840: 8, 845: 8, 846: 5, 848: 8, 852: 8, 853: 8, 856: 4, 857: 6, 861: 8, 862: 5, 872: 8, 876: 8, 877: 8, 879: 8, 880: 8, 882: 8, 884: 8, 888: 8, 893: 8, 894: 8, 901: 6, 904: 3, 905: 8, 906: 8, 908: 2, 909: 8, 910: 8, 912: 8, 920: 8, 921: 8, 925: 4, 926: 6, 936: 8, 941: 8, 949: 8, 952: 8, 953: 6, 968: 8, 969: 6, 970: 8, 971: 8, 977: 8, 984: 8, 987: 8, 990: 8, 1000: 8, 1001: 8, 1006: 8, 1007: 8, 1008: 8, 1010: 6, 1014: 1, 1015: 8, 1016: 8, 1017: 8, 1018: 8, 1020: 8, 1026: 8, 1028: 8, 1029: 8, 1030: 8, 1032: 1, 1033: 1, 1034: 8, 1048: 1, 1049: 8, 1061: 8, 1064: 8, 1065: 8, 1070: 8, 1080: 8, 1081: 8, 1097: 8, 1113: 8, 1129: 8, 1145: 8, 1160: 4, 1177: 8, 1281: 8, 1328: 8, 1329: 8, 1332: 8, 1335: 8, 1337: 8, 1353: 8, 1368: 8, 1412: 8, 1436: 8, 1476: 8, 1481: 8, 1497: 8, 1513: 8, 1519: 8, 1601: 8, 1605: 8, 1617: 8, 1621: 8, 1800: 4, 1804: 8, 1812: 8, 1815: 8, 1816: 8, 1824: 8, 1828: 8, 1831: 8, 1832: 8, 1864: 8, 1880: 8, 1892: 8, 1896: 8, 1912: 8, 1960: 8, 1992: 8, 2008: 3, 2043: 5, 2045: 4
},
],
}
DBC = {
CAR.AP2_MODELS: dbc_dict(None, 'tesla_radar', chassis_dbc='tesla_can'),
}
class CANBUS:
chassis = 0
autopilot = 2
radar = 1
GEAR_MAP = {
"DI_GEAR_INVALID": car.CarState.GearShifter.unknown,
"DI_GEAR_P": car.CarState.GearShifter.park,
"DI_GEAR_R": car.CarState.GearShifter.reverse,
"DI_GEAR_N": car.CarState.GearShifter.neutral,
"DI_GEAR_D": car.CarState.GearShifter.drive,
"DI_GEAR_SNA": car.CarState.GearShifter.unknown,
}
DOORS = ["DOOR_STATE_FL", "DOOR_STATE_FR", "DOOR_STATE_RL", "DOOR_STATE_RR", "DOOR_STATE_FrontTrunk", "BOOT_STATE"]
# Make sure the message and addr is also in the CAN parser!
BUTTONS = [
Button(car.CarState.ButtonEvent.Type.leftBlinker, "STW_ACTN_RQ", "TurnIndLvr_Stat", [1]),
Button(car.CarState.ButtonEvent.Type.rightBlinker, "STW_ACTN_RQ", "TurnIndLvr_Stat", [2]),
Button(car.CarState.ButtonEvent.Type.accelCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [4, 16]),
Button(car.CarState.ButtonEvent.Type.decelCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [8, 32]),
Button(car.CarState.ButtonEvent.Type.cancel, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [2]),
Button(car.CarState.ButtonEvent.Type.resumeCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [1]),
]
class CarControllerParams:
RATE_LIMIT_UP = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., .8, .15])
RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4])

@ -25,10 +25,11 @@ segments = [
# Enable when port is tested and dascamOnly is no longer set
#("MAZDA", "32a319f057902bb3|2020-04-27--15-18-58--2"), # MAZDA.CX5
#("TESLA", "bb50caf5f0945ab1|2021-06-19--17-20-18--3"), # TESLA.AP2_MODELS
]
# dashcamOnly makes don't need to be tested until a full port is done
excluded_interfaces = ["mock", "ford", "mazda"]
excluded_interfaces = ["mock", "ford", "mazda", "tesla"]
BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/"

@ -11,6 +11,7 @@ from selfdrive.car.mazda.values import CAR as MAZDA
from selfdrive.car.subaru.values import CAR as SUBARU
from selfdrive.car.toyota.values import CAR as TOYOTA
from selfdrive.car.volkswagen.values import CAR as VOLKSWAGEN
from selfdrive.car.tesla.values import CAR as TESLA
# TODO: add routes for these cars
non_tested_cars = [
@ -164,6 +165,8 @@ routes = [
TestRoute("10b5a4b380434151|2020-08-26--17-11-45", MAZDA.CX9),
TestRoute("74f1038827005090|2020-08-26--20-05-50", MAZDA.MAZDA3),
TestRoute("fb53c640f499b73d|2021-06-01--04-17-56", MAZDA.MAZDA6),
TestRoute("bb50caf5f0945ab1|2021-06-19--17-20-18", TESLA.AP2_MODELS),
]
forced_dashcam_routes = [

Loading…
Cancel
Save