From d8a9651f96af53d8ab6014191ad05400de43399c Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Tue, 30 Sep 2025 21:06:07 -0500 Subject: [PATCH] Torque panda --- panda/board/safety/safety_gm.h | 23 +++++----- selfdrive/car/gm/carcontroller.py | 72 +++++++++++++++++++++++++------ selfdrive/car/gm/carstate.py | 23 ++++++---- selfdrive/car/gm/gmcan.py | 31 +++++++------ selfdrive/car/gm/interface.py | 21 +++++---- selfdrive/car/gm/values.py | 43 ++++++++++++------ 6 files changed, 143 insertions(+), 70 deletions(-) diff --git a/panda/board/safety/safety_gm.h b/panda/board/safety/safety_gm.h index dfa936e69..8f53be2ca 100644 --- a/panda/board/safety/safety_gm.h +++ b/panda/board/safety/safety_gm.h @@ -10,17 +10,17 @@ const SteeringLimits GM_STEERING_LIMITS = { }; const LongitudinalLimits GM_ASCM_LONG_LIMITS = { - .max_gas = 3072, - .min_gas = 1404, - .inactive_gas = 1404, - .max_brake = 400, + .max_gas = 8191, + .min_gas = 5500, + .inactive_gas = 5500, + .max_brake = 400, }; const LongitudinalLimits GM_CAM_LONG_LIMITS = { - .max_gas = 3400, - .min_gas = 1514, - .inactive_gas = 1554, - .max_brake = 400, + .max_gas = 8848, + .min_gas = 5610, + .inactive_gas = 5650, + .max_brake = 400, }; const LongitudinalLimits *gm_long_limits; @@ -143,7 +143,7 @@ static void gm_rx_hook(const CANPacket_t *to_push) { } if ((addr == 0xC9) && ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM))) { - brake_pressed = GET_BIT(to_push, 40U); + brake_pressed = GET_BIT(to_push, 40U) != 0U; } if (addr == 0xC9) { @@ -242,10 +242,10 @@ static bool gm_tx_hook(const CANPacket_t *to_send) { // GAS/REGEN: safety check if (addr == 0x2CB) { bool apply = GET_BIT(to_send, 0U); - int gas_regen = ((GET_BYTE(to_send, 2) & 0x7FU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3); + int gas_regen = ((GET_BYTE(to_send, 1) & 0x1U) << 13) + ((GET_BYTE(to_send, 2) & 0xFFU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3); bool violation = false; - // Allow apply bit in pre-enabled and overriding states, except for inactive gas // Allow apply bit in pre-enabled and overriding states + // Allow apply bit in pre-enabled and overriding states violation |= !controls_allowed && apply; violation |= longitudinal_gas_checks(gas_regen, *gm_long_limits); @@ -324,6 +324,7 @@ static int gm_fwd_hook(int bus_num, int addr) { } static safety_config gm_init(uint16_t param) { + if GET_FLAG(param, GM_PARAM_HW_CAM) { gm_hw = GM_CAM; } else if GET_FLAG(param, GM_PARAM_HW_SDGM) { diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index ef9a32789..8e91a7d1e 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -1,5 +1,7 @@ from typing import Tuple import time +import math +from openpilot.common.swaglog import cloudlog from cereal import car from openpilot.common.conversions import Conversions as CV from openpilot.common.filter_simple import FirstOrderFilter @@ -64,13 +66,20 @@ class CarController(CarControllerBase): self.params = CarControllerParams(self.CP) self.params_ = Params() + self.mass = CP.mass + self.tireRadius = 0.075 * CP.wheelbase + 0.1453 + self.frontalArea = 1.05 * CP.wheelbase + 0.0679 + self.coeffDrag = 0.30 + self.airDensity = 1.225 + + + self.packer_pt = CANPacker(DBC[self.CP.carFingerprint]['pt']) self.packer_obj = CANPacker(DBC[self.CP.carFingerprint]['radar']) self.packer_ch = CANPacker(DBC[self.CP.carFingerprint]['chassis']) # FrogPilot variables self.accel_g = 0.0 - self.pitch = FirstOrderFilter(0., 0.09 * 4, DT_CTRL * 4) # runs at 25 Hz self.accel_g = 0.0 self.regen_paddle_pressed = False @@ -330,17 +339,19 @@ class CarController(CarControllerBase): if self.frame % 4 == 0: stopping = actuators.longControlState == LongCtrlState.stopping - # Pitch compensated acceleration; - # TODO: include future pitch (sm['modelDataV2'].orientation.y) to account for long actuator delay - if frogpilot_toggles.long_pitch and len(CC.orientationNED) > 1: - self.pitch.update(CC.orientationNED[1]) - self.accel_g = ACCELERATION_DUE_TO_GRAVITY * apply_deadzone(self.pitch.x, PITCH_DEADZONE) # driving uphill is positive pitch - accel += self.accel_g - brake_accel = actuators.accel + self.accel_g * interp(CS.out.vEgo, BRAKE_PITCH_FACTOR_BP, BRAKE_PITCH_FACTOR_V) - at_full_stop = CC.longActive and CS.out.standstill near_stop = CC.longActive and (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE) interceptor_gas_cmd = 0 + # --- Regen scaling for ACC-only cars --- + # For ACC-only cars, simulate maximum paddle hold regen + if not self.CP.enableGasInterceptor and self.CP.carFingerprint in CC_REGEN_PADDLE_CAR: + # use regen table gain to compute effective accel + _, press_regen_paddle = self.calc_pedal_command(actuators.accel, True, CS.out.vEgo) + # if simulated paddle pressed, treat as stronger regen + use_regen = press_regen_paddle + else: + use_regen = False + # --- End regen scaling insert --- if not CC.longActive: # ASCM sends max regen when not enabled self.apply_gas = self.params.INACTIVE_REGEN @@ -349,9 +360,38 @@ class CarController(CarControllerBase): self.apply_gas = self.params.INACTIVE_REGEN self.apply_brake = int(min(-100 * frogpilot_toggles.stopAccel, self.params.MAX_BRAKE)) else: - # Normal operation - self.apply_gas = int(round(interp(accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V))) + if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping: + accel_due_to_pitch = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY + else: + accel_due_to_pitch = 0.0 + + if frogpilot_toggles.sport_plus: + gas_max = self.params.MAX_GAS_PLUS + accel_max = self.params.ACCEL_MAX_PLUS + else: + gas_max = self.params.MAX_GAS + accel_max = self.params.ACCEL_MAX + + accel = clip(actuators.accel + accel_due_to_pitch, self.params.ACCEL_MIN, accel_max) + torque = self.tireRadius * ((self.mass*accel) + (0.5*self.coeffDrag*self.frontalArea*self.airDensity*CS.out.vEgo**2)) + + scaled_torque = torque + self.params.ZERO_GAS + # --- Regen torque scaling for ACC-only cars --- + # apply full paddle regen curve when simulated regen is active + if use_regen: + min_regen = self.params.GAS_LOOKUP_V[0] # max regen from lookup + else: + min_regen = self.params.MAX_ACC_REGEN + apply_gas_torque = clip(scaled_torque, min_regen, gas_max) + # --- End regen torque scaling --- + apply_gas_torque = clip(scaled_torque, self.params.MAX_ACC_REGEN, gas_max) + BRAKE_SWITCH = int(round(interp(CS.out.vEgo, self.params.BRAKE_SWITCH_LOOKUP_BP, self.params.BRAKE_SWITCH_LOOKUP_V))) + brake_accel = min((scaled_torque - BRAKE_SWITCH)/(self.tireRadius*self.mass), 0) + self.apply_gas = int(round(apply_gas_torque)) self.apply_brake = int(round(interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V))) + if self.apply_brake > 0: + self.apply_gas = self.params.INACTIVE_REGEN + # Don't allow any gas above inactive regen while stopping # FIXME: brakes aren't applied immediately when enabling at a stop if stopping: @@ -389,7 +429,7 @@ class CarController(CarControllerBase): if CC.cruiseControl.resume and CS.pcm_acc_status == AccState.STANDSTILL and frogpilot_toggles.volt_sng: acc_engaged = False else: - acc_engaged = CC.enabled + acc_engaged = CC.enabled and not (self.CP.carFingerprint == CAR.CHEVROLET_BOLT_EUV and self.CP.enableGasInterceptor) # GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop)) @@ -422,11 +462,15 @@ class CarController(CarControllerBase): # TODO: integrate this with the code block below? if ( (self.CP.flags & GMFlags.PEDAL_LONG.value) # Always cancel stock CC when using pedal interceptor - or (self.CP.flags & GMFlags.CC_LONG.value and not CC.enabled) # Cancel stock CC if OP is not active + or (self.CP.flags & GMFlags.CC_LONG.value) # Match ACC behavior for non-ACC cars ) and CS.out.cruiseState.enabled: if (self.frame - self.last_button_frame) * DT_CTRL > 0.04: self.last_button_frame = self.frame - can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL)) + # Send cancel to appropriate bus based on car type (match stock longitudinal logic) + if self.CP.carFingerprint in SDGM_CAR: + can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL)) + else: + can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL)) else: # While car is braking, cancel button causes ECM to enter a soft disable state with a fault status. diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index 68015ac06..79d86ac81 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -5,7 +5,7 @@ from openpilot.common.numpy_fast import mean from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser from openpilot.selfdrive.car.interfaces import CarStateBase -from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD, GMFlags, CC_ONLY_CAR, CAMERA_ACC_CAR, SDGM_CAR, CC_REGEN_PADDLE_CAR +from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD, GMFlags, CC_ONLY_CAR, CAMERA_ACC_CAR, SDGM_CAR, CC_REGEN_PADDLE_CAR, CAR TransmissionType = car.CarParams.TransmissionType NetworkLocation = car.CarParams.NetworkLocation @@ -53,9 +53,6 @@ class CarState(CarStateBase): self.moving_backward = (pt_cp.vl["EBCMWheelSpdRear"]["RLWheelDir"] == 2) or (pt_cp.vl["EBCMWheelSpdRear"]["RRWheelDir"] == 2) # Variables used for avoiding LKAS faults - self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0 - if self.loopback_lka_steering_cmd_updated: - self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"] # Track timestamps for OEM PRNDL2 and Regen Paddle messages (used to sync spoofing timing) self.prndl2_ts_nanos = pt_cp.ts_nanos["ECMPRDNL2"]["PRNDL2"] @@ -63,6 +60,9 @@ class CarState(CarStateBase): self.regen_paddle_ts_nanos = pt_cp.ts_nanos["EBCMRegenPaddle"]["RegenPaddle"] else: self.regen_paddle_ts_nanos = 0 + self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0 + if self.loopback_lka_steering_cmd_updated: + self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"] if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value: self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] self.cam_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] @@ -96,7 +96,7 @@ class CarState(CarStateBase): # Regen braking is braking if self.CP.transmissionType == TransmissionType.direct: ret.regenBraking = pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0 - self.single_pedal_mode = ret.gearShifter == GearShifter.low or pt_cp.vl["EVDriveMode"]["SinglePedalModeActive"] == 1 or (ret.regenBraking and GearShifter.manumatic) + self.single_pedal_mode = ret.gearShifter == GearShifter.low or pt_cp.vl["EVDriveMode"]["SinglePedalModeActive"] == 1 or (ret.regenBraking and GearShifter.manumatic) or (self.CP.carFingerprint in [CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_BOLT_CC] and self.CP.enableGasInterceptor) if self.CP.enableGasInterceptor: ret.gas = (pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2. @@ -150,6 +150,9 @@ class CarState(CarStateBase): ret.cruiseState.enabled = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] != AccState.OFF ret.cruiseState.standstill = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.STANDSTILL + if self.CP.carFingerprint == CAR.CHEVROLET_BOLT_EUV and self.CP.enableGasInterceptor: + ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0 + ret.cruiseState.standstill = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCruiseState"] == 4 if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value: if self.CP.carFingerprint not in CC_ONLY_CAR: ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS @@ -163,7 +166,11 @@ class CarState(CarStateBase): if self.CP.carFingerprint in CC_ONLY_CAR: ret.accFaulted = False ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS - ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0 + # Try ASCM first for cars that might have it (like misfingerprinted Bolts), fall back to ECM + try: + ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0 + except: + ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0 if self.CP.enableBsm: if self.CP.carFingerprint not in SDGM_CAR: @@ -173,7 +180,7 @@ class CarState(CarStateBase): ret.leftBlindspot = cam_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1 ret.rightBlindspot = cam_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1 - + # FrogPilot CarState functions self.lkas_previously_enabled = self.lkas_enabled if self.CP.carFingerprint in SDGM_CAR: self.lkas_enabled = cam_cp.vl["ASCMSteeringButton"]["LKAButton"] @@ -206,7 +213,7 @@ class CarState(CarStateBase): messages += [ ("AEBCmd", 10), ] - if CP.carFingerprint not in CC_ONLY_CAR: + # Include ASCMActiveCruiseControlStatus for all non-SDGM fwdCamera cars messages += [ ("ASCMActiveCruiseControlStatus", 25), ] diff --git a/selfdrive/car/gm/gmcan.py b/selfdrive/car/gm/gmcan.py index 3bbf2969b..0c0516d03 100644 --- a/selfdrive/car/gm/gmcan.py +++ b/selfdrive/car/gm/gmcan.py @@ -66,7 +66,6 @@ def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop): "GasRegenFullStopActive": at_full_stop, "GasRegenAlwaysOne": 1, "GasRegenAlwaysOne2": 1, - "GasRegenAlwaysOne3": 1, } dat = packer.make_can_msg("ASCMGasRegenCmd", bus, values)[2] @@ -177,21 +176,6 @@ def create_lka_icon_command(bus, active, critical, steer): dat = b"\x00\x00\x00" return make_can_msg(0x104c006c, dat, bus) -def create_prndl2_command(packer, bus, press_regen_paddle): - prndl2_value = 7 if press_regen_paddle else 6 - manual_mode = 1 if press_regen_paddle else 0 - values = { - "Byte0": 0x0C, - "Byte1": 0x0C, - "Byte2": 0x00, - "PRNDL2": prndl2_value, - "Byte4": 0x00, - "ManualMode": manual_mode, - "TransmissionState": 1, - "Byte7": 0x00 - } - return packer.make_can_msg("ECMPRDNL2", bus, values) - def create_regen_paddle_command(packer, bus, press_regen_paddle): regen_paddle_value = 2 if press_regen_paddle else 0 values = { @@ -205,6 +189,21 @@ def create_regen_paddle_command(packer, bus, press_regen_paddle): } return packer.make_can_msg("EBCMRegenPaddle", bus, values) +def create_prndl2_command(packer, bus, press_regen_paddle): + prndl2_value = 5 if press_regen_paddle else 6 + manual_mode = 1 if press_regen_paddle else 0 + values = { + "Byte0": 0x0C, + "Byte1": 0x0C, + "Byte2": 0x00, + "PRNDL2": prndl2_value, + "Byte4": 0x00, + "ManualMode": manual_mode, + "TransmissionState": 1, + "Byte7": 0x00 + } + return packer.make_can_msg("ECMPRDNL2", bus, values) + def create_gm_cc_spam_command(packer, controller, CS, actuators): if controller.params_.get_bool("IsMetric"): _CV = CV.MS_TO_KPH diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 632c3dfa7..030a8e12c 100644 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -27,8 +27,8 @@ CAM_MSG = 0x320 # AEBCmd ACCELERATOR_POS_MSG = 0xbe NON_LINEAR_TORQUE_PARAMS = { - CAR.CHEVROLET_BOLT_EUV: [1.8, 1.1, 0.280, -0.045], - CAR.CHEVROLET_BOLT_CC: [1.8, 1.1, 0.280, -0.045], + CAR.CHEVROLET_BOLT_EUV: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178], + CAR.CHEVROLET_BOLT_CC: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178], CAR.GMC_ACADIA: [4.78003305, 1.0, 0.3122, 0.05591772], CAR.CHEVROLET_SILVERADO: [3.29974374, 1.0, 0.25571356, 0.0465122] } @@ -111,11 +111,14 @@ class CarInterface(CarInterfaceBase): ret.longitudinalTuning.kiBP = [5., 35., 60.] if candidate in CAMERA_ACC_CAR: - ret.experimentalLongitudinalAvailable = candidate not in CC_ONLY_CAR + # For ACC models with pedal interceptor, behave like CC_ONLY_CAR + ret.experimentalLongitudinalAvailable = (candidate not in CC_ONLY_CAR) and not ret.enableGasInterceptor ret.networkLocation = NetworkLocation.fwdCamera ret.radarUnavailable = True # no radar - ret.pcmCruise = True + # Only use pcmCruise if no pedal interceptor (bolt_cc style behavior) + ret.pcmCruise = not ret.enableGasInterceptor ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM + # Use default minEnableSpeed for ACC models (will be overridden by pedal interceptor section if present) ret.minEnableSpeed = 5 * CV.KPH_TO_MS ret.minSteerSpeed = 10 * CV.KPH_TO_MS @@ -207,9 +210,11 @@ class CarInterface(CarInterfaceBase): CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) ret.lateralTuning.torque.kp = 1.0 - if ret.enableGasInterceptor: - # ACC Bolts use pedal for full longitudinal control, not just sng - ret.flags |= GMFlags.PEDAL_LONG.value + # Enable pedal interceptor for ACC models when detected + if candidate in CAMERA_ACC_CAR and ret.enableGasInterceptor: + # ACC models with pedal interceptor get full pedal longitudinal control + ret.flags |= GMFlags.PEDAL_LONG.value + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_ACC elif candidate == CAR.CHEVROLET_SILVERADO: # On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop @@ -271,7 +276,7 @@ class CarInterface(CarInterfaceBase): ret.stoppingControl = True ret.autoResumeSng = True - if candidate in CC_ONLY_CAR: #pedal interceptor tuning + if candidate in CC_ONLY_CAR or (candidate in CAMERA_ACC_CAR and ret.enableGasInterceptor): #pedal interceptor tuning ret.flags |= GMFlags.PEDAL_LONG.value ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_PEDAL_LONG # Note: Low speed, stop and go not tested. Should be fairly smooth on highway diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 52ab348dd..892329d06 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -38,10 +38,10 @@ class CarControllerParams: def __init__(self, CP): # Gas/brake lookups - self.ZERO_GAS = 6144 # Coasting + self.ZERO_GAS = 6150 # Coasting self.MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen - if CP.carFingerprint in CAMERA_ACC_CAR and CP.carFingerprint not in CC_ONLY_CAR and CP.carFingerprint != CAR.CHEVROLET_BOLT_EUV: + if CP.carFingerprint in CAMERA_ACC_CAR and CP.carFingerprint not in CC_ONLY_CAR: self.MAX_GAS = 7496 self.MAX_GAS_PLUS = 8848 self.MAX_ACC_REGEN = 5610 @@ -49,32 +49,49 @@ class CarControllerParams: # Camera ACC vehicles have no regen while enabled. # Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly self.max_regen_acceleration = 0. + self.BRAKE_SWITCH_MAX = self.MAX_ACC_REGEN if CP.carFingerprint in EV_CAR else self.ZERO_GAS elif CP.carFingerprint in SDGM_CAR: self.MAX_GAS = 7496 self.MAX_GAS_PLUS = 7496 - self.MAX_ACC_REGEN = 7110 + self.MAX_ACC_REGEN = 5610 self.INACTIVE_REGEN = 5650 self.max_regen_acceleration = 0. + self.BRAKE_SWITCH = self.ZERO_GAS else: self.MAX_GAS = 7168 # Safety limit, not ACC max. Stock ACC >8192 from standstill. self.MAX_GAS_PLUS = 8191 # 8292 uses new bit, possible but not tested. Matches Twilsonco tw-main max - self.MAX_ACC_REGEN = 7110 # Increased for stronger regen braking + self.MAX_ACC_REGEN = 7110 # Max ACC regen is slightly less than max paddle regen self.INACTIVE_REGEN = 5500 # ICE has much less engine braking force compared to regen in EVs, # lower threshold removes some braking deadzone - self.max_regen_acceleration = -3. if CP.carFingerprint in EV_CAR else -0.1 # More aggressive regen for EVs + self.max_regen_acceleration = -3. if CP.carFingerprint in EV_CAR else -0.1 + self.BRAKE_SWITCH_MAX = self.MAX_ACC_REGEN if CP.carFingerprint in EV_CAR else self.ZERO_GAS self.GAS_LOOKUP_BP = [self.max_regen_acceleration, 0., self.ACCEL_MAX] self.GAS_LOOKUP_BP_PLUS = [self.max_regen_acceleration, 0., self.ACCEL_MAX_PLUS] self.GAS_LOOKUP_V = [self.MAX_ACC_REGEN, self.ZERO_GAS, self.MAX_GAS] self.GAS_LOOKUP_V_PLUS = [self.MAX_ACC_REGEN, self.ZERO_GAS, self.MAX_GAS_PLUS] - self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, self.max_regen_acceleration] + self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, 0.] self.BRAKE_LOOKUP_V = [self.MAX_BRAKE, 0.] + self.BRAKE_SWITCH_LOOKUP_BP = [0.5, 10] + self.BRAKE_SWITCH_LOOKUP_V = [self.ZERO_GAS, self.BRAKE_SWITCH_MAX] + # determined by letting Volt regen to a stop in L gear from 89mph, + # and by letting off gas and allowing car to creep, for determining + # the positive threshold values at very low speed + EV_GAS_BRAKE_THRESHOLD_BP = [1.29, 1.52, 1.55, 1.6, 1.7, 1.8, 2.0, 2.2, 2.5, 5.52, 9.6, 20.5, 23.5, 35.0] # [m/s] + EV_GAS_BRAKE_THRESHOLD_V = [0.0, -0.14, -0.16, -0.18, -0.215, -0.255, -0.32, -0.41, -0.5, -0.72, -0.895, -1.125, -1.145, -1.16] # [m/s^s] + + def update_ev_gas_brake_threshold(self, v_ego): + gas_brake_threshold = interp(v_ego, self.EV_GAS_BRAKE_THRESHOLD_BP, self.EV_GAS_BRAKE_THRESHOLD_V) + self.GAS_LOOKUP_BP_PLUS = [self.max_regen_acceleration, 0., self.ACCEL_MAX_PLUS] + self.EV_GAS_LOOKUP_BP = [gas_brake_threshold, max(0., gas_brake_threshold), self.ACCEL_MAX] + self.EV_GAS_LOOKUP_BP_PLUS = [gas_brake_threshold, max(0., gas_brake_threshold), self.ACCEL_MAX_PLUS] + self.EV_BRAKE_LOOKUP_BP = [self.ACCEL_MIN, gas_brake_threshold] @dataclass class GMCarDocs(CarDocs): @@ -158,7 +175,7 @@ class CAR(Platforms): GMCarDocs("Chevrolet Silverado 1500 2020-21", "Safety Package II"), GMCarDocs("GMC Sierra 1500 2020-21", "Driver Alert Package II", video_link="https://youtu.be/5HbNoBLzRwE"), ], - GMCarSpecs(mass=2450, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0), + GMCarSpecs(mass=2994, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0), ) CHEVROLET_EQUINOX = GMPlatformConfig( [GMCarDocs("Chevrolet Equinox 2019-22")], @@ -194,15 +211,15 @@ class CAR(Platforms): CHEVROLET_SUBURBAN.specs, ) GMC_YUKON_CC = GMPlatformConfig( - [GMCarDocs("GMC Yukon No ACC")], + [GMCarDocs("GMC Yukon - No-ACC")], CarSpecs(mass=2541, wheelbase=2.95, steerRatio=16.3, centerToFrontRatio=0.4), ) CADILLAC_CT6_CC = GMPlatformConfig( - [GMCarDocs("Cadillac CT6 No ACC")], + [GMCarDocs("Cadillac CT6 - No-ACC")], CarSpecs(mass=2358, wheelbase=3.11, steerRatio=17.7, centerToFrontRatio=0.4), ) CHEVROLET_TRAILBLAZER_CC = GMPlatformConfig( - [GMCarDocs("Chevrolet Trailblazer 2021-22")], + [GMCarDocs("Chevrolet Trailblazer 2021-22 - No-ACC")], CHEVROLET_TRAILBLAZER.specs, ) CADILLAC_XT4 = GMPlatformConfig( @@ -210,7 +227,7 @@ class CAR(Platforms): CarSpecs(mass=1660, wheelbase=2.78, steerRatio=14.4, centerToFrontRatio=0.4), ) CADILLAC_XT5_CC = GMPlatformConfig( - [GMCarDocs("Cadillac XT5 No ACC")], + [GMCarDocs("Cadillac XT5 - No-ACC")], CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5), ) CHEVROLET_TRAVERSE = GMPlatformConfig( @@ -222,7 +239,7 @@ class CAR(Platforms): CarSpecs(mass=2050, wheelbase=2.86, steerRatio=16.0, centerToFrontRatio=0.5), ) CHEVROLET_MALIBU_CC = GMPlatformConfig( - [GMCarDocs("Chevrolet Malibu 2023 No ACC")], + [GMCarDocs("Chevrolet Malibu 2023 - No-ACC")], CarSpecs(mass=1450, wheelbase=2.8, steerRatio=15.8, centerToFrontRatio=0.4), ) CHEVROLET_TRAX = GMPlatformConfig( @@ -311,8 +328,8 @@ FW_QUERY_CONFIG = FwQueryConfig( EV_CAR = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC} CC_ONLY_CAR = {CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_EQUINOX_CC, CAR.CHEVROLET_SUBURBAN_CC, CAR.GMC_YUKON_CC, CAR.CADILLAC_CT6_CC, CAR.CHEVROLET_TRAILBLAZER_CC, CAR.CADILLAC_XT5_CC, CAR.CHEVROLET_MALIBU_CC} -CC_REGEN_PADDLE_CAR = {CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_BOLT_EUV} # CC_ONLY_CAR = set(c for c in CAR if str(c).endswith('_CC')) +CC_REGEN_PADDLE_CAR = {CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_BOLT_EUV} # We're integrated at the Safety Data Gateway Module on these cars SDGM_CAR = {CAR.CADILLAC_XT4, CAR.CHEVROLET_TRAVERSE, CAR.BUICK_BABYENCLAVE}