From bfa0891c889f16cf3a87f44752474cb729c2f8a3 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Tue, 29 Jul 2025 01:22:48 -0400 Subject: [PATCH] VW: External Panda support (#2582) * VW: External Panda support * no init no work * tidy up extended CAN pointer * woops * consistency * reassert my right to verbosity * consistency * whitespace styling --- opendbc/car/volkswagen/carcontroller.py | 16 +++++------ opendbc/car/volkswagen/carstate.py | 10 +++---- opendbc/car/volkswagen/interface.py | 13 ++++++--- opendbc/car/volkswagen/values.py | 37 +++++++++++++++++++++---- 4 files changed, 53 insertions(+), 23 deletions(-) diff --git a/opendbc/car/volkswagen/carcontroller.py b/opendbc/car/volkswagen/carcontroller.py index f0465fad..a79526df 100644 --- a/opendbc/car/volkswagen/carcontroller.py +++ b/opendbc/car/volkswagen/carcontroller.py @@ -4,7 +4,7 @@ from opendbc.car import Bus, DT_CTRL, apply_driver_steer_torque_limits, structs from opendbc.car.common.conversions import Conversions as CV from opendbc.car.interfaces import CarControllerBase from opendbc.car.volkswagen import mqbcan, pqcan -from opendbc.car.volkswagen.values import CANBUS, CarControllerParams, VolkswagenFlags +from opendbc.car.volkswagen.values import CanBus, CarControllerParams, VolkswagenFlags VisualAlert = structs.CarControl.HUDControl.VisualAlert LongCtrlState = structs.CarControl.Actuators.LongControlState @@ -14,9 +14,9 @@ class CarController(CarControllerBase): def __init__(self, dbc_names, CP): super().__init__(dbc_names, CP) self.CCP = CarControllerParams(CP) + self.CAN = CanBus(CP) self.CCS = pqcan if CP.flags & VolkswagenFlags.PQ else mqbcan self.packer_pt = CANPacker(dbc_names[Bus.pt]) - self.ext_bus = CANBUS.pt if CP.networkLocation == structs.CarParams.NetworkLocation.fwdCamera else CANBUS.cam self.aeb_available = not CP.flags & VolkswagenFlags.PQ self.apply_torque_last = 0 @@ -63,7 +63,7 @@ class CarController(CarControllerBase): self.eps_timer_soft_disable_alert = self.hca_frame_timer_running > self.CCP.STEER_TIME_ALERT / DT_CTRL self.apply_torque_last = apply_torque - can_sends.append(self.CCS.create_steering_control(self.packer_pt, CANBUS.pt, apply_torque, hca_enabled)) + can_sends.append(self.CCS.create_steering_control(self.packer_pt, self.CAN.pt, apply_torque, hca_enabled)) if self.CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT: # Pacify VW Emergency Assist driver inactivity detection by changing its view of driver steering input torque @@ -72,7 +72,7 @@ class CarController(CarControllerBase): ea_simulated_torque = float(np.clip(apply_torque * 2, -self.CCP.STEER_MAX, self.CCP.STEER_MAX)) if abs(CS.out.steeringTorque) > abs(ea_simulated_torque): ea_simulated_torque = CS.out.steeringTorque - can_sends.append(self.CCS.create_eps_update(self.packer_pt, CANBUS.cam, CS.eps_stock_values, ea_simulated_torque)) + can_sends.append(self.CCS.create_eps_update(self.packer_pt, self.CAN.cam, CS.eps_stock_values, ea_simulated_torque)) # **** Acceleration Controls ******************************************** # @@ -82,7 +82,7 @@ class CarController(CarControllerBase): accel = float(np.clip(actuators.accel, self.CCP.ACCEL_MIN, self.CCP.ACCEL_MAX) if CC.longActive else 0) stopping = actuators.longControlState == LongCtrlState.stopping starting = actuators.longControlState == LongCtrlState.pid and (CS.esp_hold_confirmation or CS.out.vEgo < self.CP.vEgoStopping) - can_sends.extend(self.CCS.create_acc_accel_control(self.packer_pt, CANBUS.pt, CS.acc_type, CC.longActive, accel, + can_sends.extend(self.CCS.create_acc_accel_control(self.packer_pt, self.CAN.pt, CS.acc_type, CC.longActive, accel, acc_control, stopping, starting, CS.esp_hold_confirmation)) #if self.aeb_available: @@ -97,7 +97,7 @@ class CarController(CarControllerBase): hud_alert = 0 if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw): hud_alert = self.CCP.LDW_MESSAGES["laneAssistTakeOver"] - can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, CANBUS.pt, CS.ldw_stock_values, CC.latActive, + can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, self.CAN.pt, CS.ldw_stock_values, CC.latActive, CS.out.steeringPressed, hud_alert, hud_control)) if self.frame % self.CCP.ACC_HUD_STEP == 0 and self.CP.openpilotLongitudinalControl: @@ -108,14 +108,14 @@ class CarController(CarControllerBase): # FIXME: PQ may need to use the on-the-wire mph/kmh toggle to fix rounding errors # FIXME: Detect clusters with vEgoCluster offsets and apply an identical vCruiseCluster offset set_speed = hud_control.setSpeed * CV.MS_TO_KPH - can_sends.append(self.CCS.create_acc_hud_control(self.packer_pt, CANBUS.pt, acc_hud_status, set_speed, + can_sends.append(self.CCS.create_acc_hud_control(self.packer_pt, self.CAN.pt, acc_hud_status, set_speed, lead_distance, hud_control.leadDistanceBars)) # **** Stock ACC Button Controls **************************************** # gra_send_ready = self.CP.pcmCruise and CS.gra_stock_values["COUNTER"] != self.gra_acc_counter_last if gra_send_ready and (CC.cruiseControl.cancel or CC.cruiseControl.resume): - can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values, + can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.CAN.ext, CS.gra_stock_values, cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume)) new_actuators = actuators.as_builder() diff --git a/opendbc/car/volkswagen/carstate.py b/opendbc/car/volkswagen/carstate.py index e7972961..86ac2bda 100644 --- a/opendbc/car/volkswagen/carstate.py +++ b/opendbc/car/volkswagen/carstate.py @@ -2,7 +2,7 @@ from opendbc.can import CANParser from opendbc.car import Bus, structs from opendbc.car.interfaces import CarStateBase from opendbc.car.common.conversions import Conversions as CV -from opendbc.car.volkswagen.values import DBC, CANBUS, NetworkLocation, TransmissionType, GearShifter, \ +from opendbc.car.volkswagen.values import DBC, CanBus, NetworkLocation, TransmissionType, GearShifter, \ CarControllerParams, VolkswagenFlags ButtonType = structs.CarState.ButtonEvent.Type @@ -265,14 +265,14 @@ class CarState(CarStateBase): Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [ # the 50->1Hz is currently too much for the CANParser to figure out ("Blinkmodi_02", 1), # From J519 BCM (sent at 1Hz when no lights active, 50Hz when active) - ], CANBUS.pt), - Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_messages, CANBUS.cam), + ], CanBus(CP).pt), + Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_messages, CanBus(CP).cam), } @staticmethod def get_can_parsers_pq(CP): return { - Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CANBUS.pt), - Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CANBUS.cam), + Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).pt), + Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).cam), } diff --git a/opendbc/car/volkswagen/interface.py b/opendbc/car/volkswagen/interface.py index bd848017..4c40f874 100644 --- a/opendbc/car/volkswagen/interface.py +++ b/opendbc/car/volkswagen/interface.py @@ -2,7 +2,7 @@ from opendbc.car import get_safety_config, structs from opendbc.car.interfaces import CarInterfaceBase from opendbc.car.volkswagen.carcontroller import CarController from opendbc.car.volkswagen.carstate import CarState -from opendbc.car.volkswagen.values import CAR, NetworkLocation, TransmissionType, VolkswagenFlags, VolkswagenSafetyFlags +from opendbc.car.volkswagen.values import CanBus, CAR, NetworkLocation, TransmissionType, VolkswagenFlags, VolkswagenSafetyFlags class CarInterface(CarInterfaceBase): @@ -16,7 +16,7 @@ class CarInterface(CarInterfaceBase): if ret.flags & VolkswagenFlags.PQ: # Set global PQ35/PQ46/NMS parameters - ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.volkswagenPq)] + safety_configs = [get_safety_config(structs.CarParams.SafetyModel.volkswagenPq)] ret.enableBsm = 0x3BA in fingerprint[0] # SWA_1 if 0x440 in fingerprint[0] or docs: # Getriebe_1 @@ -39,7 +39,7 @@ class CarInterface(CarInterfaceBase): else: # Set global MQB parameters - ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.volkswagen)] + safety_configs = [get_safety_config(structs.CarParams.SafetyModel.volkswagen)] ret.enableBsm = 0x30F in fingerprint[0] # SWA_01 if 0xAD in fingerprint[0] or docs: # Getriebe_11 @@ -79,7 +79,7 @@ class CarInterface(CarInterfaceBase): if alpha_long: # Proof-of-concept, prep for E2E only. No radar points available. Panda ALLOW_DEBUG firmware required. ret.openpilotLongitudinalControl = True - ret.safetyConfigs[0].safetyParam |= VolkswagenSafetyFlags.LONG_CONTROL.value + safety_configs[0].safetyParam |= VolkswagenSafetyFlags.LONG_CONTROL.value if ret.transmissionType == TransmissionType.manual: ret.minEnableSpeed = 4.5 @@ -89,4 +89,9 @@ class CarInterface(CarInterfaceBase): ret.vEgoStopping = 0.5 ret.autoResumeSng = ret.minEnableSpeed == -1 + CAN = CanBus(fingerprint=fingerprint) + if CAN.pt >= 4: + safety_configs.insert(0, get_safety_config(structs.CarParams.SafetyModel.noOutput)) + ret.safetyConfigs = safety_configs + return ret diff --git a/opendbc/car/volkswagen/values.py b/opendbc/car/volkswagen/values.py index bc283d66..573596fb 100644 --- a/opendbc/car/volkswagen/values.py +++ b/opendbc/car/volkswagen/values.py @@ -2,7 +2,7 @@ from collections import defaultdict, namedtuple from dataclasses import dataclass, field from enum import Enum, IntFlag, StrEnum -from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, structs, uds +from opendbc.car import Bus, CanBusBase, CarSpecs, DbcDict, PlatformConfig, Platforms, structs, uds from opendbc.can import CANDefine from opendbc.car.common.conversions import Conversions as CV from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \ @@ -17,6 +17,36 @@ GearShifter = structs.CarState.GearShifter Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values']) +class CanBus(CanBusBase): + def __init__(self, CP=None, fingerprint=None) -> None: + super().__init__(CP, fingerprint) + + self._ext = self.offset + if CP is not None: + self._ext = self.offset + 2 if CP.networkLocation == NetworkLocation.gateway else self.offset + + @property + def pt(self) -> int: + # ADAS / Extended CAN, gateway side of the relay + return self.offset + + @property + def aux(self) -> int: + # NetworkLocation.fwdCamera: radar-camera object fusion CAN + # NetworkLocation.gateway: powertrain CAN + return self.offset + 1 + + @property + def cam(self) -> int: + # ADAS / Extended CAN, camera side of the relay + return self.offset + 2 + + @property + def ext(self) -> int: + # ADAS / Extended CAN, side of the relay with the ACC radar + return self._ext + + class CarControllerParams: STEER_STEP = 2 # HCA_01/HCA_1 message frequency 50Hz ACC_CONTROL_STEP = 2 # ACC_06/ACC_07/ACC_System frequency 50Hz @@ -107,11 +137,6 @@ class CarControllerParams: } -class CANBUS: - pt = 0 - cam = 2 - - class WMI(StrEnum): VOLKSWAGEN_USA_SUV = "1V2" VOLKSWAGEN_USA_CAR = "1VW"