diff --git a/docs/CARS.md b/docs/CARS.md
index b79ba6a478..af2c3007a4 100644
--- a/docs/CARS.md
+++ b/docs/CARS.md
@@ -18,8 +18,8 @@ A supported vehicle is one that just works when you install a comma three. All s
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|Cadillac|Escalade ESV 2016[2](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[](##)|[](##)|OBD-II|
-|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|Stock|3 mph|6 mph|[](##)|[](##)|GM|
-|Chevrolet|Silverado 1500 2020-21|Safety Package II|Stock|3 mph|6 mph|[](##)|[](##)|GM|
+|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[](##)|[](##)|GM|
+|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[](##)|[](##)|GM|
|Chevrolet|Volt 2017-18[2](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[](##)|[](##)|OBD-II|
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|FCA|
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|FCA|
@@ -32,7 +32,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai H|
|Genesis|G90 2017-18|All|openpilot available[1](#footnotes)|0 mph|0 mph|[](##)|[](##)|Hyundai C|
|GMC|Acadia 2018[2](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[](##)|[](##)|OBD-II|
-|GMC|Sierra 1500 2020-21|Driver Alert Package II|Stock|3 mph|6 mph|[](##)|[](##)|GM|
+|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[](##)|[](##)|GM|
|Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[](##)|[](##)|Honda Bosch A|
|Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[](##)|[](##)|Honda Bosch A|
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|Honda Nidec|
diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py
index a75eb89f25..a6cd2f19b9 100644
--- a/selfdrive/car/gm/carcontroller.py
+++ b/selfdrive/car/gm/carcontroller.py
@@ -5,10 +5,11 @@ from common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits
from selfdrive.car.gm import gmcan
-from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons, EV_CAR
+from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons
VisualAlert = car.CarControl.HUDControl.VisualAlert
NetworkLocation = car.CarParams.NetworkLocation
+LongCtrlState = car.CarControl.Actuators.LongControlState
# Camera cancels up to 0.1s after brake is pressed, ECM allows 0.5s
CAMERA_CANCEL_DELAY_FRAMES = 10
@@ -30,7 +31,7 @@ class CarController:
self.sent_lka_steering_cmd = False
self.lka_icon_status_last = (False, False)
- self.params = CarControllerParams()
+ self.params = CarControllerParams(self.CP)
self.packer_pt = CANPacker(DBC[self.CP.carFingerprint]['pt'])
self.packer_obj = CANPacker(DBC[self.CP.carFingerprint]['radar'])
@@ -83,20 +84,23 @@ class CarController:
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = 0
else:
- if self.CP.carFingerprint in EV_CAR:
- self.apply_gas = int(round(interp(actuators.accel, self.params.EV_GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
- self.apply_brake = int(round(interp(actuators.accel, self.params.EV_BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
- else:
- self.apply_gas = int(round(interp(actuators.accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
- self.apply_brake = int(round(interp(actuators.accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
+ self.apply_gas = int(round(interp(actuators.accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
+ self.apply_brake = int(round(interp(actuators.accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
idx = (self.frame // 4) % 4
at_full_stop = CC.longActive and CS.out.standstill
near_stop = CC.longActive and (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE)
+ friction_brake_bus = CanBus.CHASSIS
+ # GM Camera exceptions
+ # TODO: can we always check the longControlState?
+ if self.CP.networkLocation == NetworkLocation.fwdCamera:
+ at_full_stop = at_full_stop and actuators.longControlState == LongCtrlState.stopping
+ friction_brake_bus = CanBus.POWERTRAIN
+
# 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, CC.enabled, at_full_stop))
- can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, CanBus.CHASSIS, self.apply_brake, idx, near_stop, at_full_stop))
+ can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake, idx, CC.enabled, near_stop, at_full_stop, self.CP))
# Send dashboard UI commands (ACC status)
send_fcw = hud_alert == VisualAlert.fcw
diff --git a/selfdrive/car/gm/gmcan.py b/selfdrive/car/gm/gmcan.py
index 42df2c6afd..56c981326f 100644
--- a/selfdrive/car/gm/gmcan.py
+++ b/selfdrive/car/gm/gmcan.py
@@ -1,4 +1,5 @@
from selfdrive.car import make_can_msg
+from selfdrive.car.gm.values import CAR
def create_buttons(packer, bus, idx, button):
@@ -52,15 +53,20 @@ def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop):
return packer.make_can_msg("ASCMGasRegenCmd", bus, values)
-def create_friction_brake_command(packer, bus, apply_brake, idx, near_stop, at_full_stop):
+def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP):
mode = 0x1
+
+ # TODO: Understand this better. Volts and ICE Camera ACC cars are 0x1 when enabled with no brake
+ if enabled and CP.carFingerprint in (CAR.BOLT_EUV,):
+ mode = 0x9
+
if apply_brake > 0:
mode = 0xa
if at_full_stop:
mode = 0xd
# TODO: this is to have GM bringing the car to complete stop,
- # but currently it conflicts with OP controls, so turned off.
+ # but currently it conflicts with OP controls, so turned off. Not set by all cars
#elif near_stop:
# mode = 0xb
diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py
index 905b85436a..9ef7f388ff 100755
--- a/selfdrive/car/gm/interface.py
+++ b/selfdrive/car/gm/interface.py
@@ -20,8 +20,7 @@ BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.D
class CarInterface(CarInterfaceBase):
@staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed):
- params = CarControllerParams()
- return params.ACCEL_MIN, params.ACCEL_MAX
+ return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
# Determined by iteratively plotting and minimizing error for f(angle, speed) = steer.
@staticmethod
@@ -56,13 +55,34 @@ class CarInterface(CarInterfaceBase):
else:
ret.transmissionType = TransmissionType.automatic
+ ret.longitudinalTuning.deadzoneBP = [0.]
+ ret.longitudinalTuning.deadzoneV = [0.15]
+
+ ret.longitudinalTuning.kpBP = [5., 35.]
+ ret.longitudinalTuning.kiBP = [0.]
+
if candidate in CAMERA_ACC_CAR:
- ret.openpilotLongitudinalControl = False
+ ret.experimentalLongitudinalAvailable = True
ret.networkLocation = NetworkLocation.fwdCamera
ret.radarOffCan = True # no radar
ret.pcmCruise = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
+
+ if experimental_long:
+ ret.pcmCruise = False
+ ret.openpilotLongitudinalControl = True
+ ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
+
+ # Tuning
+ ret.longitudinalTuning.kpV = [2.0, 1.5]
+ ret.longitudinalTuning.kiV = [0.72]
+ ret.stopAccel = -2.0
+ ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling
+ ret.vEgoStopping = 0.25
+ ret.vEgoStarting = 0.25
+ ret.longitudinalActuatorDelayUpperBound = 0.5
+
else: # ASCM, OBD-II harness
ret.openpilotLongitudinalControl = True
ret.networkLocation = NetworkLocation.gateway
@@ -71,6 +91,10 @@ class CarInterface(CarInterfaceBase):
# supports stop and go, but initial engage must (conservatively) be above 18mph
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
+ # Tuning
+ ret.longitudinalTuning.kpV = [2.4, 1.5]
+ ret.longitudinalTuning.kiV = [0.36]
+
# These cars have been put into dashcam only due to both a lack of users and test coverage.
# These cars likely still work fine. Once a user confirms each car works and a test route is
# added to selfdrive/car/tests/routes.py, we can remove it from this list.
@@ -85,14 +109,6 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.1 # Default delay, not measured yet
tire_stiffness_factor = 0.444 # not optimized yet
- ret.longitudinalTuning.kpBP = [5., 35.]
- ret.longitudinalTuning.kpV = [2.4, 1.5]
- ret.longitudinalTuning.kiBP = [0.]
- ret.longitudinalTuning.kiV = [0.36]
-
- ret.longitudinalTuning.deadzoneBP = [0.]
- ret.longitudinalTuning.deadzoneV = [0.15]
-
ret.steerLimitTimer = 0.4
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py
index 85e291aaf6..eace6b6aca 100644
--- a/selfdrive/car/gm/values.py
+++ b/selfdrive/car/gm/values.py
@@ -24,15 +24,6 @@ class CarControllerParams:
ADAS_KEEPALIVE_STEP = 100
CAMERA_KEEPALIVE_STEP = 100
- # Volt gas/brake lookups
- # TODO: These values should be confirmed on non-Volt vehicles.
- # MAX_GAS should achieve 2 m/s^2 and MAX_BRAKE with regen should achieve -4.0 m/s^2
- MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill.
- ZERO_GAS = 2048 # Coasting
- MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen
- MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen
- INACTIVE_REGEN = 1404
-
# Allow small margin below -3.5 m/s^2 from ISO 15622:2018 since we
# perform the closed loop control, and might need some
# to apply some more braking if we're on a downhill slope.
@@ -41,15 +32,32 @@ class CarControllerParams:
ACCEL_MAX = 2. # m/s^2
ACCEL_MIN = -4. # m/s^2
- # ICE has much less engine braking force compared to regen in EVs,
- # lower threshold removes some braking deadzone
- GAS_LOOKUP_BP = [-0.1, 0., ACCEL_MAX]
- EV_GAS_LOOKUP_BP = [-1., 0., ACCEL_MAX]
- GAS_LOOKUP_V = [MAX_ACC_REGEN, ZERO_GAS, MAX_GAS]
-
- BRAKE_LOOKUP_BP = [ACCEL_MIN, -0.1]
- EV_BRAKE_LOOKUP_BP = [ACCEL_MIN, -1.]
- BRAKE_LOOKUP_V = [MAX_BRAKE, 0.]
+ def __init__(self, CP):
+ # Gas/brake lookups
+ self.ZERO_GAS = 2048 # Coasting
+ self.MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen
+
+ if CP.carFingerprint in CAMERA_ACC_CAR:
+ self.MAX_GAS = 3400
+ self.MAX_ACC_REGEN = 1514
+ self.INACTIVE_REGEN = 1554
+ # Camera ACC vehicles have no regen while enabled.
+ # Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly
+ max_regen_acceleration = 0.
+
+ else:
+ self.MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill.
+ self.MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen
+ self.INACTIVE_REGEN = 1404
+ # ICE has much less engine braking force compared to regen in EVs,
+ # lower threshold removes some braking deadzone
+ max_regen_acceleration = -1. if CP.carFingerprint in EV_CAR else -0.1
+
+ self.GAS_LOOKUP_BP = [max_regen_acceleration, 0., self.ACCEL_MAX]
+ self.GAS_LOOKUP_V = [self.MAX_ACC_REGEN, self.ZERO_GAS, self.MAX_GAS]
+
+ self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, max_regen_acceleration]
+ self.BRAKE_LOOKUP_V = [self.MAX_BRAKE, 0.]
class CAR:
diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit
index be8b59f6a4..7b5d954c43 100644
--- a/selfdrive/test/process_replay/ref_commit
+++ b/selfdrive/test/process_replay/ref_commit
@@ -1 +1 @@
-46922acfb984342daa35734e4297ff9d2ddfb18b
\ No newline at end of file
+d37afafc466661262b34631e32e4c383f1277bc5
\ No newline at end of file