Merge branch 'master' of https://github.com/commaai/openpilot into mqb-long

pull/23257/head
Jason Young 4 years ago
commit bbd29c8210
  1. 1
      RELEASES.md
  2. 1
      docs/CARS.md
  3. 2
      panda
  4. 1
      release/files_common
  5. 10
      selfdrive/boardd/panda.cc
  6. 1
      selfdrive/boardd/panda.h
  7. 4
      selfdrive/car/honda/carstate.py
  8. 6
      selfdrive/car/honda/interface.py
  9. 4
      selfdrive/car/honda/values.py
  10. 20
      selfdrive/car/tesla/carcontroller.py
  11. 8
      selfdrive/car/tesla/carstate.py
  12. 27
      selfdrive/car/tesla/interface.py
  13. 28
      selfdrive/car/tesla/teslacan.py
  14. 16
      selfdrive/car/tesla/values.py
  15. 4
      selfdrive/car/volkswagen/interface.py
  16. 18
      selfdrive/car/volkswagen/values.py
  17. 16
      selfdrive/controls/radard.py
  18. 4
      selfdrive/loggerd/tests/test_uploader.py
  19. 4
      selfdrive/loggerd/uploader.py
  20. 7
      selfdrive/manager/manager.py
  21. 10
      selfdrive/swaglog.py
  22. 2
      selfdrive/test/test_models.py
  23. 1
      selfdrive/test/test_routes.py
  24. 4
      selfdrive/thermald/thermald.py
  25. 1
      selfdrive/ui/qt/api.cc
  26. 20
      selfdrive/ui/qt/maps/map.cc
  27. 1
      selfdrive/ui/qt/maps/map.h
  28. 11
      selfdrive/ui/qt/util.cc
  29. 2
      selfdrive/ui/qt/util.h
  30. 2
      tools/plotjuggler/juggle.py

@ -1,5 +1,6 @@
Version 0.8.12 (202X-XX-XX) Version 0.8.12 (202X-XX-XX)
======================== ========================
* Volkswagen T-Roc 2021 support thanks to jyoung8607!
Version 0.8.11 (2021-11-22) Version 0.8.11 (2021-11-22)
======================== ========================

@ -162,6 +162,7 @@
| Volkswagen| Passat 2016-18<sup>3</sup> | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| Passat 2016-18<sup>3</sup> | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Polo 2020 | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| Polo 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| T-Cross 2021<sup>4</sup> | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| T-Cross 2021<sup>4</sup> | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| T-Roc 2021<sup>4</sup> | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Taos 2022<sup>4</sup> | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| Taos 2022<sup>4</sup> | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Tiguan 2020 | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| Tiguan 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Touran 2017 | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| Touran 2017 | Driver Assistance | Stock | 0mph | 0mph |

@ -1 +1 @@
Subproject commit f3bacee55ae5d7f4197c98b697d6b37113df5199 Subproject commit ddb3698f9bf03dc6096a754c01529cddd580a976

@ -616,3 +616,4 @@ opendbc/vw_mqb_2010.dbc
opendbc/tesla_can.dbc opendbc/tesla_can.dbc
opendbc/tesla_radar.dbc opendbc/tesla_radar.dbc
opendbc/tesla_powertrain.dbc

@ -357,9 +357,9 @@ uint8_t Panda::len_to_dlc(uint8_t len) {
return len; return len;
} }
if (len <= 24) { if (len <= 24) {
return 8 + ((len - 8) / 4) + (len % 4) ? 1 : 0; return 8 + ((len - 8) / 4) + ((len % 4) ? 1 : 0);
} else { } else {
return 11 + (len / 16) + (len % 16) ? 1 : 0; return 11 + (len / 16) + ((len % 16) ? 1 : 0);
} }
} }
@ -433,15 +433,15 @@ bool Panda::can_receive(std::vector<can_frame>& out_vec) {
static uint8_t tail[CANPACKET_MAX_SIZE]; static uint8_t tail[CANPACKET_MAX_SIZE];
uint8_t tail_size = 0; uint8_t tail_size = 0;
uint8_t counter = 0; uint8_t counter = 0;
for (int i = 0; i < recv; i += 64) { for (int i = 0; i < recv; i += USBPACKET_MAX_SIZE) {
// Check for counter every 64 bytes (length of USB packet) // Check for counter every 64 bytes (length of USB packet)
if (counter != data[i]) { if (counter != data[i]) {
LOGE("CAN: MALFORMED USB RECV PACKET"); LOGE("CAN: MALFORMED USB RECV PACKET");
break; break;
} }
counter++; counter++;
uint8_t chunk_len = ((recv - i) > 64) ? 63 : (recv - i - 1); // as 1 is always reserved for counter uint8_t chunk_len = ((recv - i) > USBPACKET_MAX_SIZE) ? 63 : (recv - i - 1); // as 1 is always reserved for counter
uint8_t chunk[CANPACKET_MAX_SIZE]; uint8_t chunk[USBPACKET_MAX_SIZE + CANPACKET_MAX_SIZE];
memcpy(chunk, tail, tail_size); memcpy(chunk, tail, tail_size);
memcpy(&chunk[tail_size], &data[i+1], chunk_len); memcpy(&chunk[tail_size], &data[i+1], chunk_len);
chunk_len += tail_size; chunk_len += tail_size;

@ -17,6 +17,7 @@
#define PANDA_BUS_CNT 4 #define PANDA_BUS_CNT 4
#define RECV_SIZE (0x4000U) #define RECV_SIZE (0x4000U)
#define USB_TX_SOFT_LIMIT (0x100U) #define USB_TX_SOFT_LIMIT (0x100U)
#define USBPACKET_MAX_SIZE (0x40U)
#define CANPACKET_HEAD_SIZE 5U #define CANPACKET_HEAD_SIZE 5U
#define CANPACKET_MAX_SIZE 72U #define CANPACKET_MAX_SIZE 72U
#define CANPACKET_REJECTED (0xC0U) #define CANPACKET_REJECTED (0xC0U)

@ -5,7 +5,7 @@ from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from selfdrive.car.interfaces import CarStateBase from selfdrive.car.interfaces import CarStateBase
from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, SPEED_FACTOR, HONDA_BOSCH, HONDA_NIDEC_ALT_MAIN, HONDA_BOSCH_ALT_BRAKE_SIGNAL from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, SPEED_FACTOR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL
TransmissionType = car.CarParams.TransmissionType TransmissionType = car.CarParams.TransmissionType
@ -161,7 +161,7 @@ class CarState(CarStateBase):
self.gearbox_msg = "GEARBOX_15T" self.gearbox_msg = "GEARBOX_15T"
self.main_on_sig_msg = "SCM_FEEDBACK" self.main_on_sig_msg = "SCM_FEEDBACK"
if CP.carFingerprint in HONDA_NIDEC_ALT_MAIN: if CP.carFingerprint in HONDA_NIDEC_ALT_SCM_MESSAGES:
self.main_on_sig_msg = "SCM_BUTTONS" self.main_on_sig_msg = "SCM_BUTTONS"
self.shifter_values = can_define.dv[self.gearbox_msg]["GEAR_SHIFTER"] self.shifter_values = can_define.dv[self.gearbox_msg]["GEAR_SHIFTER"]

@ -3,7 +3,7 @@ from cereal import car
from panda import Panda from panda import Panda
from common.numpy_fast import interp from common.numpy_fast import interp
from common.params import Params from common.params import Params
from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_MAIN, HONDA_BOSCH_ALT_BRAKE_SIGNAL from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL
from selfdrive.car import STD_CARGO_KG, CivicParams, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car import STD_CARGO_KG, CivicParams, 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.disable_ecu import disable_ecu from selfdrive.car.disable_ecu import disable_ecu
@ -299,8 +299,8 @@ class CarInterface(CarInterfaceBase):
if candidate in HONDA_BOSCH_ALT_BRAKE_SIGNAL: if candidate in HONDA_BOSCH_ALT_BRAKE_SIGNAL:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_ALT_BRAKE ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_ALT_BRAKE
# These cars use an alternate main on message # These cars use alternate SCM messages (SCM_FEEDBACK AND SCM_BUTTON)
if candidate in HONDA_NIDEC_ALT_MAIN: if candidate in HONDA_NIDEC_ALT_SCM_MESSAGES:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_NIDEC_ALT ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_NIDEC_ALT
if ret.openpilotLongitudinalControl and candidate in HONDA_BOSCH: if ret.openpilotLongitudinalControl and candidate in HONDA_BOSCH:

@ -1365,7 +1365,7 @@ SPEED_FACTOR = {
} }
HONDA_NIDEC_ALT_PCM_ACCEL = set([CAR.ODYSSEY]) HONDA_NIDEC_ALT_PCM_ACCEL = set([CAR.ODYSSEY])
HONDA_NIDEC_ALT_MAIN = set([CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN, HONDA_NIDEC_ALT_SCM_MESSAGES = set([CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN,
CAR.PILOT, CAR.PILOT_2019, CAR.PASSPORT, CAR.RIDGELINE]) CAR.PILOT, CAR.PILOT_2019, CAR.PASSPORT, CAR.RIDGELINE])
HONDA_BOSCH = set([CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E]) HONDA_BOSCH = set([CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E])
HONDA_BOSCH_ALT_BRAKE_SIGNAL = set([CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G]) HONDA_BOSCH_ALT_BRAKE_SIGNAL = set([CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G])

@ -1,14 +1,16 @@
from common.numpy_fast import clip, interp from common.numpy_fast import clip, interp
from selfdrive.car.tesla.teslacan import TeslaCAN
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car.tesla.values import CANBUS, CarControllerParams from selfdrive.car.tesla.teslacan import TeslaCAN
from selfdrive.car.tesla.values import DBC, CANBUS, 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.last_angle = 0 self.last_angle = 0
self.long_control_counter = 0
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)
self.tesla_can = TeslaCAN(dbc_name, self.packer) self.pt_packer = CANPacker(DBC[CP.carFingerprint]['pt'])
self.tesla_can = TeslaCAN(self.packer, self.pt_packer)
def update(self, enabled, CS, frame, actuators, cruise_cancel): def update(self, enabled, CS, frame, actuators, cruise_cancel):
can_sends = [] can_sends = []
@ -34,6 +36,16 @@ class CarController():
self.last_angle = apply_angle self.last_angle = apply_angle
can_sends.append(self.tesla_can.create_steering_control(apply_angle, lkas_enabled, frame)) can_sends.append(self.tesla_can.create_steering_control(apply_angle, lkas_enabled, frame))
# Longitudinal control (40Hz)
if self.CP.openpilotLongitudinalControl and ((frame % 5) in [0, 2]):
target_accel = actuators.accel
target_speed = max(CS.out.vEgo + (target_accel * CarControllerParams.ACCEL_TO_SPEED_MULTIPLIER), 0)
max_accel = 0 if target_accel < 0 else target_accel
min_accel = 0 if target_accel > 0 else target_accel
can_sends.extend(self.tesla_can.create_longitudinal_commands(CS.acc_state, target_speed, min_accel, max_accel, self.long_control_counter))
self.long_control_counter += 1
# Cancel on user steering override, since there is no steering torque blending # Cancel on user steering override, since there is no steering torque blending
if hands_on_fault: if hands_on_fault:
cruise_cancel = True cruise_cancel = True
@ -46,7 +58,7 @@ class CarController():
# Spam every possible counter value, otherwise it might not be accepted # Spam every possible counter value, otherwise it might not be accepted
for counter in range(16): 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.chassis, counter))
can_sends.append(self.tesla_can.create_action_request(CS.msg_stw_actn_req, cruise_cancel, CANBUS.autopilot, counter)) can_sends.append(self.tesla_can.create_action_request(CS.msg_stw_actn_req, cruise_cancel, CANBUS.autopilot_chassis, counter))
# TODO: HUD control # TODO: HUD control

@ -16,6 +16,7 @@ class CarState(CarStateBase):
self.msg_stw_actn_req = None self.msg_stw_actn_req = None
self.hands_on_level = 0 self.hands_on_level = 0
self.steer_warning = None self.steer_warning = None
self.acc_state = 0
def update(self, cp, cp_cam): def update(self, cp, cp_cam):
ret = car.CarState.new_message() ret = car.CarState.new_message()
@ -57,7 +58,7 @@ class CarState(CarStateBase):
elif speed_units == "MPH": elif speed_units == "MPH":
ret.cruiseState.speed = cp.vl["DI_state"]["DI_digitalSpeed"] * CV.MPH_TO_MS 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.available = ((cruise_state == "STANDBY") or ret.cruiseState.enabled)
ret.cruiseState.standstill = (cruise_state == "STANDSTILL") ret.cruiseState.standstill = False # This needs to be false, since we can resume from stop without sending anything special
# Gear # Gear
ret.gearShifter = GEAR_MAP[self.can_define.dv["DI_torque2"]["DI_gear"].get(int(cp.vl["DI_torque2"]["DI_gear"]), "DI_GEAR_INVALID")] ret.gearShifter = GEAR_MAP[self.can_define.dv["DI_torque2"]["DI_gear"].get(int(cp.vl["DI_torque2"]["DI_gear"]), "DI_GEAR_INVALID")]
@ -88,6 +89,7 @@ class CarState(CarStateBase):
# Messages needed by carcontroller # Messages needed by carcontroller
self.msg_stw_actn_req = copy.copy(cp.vl["STW_ACTN_RQ"]) self.msg_stw_actn_req = copy.copy(cp.vl["STW_ACTN_RQ"])
self.acc_state = cp_cam.vl["DAS_control"]["DAS_accState"]
return ret return ret
@ -174,8 +176,10 @@ class CarState(CarStateBase):
def get_cam_can_parser(CP): def get_cam_can_parser(CP):
signals = [ signals = [
# sig_name, sig_address, default # sig_name, sig_address, default
("DAS_accState", "DAS_control", 0),
] ]
checks = [ checks = [
# sig_address, frequency # sig_address, frequency
("DAS_control", 40),
] ]
return CANParser(DBC[CP.carFingerprint]['chassis'], signals, checks, CANBUS.autopilot) return CANParser(DBC[CP.carFingerprint]['chassis'], signals, checks, CANBUS.autopilot_chassis)

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal import car
from selfdrive.car.tesla.values import CAR from panda import Panda
from selfdrive.car.tesla.values import CANBUS, CAR
from selfdrive.car import STD_CARGO_KG, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, get_safety_config from selfdrive.car import STD_CARGO_KG, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
@ -10,7 +11,6 @@ class CarInterface(CarInterfaceBase):
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None): def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint) ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "tesla" ret.carName = "tesla"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.tesla)]
# There is no safe way to do steer blending with user torque, # There is no safe way to do steer blending with user torque,
# so the steering behaves like autopilot. This is not # so the steering behaves like autopilot. This is not
@ -18,7 +18,28 @@ class CarInterface(CarInterfaceBase):
ret.dashcamOnly = True ret.dashcamOnly = True
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = car.CarParams.SteerControlType.angle
ret.openpilotLongitudinalControl = False
# Set kP and kI to 0 over the whole speed range to have the planner accel as actuator command
ret.longitudinalTuning.kpBP = [0]
ret.longitudinalTuning.kpV = [0]
ret.longitudinalTuning.kiBP = [0]
ret.longitudinalTuning.kiV = [0]
ret.stopAccel = 0.0
ret.startAccel = 0.0
ret.longitudinalActuatorDelayUpperBound = 0.5 # s
ret.radarTimeStep = (1.0 / 8) # 8Hz
# Check if we have messages on an auxiliary panda, and that 0x2bf (DAS_control) is present on the AP powertrain bus
# If so, we assume that it is connected to the longitudinal harness.
if (CANBUS.autopilot_powertrain in fingerprint.keys()) and (0x2bf in fingerprint[CANBUS.autopilot_powertrain].keys()):
ret.openpilotLongitudinalControl = True
ret.safetyConfigs = [
get_safety_config(car.CarParams.SafetyModel.tesla, Panda.FLAG_TESLA_LONG_CONTROL),
get_safety_config(car.CarParams.SafetyModel.tesla, Panda.FLAG_TESLA_LONG_CONTROL | Panda.FLAG_TESLA_POWERTRAIN),
]
else:
ret.openpilotLongitudinalControl = False
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.tesla, 0)]
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.steerRateCost = 0.5 ret.steerRateCost = 0.5

@ -1,13 +1,13 @@
import copy import copy
import crcmod import crcmod
from opendbc.can.can_define import CANDefine from selfdrive.config import Conversions as CV
from selfdrive.car.tesla.values import CANBUS from selfdrive.car.tesla.values import CANBUS, CarControllerParams
class TeslaCAN: class TeslaCAN:
def __init__(self, dbc_name, packer): def __init__(self, packer, pt_packer):
self.can_define = CANDefine(dbc_name)
self.packer = packer self.packer = packer
self.pt_packer = pt_packer
self.crc = crcmod.mkCrcFun(0x11d, initCrc=0x00, rev=False, xorOut=0xff) self.crc = crcmod.mkCrcFun(0x11d, initCrc=0x00, rev=False, xorOut=0xff)
@staticmethod @staticmethod
@ -39,3 +39,23 @@ class TeslaCAN:
data = self.packer.make_can_msg("STW_ACTN_RQ", bus, values)[2] data = self.packer.make_can_msg("STW_ACTN_RQ", bus, values)[2]
values["CRC_STW_ACTN_RQ"] = self.crc(data[:7]) values["CRC_STW_ACTN_RQ"] = self.crc(data[:7])
return self.packer.make_can_msg("STW_ACTN_RQ", bus, values) return self.packer.make_can_msg("STW_ACTN_RQ", bus, values)
def create_longitudinal_commands(self, acc_state, speed, min_accel, max_accel, cnt):
messages = []
values = {
"DAS_setSpeed": speed * CV.MS_TO_KPH,
"DAS_accState": acc_state,
"DAS_aebEvent": 0,
"DAS_jerkMin": CarControllerParams.JERK_LIMIT_MIN,
"DAS_jerkMax": CarControllerParams.JERK_LIMIT_MAX,
"DAS_accelMin": min_accel,
"DAS_accelMax": max_accel,
"DAS_controlCounter": (cnt % 8),
"DAS_controlChecksum": 0,
}
for packer, bus in [(self.packer, CANBUS.chassis), (self.pt_packer, CANBUS.powertrain)]:
data = packer.make_can_msg("DAS_control", bus, values)[2]
values["DAS_controlChecksum"] = self.checksum(0x2b9, data[:7])
messages.append(packer.make_can_msg("DAS_control", bus, values))
return messages

@ -25,14 +25,20 @@ FINGERPRINTS = {
} }
DBC = { DBC = {
CAR.AP2_MODELS: dbc_dict(None, 'tesla_radar', chassis_dbc='tesla_can'), CAR.AP2_MODELS: dbc_dict('tesla_powertrain', 'tesla_radar', chassis_dbc='tesla_can'),
CAR.AP1_MODELS: dbc_dict(None, 'tesla_radar', chassis_dbc='tesla_can'), CAR.AP1_MODELS: dbc_dict('tesla_powertrain', 'tesla_radar', chassis_dbc='tesla_can'),
} }
class CANBUS: class CANBUS:
# Lateral harness
chassis = 0 chassis = 0
autopilot = 2
radar = 1 radar = 1
autopilot_chassis = 2
# Longitudinal harness
powertrain = 4
private = 5
autopilot_powertrain = 6
GEAR_MAP = { GEAR_MAP = {
"DI_GEAR_INVALID": car.CarState.GearShifter.unknown, "DI_GEAR_INVALID": car.CarState.GearShifter.unknown,
@ -58,4 +64,6 @@ BUTTONS = [
class CarControllerParams: class CarControllerParams:
RATE_LIMIT_UP = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., .8, .15]) 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]) RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4])
JERK_LIMIT_MAX = 8
JERK_LIMIT_MIN = -8
ACCEL_TO_SPEED_MULTIPLIER = 3

@ -125,6 +125,10 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 3.00 # SWB, LWB is 3.40, TBD how to detect difference ret.wheelbase = 3.00 # SWB, LWB is 3.40, TBD how to detect difference
ret.minSteerSpeed = 14.0 ret.minSteerSpeed = 14.0
elif candidate == CAR.TROC_MK1:
ret.mass = 1413 + STD_CARGO_KG
ret.wheelbase = 2.63
elif candidate == CAR.AUDI_A3_MK3: elif candidate == CAR.AUDI_A3_MK3:
ret.mass = 1335 + STD_CARGO_KG ret.mass = 1335 + STD_CARGO_KG
ret.wheelbase = 2.61 ret.wheelbase = 2.61

@ -81,6 +81,7 @@ class CAR:
TIGUAN_MK2 = "VOLKSWAGEN TIGUAN 2ND GEN" # Chassis AD/BW, Mk2 VW Tiguan and variants TIGUAN_MK2 = "VOLKSWAGEN TIGUAN 2ND GEN" # Chassis AD/BW, Mk2 VW Tiguan and variants
TOURAN_MK2 = "VOLKSWAGEN TOURAN 2ND GEN" # Chassis 1T, Mk2 VW Touran and variants TOURAN_MK2 = "VOLKSWAGEN TOURAN 2ND GEN" # Chassis 1T, Mk2 VW Touran and variants
TRANSPORTER_T61 = "VOLKSWAGEN TRANSPORTER T6.1" # Chassis 7H/7L, T6-facelift Transporter/Multivan/Caravelle/California TRANSPORTER_T61 = "VOLKSWAGEN TRANSPORTER T6.1" # Chassis 7H/7L, T6-facelift Transporter/Multivan/Caravelle/California
TROC_MK1 = "VOLKSWAGEN T-ROC 1ST GEN" # Chassis A1, Mk1 VW VW T-Roc and variants
AUDI_A3_MK3 = "AUDI A3 3RD GEN" # Chassis 8V/FF, Mk3 Audi A3 and variants AUDI_A3_MK3 = "AUDI A3 3RD GEN" # Chassis 8V/FF, Mk3 Audi A3 and variants
AUDI_Q2_MK1 = "AUDI Q2 1ST GEN" # Chassis GA, Mk1 Audi Q2 (RoW) and Q2L (China only) AUDI_Q2_MK1 = "AUDI Q2 1ST GEN" # Chassis GA, Mk1 Audi Q2 (RoW) and Q2L (China only)
SEAT_ATECA_MK1 = "SEAT ATECA 1ST GEN" # Chassis 5F, Mk1 SEAT Ateca and CUPRA Ateca SEAT_ATECA_MK1 = "SEAT ATECA 1ST GEN" # Chassis 5F, Mk1 SEAT Ateca and CUPRA Ateca
@ -472,6 +473,23 @@ FW_VERSIONS = {
b'\xf1\x872Q0907572R \xf1\x890372', b'\xf1\x872Q0907572R \xf1\x890372',
], ],
}, },
CAR.TROC_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8705E906018AT\xf1\x899640',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x870CW300051M \xf1\x891925',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x875Q0959655CG\xf1\x890421\xf1\x82\x13111100123333003142404M1152119333613100',
],
(Ecu.eps, 0x712, None): [
b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\x0521060405A1',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907572T \xf1\x890383',
],
},
CAR.AUDI_A3_MK3: { CAR.AUDI_A3_MK3: {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906023AN\xf1\x893695', b'\xf1\x8704E906023AN\xf1\x893695',

@ -18,16 +18,22 @@ from selfdrive.hardware import TICI
class KalmanParams(): class KalmanParams():
def __init__(self, dt): def __init__(self, dt):
# Lead Kalman Filter params, calculating K from A, C, Q, R requires the control library. # Lead Kalman Filter params, calculating K from A, C, Q, R requires the control library.
# hardcoding a lookup table to compute K for values of radar_ts between 0.1s and 1.0s # hardcoding a lookup table to compute K for values of radar_ts between 0.01s and 0.2s
assert dt > .01 and dt < .1, "Radar time step must be between .01s and 0.1s" assert dt > .01 and dt < .2, "Radar time step must be between .01s and 0.2s"
self.A = [[1.0, dt], [0.0, 1.0]] self.A = [[1.0, dt], [0.0, 1.0]]
self.C = [1.0, 0.0] self.C = [1.0, 0.0]
#Q = np.matrix([[10., 0.0], [0.0, 100.]]) #Q = np.matrix([[10., 0.0], [0.0, 100.]])
#R = 1e3 #R = 1e3
#K = np.matrix([[ 0.05705578], [ 0.03073241]]) #K = np.matrix([[ 0.05705578], [ 0.03073241]])
dts = [dt * 0.01 for dt in range(1, 11)] dts = [dt * 0.01 for dt in range(1, 21)]
K0 = [0.12288, 0.14557, 0.16523, 0.18282, 0.19887, 0.21372, 0.22761, 0.24069, 0.2531, 0.26491] K0 = [0.12287673, 0.14556536, 0.16522756, 0.18281627, 0.1988689, 0.21372394,
K1 = [0.29666, 0.29331, 0.29043, 0.28787, 0.28555, 0.28342, 0.28144, 0.27958, 0.27783, 0.27617] 0.22761098, 0.24069424, 0.253096, 0.26491023, 0.27621103, 0.28705801,
0.29750003, 0.30757767, 0.31732515, 0.32677158, 0.33594201, 0.34485814,
0.35353899, 0.36200124]
K1 = [0.29666309, 0.29330885, 0.29042818, 0.28787125, 0.28555364, 0.28342219,
0.28144091, 0.27958406, 0.27783249, 0.27617149, 0.27458948, 0.27307714,
0.27162685, 0.27023228, 0.26888809, 0.26758976, 0.26633338, 0.26511557,
0.26393339, 0.26278425]
self.K = [[interp(dt, dts, K0)], [interp(dt, dts, K1)]] self.K = [[interp(dt, dts, K0)], [interp(dt, dts, K1)]]

@ -11,6 +11,7 @@ from common.xattr import getxattr
from selfdrive.loggerd.tests.loggerd_tests_common import UploaderTestCase from selfdrive.loggerd.tests.loggerd_tests_common import UploaderTestCase
class TestLogHandler(logging.Handler): class TestLogHandler(logging.Handler):
def __init__(self): def __init__(self):
logging.Handler.__init__(self) logging.Handler.__init__(self)
@ -22,7 +23,7 @@ class TestLogHandler(logging.Handler):
def emit(self, record): def emit(self, record):
try: try:
j = json.loads(record.message) j = json.loads(record.getMessage())
if j["event"] == "upload_success": if j["event"] == "upload_success":
self.upload_order.append(j["key"]) self.upload_order.append(j["key"])
if j["event"] == "upload_ignored": if j["event"] == "upload_ignored":
@ -33,6 +34,7 @@ class TestLogHandler(logging.Handler):
log_handler = TestLogHandler() log_handler = TestLogHandler()
cloudlog.addHandler(log_handler) cloudlog.addHandler(log_handler)
class TestUploader(UploaderTestCase): class TestUploader(UploaderTestCase):
def setUp(self): def setUp(self):
super(TestUploader, self).setUp() super(TestUploader, self).setUp()

@ -147,7 +147,7 @@ class Uploader():
def do_upload(self, key, fn): def do_upload(self, key, fn):
try: try:
url_resp = self.api.get("v1.3/"+self.dongle_id+"/upload_url/", timeout=10, path=key, access_token=self.api.get_token()) url_resp = self.api.get("v1.4/"+self.dongle_id+"/upload_url/", timeout=10, path=key, access_token=self.api.get_token())
if url_resp.status_code == 412: if url_resp.status_code == 412:
self.last_resp = url_resp self.last_resp = url_resp
return return
@ -155,7 +155,7 @@ class Uploader():
url_resp_json = json.loads(url_resp.text) url_resp_json = json.loads(url_resp.text)
url = url_resp_json['url'] url = url_resp_json['url']
headers = url_resp_json['headers'] headers = url_resp_json['headers']
cloudlog.debug("upload_url v1.3 %s %s", url, str(headers)) cloudlog.debug("upload_url v1.4 %s %s", url, str(headers))
if fake_upload: if fake_upload:
cloudlog.debug("*** WARNING, THIS IS A FAKE UPLOAD TO %s ***" % url) cloudlog.debug("*** WARNING, THIS IS A FAKE UPLOAD TO %s ***" % url)

@ -149,9 +149,10 @@ def manager_thread():
started_prev = started started_prev = started
running_list = ["%s%s\u001b[0m" % ("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name) running = ' '.join(["%s%s\u001b[0m" % ("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
for p in managed_processes.values() if p.proc] for p in managed_processes.values() if p.proc])
cloudlog.debug(' '.join(running_list)) print(running)
cloudlog.debug(running)
# send managerState # send managerState
msg = messaging.new_message('managerState') msg = messaging.new_message('managerState')

@ -106,7 +106,17 @@ def add_file_handler(log):
cloudlog = log = SwagLogger() cloudlog = log = SwagLogger()
log.setLevel(logging.DEBUG) log.setLevel(logging.DEBUG)
outhandler = logging.StreamHandler() outhandler = logging.StreamHandler()
print_level = os.environ.get('LOGPRINT', 'warning')
if print_level == 'debug':
outhandler.setLevel(logging.DEBUG)
elif print_level == 'info':
outhandler.setLevel(logging.INFO)
elif print_level == 'warning':
outhandler.setLevel(logging.WARNING)
log.addHandler(outhandler) log.addHandler(outhandler)
# logs are sent through IPC before writing to disk to prevent disk I/O blocking # logs are sent through IPC before writing to disk to prevent disk I/O blocking
log.addHandler(UnixDomainSocketHandler(SwagFormatter(log))) log.addHandler(UnixDomainSocketHandler(SwagFormatter(log)))

@ -37,7 +37,7 @@ ignore_carstate_check = [
CHRYSLER.PACIFICA_2017_HYBRID, CHRYSLER.PACIFICA_2017_HYBRID,
] ]
@parameterized_class(('car_model'), [(car,) for i, car in enumerate(all_known_cars()) if i % NUM_JOBS == JOB_ID]) @parameterized_class(('car_model'), [(car,) for i, car in enumerate(sorted(all_known_cars())) if i % NUM_JOBS == JOB_ID])
class TestCarModel(unittest.TestCase): class TestCarModel(unittest.TestCase):
@classmethod @classmethod

@ -150,6 +150,7 @@ routes = [
TestRoute("2cef8a0b898f331a|2021-03-25--20-13-57", VOLKSWAGEN.TIGUAN_MK2), TestRoute("2cef8a0b898f331a|2021-03-25--20-13-57", VOLKSWAGEN.TIGUAN_MK2),
TestRoute("a589dcc642fdb10a|2021-06-14--20-54-26", VOLKSWAGEN.TOURAN_MK2), TestRoute("a589dcc642fdb10a|2021-06-14--20-54-26", VOLKSWAGEN.TOURAN_MK2),
TestRoute("a459f4556782eba1|2021-09-19--09-48-00", VOLKSWAGEN.TRANSPORTER_T61), TestRoute("a459f4556782eba1|2021-09-19--09-48-00", VOLKSWAGEN.TRANSPORTER_T61),
TestRoute("0cd0b7f7e31a3853|2021-11-18--00-38-32", VOLKSWAGEN.TROC_MK1),
TestRoute("07667b885add75fd|2021-01-23--19-48-42", VOLKSWAGEN.AUDI_A3_MK3), TestRoute("07667b885add75fd|2021-01-23--19-48-42", VOLKSWAGEN.AUDI_A3_MK3),
TestRoute("6c6b466346192818|2021-06-06--14-17-47", VOLKSWAGEN.AUDI_Q2_MK1), TestRoute("6c6b466346192818|2021-06-06--14-17-47", VOLKSWAGEN.AUDI_Q2_MK1),
TestRoute("8f205bdd11bcbb65|2021-03-26--01-00-17", VOLKSWAGEN.SEAT_ATECA_MK1), TestRoute("8f205bdd11bcbb65|2021-03-26--01-00-17", VOLKSWAGEN.SEAT_ATECA_MK1),

@ -355,10 +355,10 @@ def thermald_thread():
set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False) set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)
set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", True) set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", True)
elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT: elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
remaining_time = str(max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 0)) remaining = max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 1)
set_offroad_alert_if_changed("Offroad_UpdateFailed", False) set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False) set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining_time} days.") set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining} day{'' if remaining == 1 else 's'}.")
else: else:
set_offroad_alert_if_changed("Offroad_UpdateFailed", False) set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False) set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)

@ -90,6 +90,7 @@ void HttpRequest::sendRequest(const QString &requestURL, const HttpRequest::Meth
QNetworkRequest request; QNetworkRequest request;
request.setUrl(QUrl(requestURL)); request.setUrl(QUrl(requestURL));
request.setRawHeader("User-Agent", getUserAgent().toUtf8());
if (!token.isEmpty()) { if (!token.isEmpty()) {
request.setRawHeader(QByteArray("Authorization"), ("JWT " + token).toUtf8()); request.setRawHeader(QByteArray("Authorization"), ("JWT " + token).toUtf8());

@ -4,6 +4,7 @@
#include <QDebug> #include <QDebug>
#include <QPainterPath> #include <QPainterPath>
#include <QFileInfo>
#include "selfdrive/common/swaglog.h" #include "selfdrive/common/swaglog.h"
#include "selfdrive/ui/ui.h" #include "selfdrive/ui/ui.h"
@ -21,6 +22,8 @@ const float MAX_PITCH = 50;
const float MIN_PITCH = 0; const float MIN_PITCH = 0;
const float MAP_SCALE = 2; const float MAP_SCALE = 2;
const QString ICON_SUFFIX = ".png";
MapWindow::MapWindow(const QMapboxGLSettings &settings) : MapWindow::MapWindow(const QMapboxGLSettings &settings) :
m_settings(settings), velocity_filter(0, 10, 0.05) { m_settings(settings), velocity_filter(0, 10, 0.05) {
sm = new SubMaster({"liveLocationKalman", "navInstruction", "navRoute"}); sm = new SubMaster({"liveLocationKalman", "navInstruction", "navRoute"});
@ -324,6 +327,7 @@ void MapWindow::offroadTransition(bool offroad) {
} }
MapInstructions::MapInstructions(QWidget * parent) : QWidget(parent) { MapInstructions::MapInstructions(QWidget * parent) : QWidget(parent) {
is_rhd = Params().getBool("IsRHD");
QHBoxLayout *main_layout = new QHBoxLayout(this); QHBoxLayout *main_layout = new QHBoxLayout(this);
main_layout->setContentsMargins(11, 50, 11, 11); main_layout->setContentsMargins(11, 50, 11, 11);
{ {
@ -442,10 +446,22 @@ void MapInstructions::updateInstructions(cereal::NavInstruction::Reader instruct
if (!modifier.isEmpty()) { if (!modifier.isEmpty()) {
fn += "_" + modifier; fn += "_" + modifier;
} }
fn += + ".png"; fn += ICON_SUFFIX;
fn = fn.replace(' ', '_'); fn = fn.replace(' ', '_');
// for rhd, reflect direction and then flip
if (is_rhd) {
if (fn.contains("left")) {
fn.replace(QString("left"), QString("right"));
} else if (fn.contains("right")) {
fn.replace(QString("right"), QString("left"));
}
}
QPixmap pix(fn); QPixmap pix(fn);
if (is_rhd) {
pix = pix.transformed(QTransform().scale(-1, 1));
}
icon_01->setPixmap(pix.scaledToWidth(200, Qt::SmoothTransformation)); icon_01->setPixmap(pix.scaledToWidth(200, Qt::SmoothTransformation));
icon_01->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); icon_01->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
icon_01->setVisible(true); icon_01->setVisible(true);
@ -476,7 +492,7 @@ void MapInstructions::updateInstructions(cereal::NavInstruction::Reader instruct
fn += "turn_straight"; fn += "turn_straight";
} }
QPixmap pix(fn + ".png"); QPixmap pix(fn + ICON_SUFFIX);
auto icon = new QLabel; auto icon = new QLabel;
int wh = active ? 125 : 75; int wh = active ? 125 : 75;
icon->setPixmap(pix.scaled(wh, wh, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); icon->setPixmap(pix.scaled(wh, wh, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));

@ -36,6 +36,7 @@ private:
QWidget *lane_widget; QWidget *lane_widget;
QHBoxLayout *lane_layout; QHBoxLayout *lane_layout;
bool error = false; bool error = false;
bool is_rhd = false;
public: public:
MapInstructions(QWidget * parent=nullptr); MapInstructions(QWidget * parent=nullptr);

@ -8,12 +8,21 @@
#include "selfdrive/common/swaglog.h" #include "selfdrive/common/swaglog.h"
#include "selfdrive/hardware/hw.h" #include "selfdrive/hardware/hw.h"
QString getVersion() {
static QString version = QString::fromStdString(Params().get("Version"));
return version;
}
QString getBrand() { QString getBrand() {
return Params().getBool("Passive") ? "dashcam" : "openpilot"; return Params().getBool("Passive") ? "dashcam" : "openpilot";
} }
QString getBrandVersion() { QString getBrandVersion() {
return getBrand() + " v" + QString::fromStdString(Params().get("Version")).left(14).trimmed(); return getBrand() + " v" + getVersion().left(14).trimmed();
}
QString getUserAgent() {
return "openpilot-" + getVersion();
} }
std::optional<QString> getDongleId() { std::optional<QString> getDongleId() {

@ -9,8 +9,10 @@
#include <QSurfaceFormat> #include <QSurfaceFormat>
#include <QWidget> #include <QWidget>
QString getVersion();
QString getBrand(); QString getBrand();
QString getBrandVersion(); QString getBrandVersion();
QString getUserAgent();
std::optional<QString> getDongleId(); std::optional<QString> getDongleId();
void configFont(QPainter &p, const QString &family, int size, const QString &style); void configFont(QPainter &p, const QString &family, int size, const QString &style);
void clearLayout(QLayout* layout); void clearLayout(QLayout* layout);

@ -116,4 +116,4 @@ if __name__ == "__main__":
if args.stream: if args.stream:
start_juggler(layout=args.layout) start_juggler(layout=args.layout)
else: else:
juggle_route(args.route_name, args.segment_number, args.segment_count, args.qlog, args.can, args.layout) juggle_route(args.route_name.strip(), args.segment_number, args.segment_count, args.qlog, args.can, args.layout)

Loading…
Cancel
Save