Tyger Tyger Burning Bright

This commit is contained in:
firestar5683
2026-06-19 15:48:26 -05:00
parent 97881b5c66
commit ce98b31cbf
6 changed files with 268 additions and 4 deletions
@@ -40,6 +40,9 @@ AUTO_HOLD_MAX_BRAKE = 240
AUTO_HOLD_MIN_DRIVE_TIME_S = 3.0
AUTO_HOLD_STOPPED_SPEED = 0.02
AUTO_HOLD_2019_MIN_BRAKE = 100
BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES = 5
BOLT_PEDAL_LONG_ACCEL_LIMIT_BP = [0.0, 1.5, 4.0, 8.0, 15.0, 30.0]
BOLT_PEDAL_LONG_ACCEL_LIMIT_V = [-0.93, -1.28, -1.98, -2.58, -2.86, -2.95]
VOLT_ONE_PEDAL_DECEL_BP = [0.5 * CV.MPH_TO_MS, 6.0 * CV.MPH_TO_MS]
VOLT_ONE_PEDAL_DECEL_V = [-1.0, -1.1]
VOLT_ONE_PEDAL_REGEN_PADDLE_DECEL_V = [-1.5, -1.6]
@@ -285,6 +288,51 @@ def get_friction_brake_bus(CP):
return CanBus.CHASSIS
def supports_bolt_acc_pedal_friction_experiment(CP) -> bool:
return (
CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and
CP.openpilotLongitudinalControl and
CP.enableGasInterceptorDEPRECATED and
bool(CP.flags & GMFlags.PEDAL_LONG.value)
)
def get_bolt_acc_pedal_friction_brake(apply_brake, full_brake_accel, v_ego, params) -> int:
if apply_brake <= 0:
return 0
full_brake_accel = min(full_brake_accel, -0.1)
legacy_full_scale = max(-params.ACCEL_MIN, 0.1)
corrected_scale = legacy_full_scale / max(-full_brake_accel, 0.1)
speed_gain = float(np.interp(v_ego, [0.0, 10.0, 25.0], [1.0, 1.15, 1.3]))
return int(round(np.clip(apply_brake * corrected_scale * speed_gain, 0, params.MAX_BRAKE)))
def get_bolt_pedal_long_accel_limit(v_ego: float) -> float:
return float(np.interp(v_ego, BOLT_PEDAL_LONG_ACCEL_LIMIT_BP, BOLT_PEDAL_LONG_ACCEL_LIMIT_V))
def get_bolt_acc_pedal_planner_brake_switch(v_ego: float, params, tire_radius: float, mass: float,
coeff_drag: float, frontal_area: float, air_density: float) -> int:
planner_accel_limit = get_bolt_pedal_long_accel_limit(v_ego)
aero_drag_force = 0.5 * coeff_drag * frontal_area * air_density * v_ego ** 2
planner_torque = tire_radius * ((mass * planner_accel_limit) + aero_drag_force)
return int(round(planner_torque + params.ZERO_GAS))
def get_bolt_acc_pedal_friction_command_state(apply_brake: int, cruise_main_on: bool, release_frames: int):
command_brake = apply_brake if cruise_main_on else 0
if command_brake > 0:
release_frames = BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES
elif release_frames > 0:
release_frames -= 1
should_send = cruise_main_on or release_frames > 0
return command_brake, release_frames, should_send
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP):
super().__init__(dbc_names, CP)
@@ -351,6 +399,7 @@ class CarController(CarControllerBase):
self.gm_auto_hold_enabled = self.params_.get_bool("GMAutoHold")
except UnknownKeyName:
self.gm_auto_hold_enabled = False
self.bolt_acc_pedal_friction_release_frames = 0
def _reset_volt_one_pedal(self):
self.volt_one_pedal_pid.reset()
@@ -678,6 +727,8 @@ class CarController(CarControllerBase):
CS.out.vEgo,
get_auto_hold_stop_threshold(self.CP, CS.auto_hold_engaged),
)
bolt_acc_pedal_friction_experiment = supports_bolt_acc_pedal_friction_experiment(self.CP)
bolt_acc_pedal_friction_main_on = bolt_acc_pedal_friction_experiment and CS.out.cruiseState.available
volt_one_pedal_braking = volt_one_pedal_active and self.volt_one_pedal_brake > 0
volt_one_pedal_hold_active = (
volt_one_pedal_braking and
@@ -806,9 +857,23 @@ class CarController(CarControllerBase):
if testing_ground.use_1:
brake_switch_bias = get_testing_ground_1_brake_switch_bias(CS.out.vEgo)
brake_switch = min(self.params.ZERO_GAS, brake_switch + brake_switch_bias)
if bolt_acc_pedal_friction_main_on:
planner_brake_switch = get_bolt_acc_pedal_planner_brake_switch(
CS.out.vEgo, self.params, self.tireRadius, self.mass, self.coeffDrag, self.frontalArea, self.airDensity,
)
brake_switch = min(brake_switch, planner_brake_switch)
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(np.interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
if bolt_acc_pedal_friction_main_on and self.apply_brake > 0:
full_brake_accel = min(
self.params.ACCEL_MIN + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2) / self.mass +
(self.params.ZERO_GAS - brake_switch) / (self.tireRadius * self.mass),
-0.1,
)
self.apply_brake = get_bolt_acc_pedal_friction_brake(
self.apply_brake, full_brake_accel, CS.out.vEgo, self.params,
)
if self.apply_brake > 0:
self.apply_gas = self.params.INACTIVE_REGEN
@@ -865,6 +930,26 @@ class CarController(CarControllerBase):
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
if self.CP.enableGasInterceptorDEPRECATED:
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
if bolt_acc_pedal_friction_experiment:
friction_brake_bus = get_friction_brake_bus(self.CP)
if self.CP.networkLocation == NetworkLocation.fwdCamera:
at_full_stop = at_full_stop and stopping
experiment_brake, self.bolt_acc_pedal_friction_release_frames, should_send_bolt_acc_pedal_friction = \
get_bolt_acc_pedal_friction_command_state(
self.apply_brake,
bolt_acc_pedal_friction_main_on,
self.bolt_acc_pedal_friction_release_frames,
)
# This fingerprint is routed through the CC-only pedal path, so it
# does not fall through to the normal friction-brake sender below.
# Never apply stock friction with cruise main off, but do send a short
# explicit zero-brake unwind so the last nonzero stock-EBCM command
# cannot linger after a disengage or main-off event.
if should_send_bolt_acc_pedal_friction:
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, friction_brake_bus, experiment_brake, idx, False, near_stop, at_full_stop, self.CP))
if self.CP.carFingerprint not in CC_ONLY_CAR:
friction_brake_bus = get_friction_brake_bus(self.CP)
# GM Camera exceptions
@@ -1,6 +1,8 @@
import sys
import types
from types import SimpleNamespace
import numpy as np
import pytest
from opendbc.car import structs
@@ -40,6 +42,10 @@ from opendbc.car.gm.carcontroller import (
estimate_auto_hold_brake,
get_adas_keepalive_step,
get_auto_hold_stop_threshold,
get_bolt_acc_pedal_friction_brake,
get_bolt_acc_pedal_friction_command_state,
get_bolt_acc_pedal_planner_brake_switch,
get_bolt_pedal_long_accel_limit,
get_lka_steering_cmd_counter,
get_volt_one_pedal_target_decel,
get_testing_ground_1_brake_switch_bias,
@@ -51,6 +57,7 @@ from opendbc.car.gm.carcontroller import (
should_send_stock_long_cancel,
should_spoof_dash_speed,
should_spoof_ecm_cruise_status,
supports_bolt_acc_pedal_friction_experiment,
supports_volt_auto_hold,
supports_volt_one_pedal,
use_interceptor_sng_launch,
@@ -107,6 +114,90 @@ def test_gen2_bolt_acc_pedal_cancel_uses_enabled_only():
assert not get_stock_cc_active_for_cancel(CP, _cs(False, AccState.ACTIVE))
def test_bolt_acc_pedal_friction_experiment_is_single_fingerprint_only():
assert supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_MALIBU_HYBRID_CC,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=False,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
def test_bolt_acc_pedal_friction_blend_preserves_zero_before_crossover():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
assert get_bolt_acc_pedal_friction_brake(0, -2.8, 20.0, params) == 0
def test_bolt_acc_pedal_friction_blend_uses_full_brake_range_after_regen():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
# Legacy mapping tops out early once regen has already consumed part of the
# decel request. The experiment remaps that reduced span back to full scale.
assert get_bolt_acc_pedal_friction_brake(286, -2.86, 20.0, params) == 400
def test_bolt_acc_pedal_friction_blend_biases_small_commands_upward_at_speed():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
low_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 0.0, params)
high_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 20.0, params)
assert low_speed > 31
assert high_speed > low_speed
def test_bolt_pedal_long_accel_limit_matches_planner_regen_envelope():
assert get_bolt_pedal_long_accel_limit(6.66) == pytest.approx(-2.379, abs=1e-3)
assert get_bolt_pedal_long_accel_limit(3.0) == pytest.approx(-1.70, abs=1e-3)
def test_bolt_acc_pedal_planner_brake_switch_delays_friction_vs_stock_switch():
params = SimpleNamespace(ZERO_GAS=6150, BRAKE_SWITCH_LOOKUP_BP=[0.5, 10.0], BRAKE_SWITCH_LOOKUP_V=[6150, 5500])
v_ego = 6.66
stock_switch = int(round(np.interp(v_ego, params.BRAKE_SWITCH_LOOKUP_BP, params.BRAKE_SWITCH_LOOKUP_V)))
planner_switch = get_bolt_acc_pedal_planner_brake_switch(
v_ego, params, tire_radius=0.336, mass=1832.0, coeff_drag=0.30, frontal_area=2.35, air_density=1.225,
)
assert planner_switch < stock_switch
def test_bolt_acc_pedal_friction_command_state_requires_cruise_main_for_positive_brake():
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, False, 0)
assert command_brake == 0
assert release_frames == 0
assert not should_send
def test_bolt_acc_pedal_friction_command_state_sends_zero_unwind_after_main_off():
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, True, 0)
assert command_brake == 120
assert release_frames > 0
assert should_send
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(0, False, release_frames)
assert command_brake == 0
assert release_frames >= 0
assert should_send
def test_stock_cancel_is_suppressed_when_acc_is_faulted():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_CAMERA)
cs = _cs(True, AccState.FAULTED)
@@ -261,6 +261,7 @@ class TestGMInterface:
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
def test_cadillac_xt5_sdgm_sascm_gates_alpha_long(self):
CarInterface = interfaces[CAR.CADILLAC_XT5]
+24 -2
View File
@@ -614,6 +614,11 @@ static safety_config gm_init(uint16_t param) {
{0x200, 0, 6, .check_relay = false},
{0x1E1, 0, 7, .check_relay = false},
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
static const CanMsg GM_CAM_BOLT_2022_PEDAL_FRICTION_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = true}, // pt bus
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = true}, // camera bus
{0x200, 0, 6, .check_relay = false},
{0x1E1, 0, 7, .check_relay = false},
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
static const CanMsg GM_CAM_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = true}, // pt bus
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = true}, // camera bus
{0x200, 0, 6, .check_relay = false},
@@ -637,6 +642,12 @@ static safety_config gm_init(uint16_t param) {
{0x200, 0, 6, .check_relay = false},
{0x1E1, 0, 7, .check_relay = false},
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
static const CanMsg GM_CAM_NO_CAMERA_BOLT_2022_PEDAL_FRICTION_TX_MSGS[] = {{0x180, 0, 4, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = false}, // pt bus
{0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false},
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, // camera bus
{0x200, 0, 6, .check_relay = false},
{0x1E1, 0, 7, .check_relay = false},
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
static const CanMsg GM_CAM_NO_CAMERA_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = false}, // pt bus
{0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false},
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, // camera bus
@@ -736,6 +747,11 @@ static safety_config gm_init(uint16_t param) {
gm_pcm_cruise = (gm_hw == GM_CAM || gm_sdgm) && !gm_cam_long && !gm_force_ascm && !gm_pedal_long;
const bool gm_ascm_int_stock_cam = gm_ascm_int && (gm_hw == GM_CAM) && gm_pcm_cruise && !gm_cam_long && !gm_pedal_long && !gm_cc_long;
const bool gm_ascm_int_no_accel_pos = gm_ascm_int && (gm_hw == GM_CAM) && gm_force_brake_c9;
// FLAG_GM_BOLT_2022_PEDAL is shared with Malibu Hybrid pedal-long. Requiring
// the paddle scheduler bit narrows this whitelist to the Gen2 Bolt pedal-long
// experiment, which is the only path that should probe chassis friction brake
// while stock ACC remains canceled.
const bool gm_bolt_2022_pedal_friction = gm_bolt_2022_pedal && gm_panda_paddle_sched && !gm_has_acc;
gm_steer_limits = GET_FLAG(param, GM_PARAM_BOLT_2017) ? &GM_BOLT_2017_STEERING_LIMITS : &GM_STEERING_LIMITS;
if ((gm_hw == GM_ASCM && !gm_sdgm) || gm_ascm_int || gm_force_ascm) {
@@ -774,13 +790,19 @@ static safety_config gm_init(uint16_t param) {
if (gm_volt_stock_brake && gm_sdgm) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_VOLT_AUTO_HOLD_TX_MSGS);
} else if (gm_no_camera) {
if (gm_volt_stock_brake) {
if (gm_bolt_2022_pedal_friction) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_BOLT_2022_PEDAL_FRICTION_TX_MSGS);
} else if (gm_volt_stock_brake) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_VOLT_AUTO_HOLD_TX_MSGS);
} else {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_TX_MSGS);
}
} else {
if (gm_volt_stock_brake) {
if (gm_bolt_2022_pedal_friction) {
// Experimental Gen2 Bolt pedal-long path: keep stock ACC canceled but
// allow OP to probe chassis friction-brake acceptance through panda.
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_BOLT_2022_PEDAL_FRICTION_TX_MSGS);
} else if (gm_volt_stock_brake) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_VOLT_AUTO_HOLD_TX_MSGS);
} else {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS);
+2 -2
View File
@@ -973,8 +973,8 @@ class SafetyTest(SafetyTestBase):
continue
if {attr, current_test}.issubset({'TestGmCameraSafety', 'TestGmCameraLongitudinalSafety', 'TestGmAscmSafety',
'TestGmCameraEVSafety', 'TestGmCameraLongitudinalEVSafety', 'TestGmAscmEVSafety',
'TestGmInterceptorSafety', 'TestGmCcLongitudinalSafety',
'TestGmCcLongitudinalPandaSchedSafety'}):
'TestGmInterceptorSafety', 'TestGmBolt2022PedalFrictionSafety',
'TestGmCcLongitudinalSafety', 'TestGmCcLongitudinalPandaSchedSafety'}):
continue
if attr.startswith('TestFord') and current_test.startswith('TestFord'):
continue
@@ -469,6 +469,71 @@ class TestGmInterceptorSafety(common.GasInterceptorSafetyTest, TestGmCameraSafet
return to_send
class TestGmBolt2022PedalFrictionSafety(TestGmInterceptorSafety):
TX_MSGS = TestGmInterceptorSafety.TX_MSGS + [[0x315, 0]]
FWD_BLACKLISTED_ADDRS = {2: [0x180, 0x370, 0x315], 0: [0x184, 0x3D1]}
RELAY_MALFUNCTION_ADDRS = {0: (0x180, 0x315), 2: (0x184,)}
EXTRA_SAFETY_PARAM = GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL
def setUp(self):
self.packer = CANPackerPanda("gm_global_a_powertrain_generated")
self.packer_chassis = CANPackerPanda("gm_global_a_chassis")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(
CarParams.SafetyModel.gm,
GMSafetyFlags.HW_CAM |
GMSafetyFlags.FLAG_GM_NO_ACC |
GMSafetyFlags.FLAG_GM_PEDAL_LONG |
GMSafetyFlags.FLAG_GM_GAS_INTERCEPTOR |
GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED |
self.EXTRA_SAFETY_PARAM)
self.safety.init_tests()
def _send_brake_msg(self, brake):
values = {"FrictionBrakeCmd": -brake}
return self.packer_chassis.make_can_msg_safety("EBCMFrictionBrakeCmd", 0, values)
def _engage_longitudinal(self):
self._rx(self.packer.make_can_msg_panda("ASCMSteeringButton", 0, {"ACCButtons": Buttons.DECEL_SET}))
self._rx(self.packer.make_can_msg_panda("ASCMSteeringButton", 0, {"ACCButtons": Buttons.UNPRESS}))
self.assertTrue(self.safety.get_controls_allowed())
self.assertTrue(self.safety.get_longitudinal_allowed())
def test_tx_hook_on_wrong_safety_mode(self):
self.skipTest("Gen2 Bolt pedal friction experiment intentionally shares the interceptor TX set plus 0x315")
def test_buttons(self):
for controls_allowed in (False, True):
self.safety.set_controls_allowed(controls_allowed)
for btn in range(8):
self.assertEqual(btn == Buttons.CANCEL, self._tx(self._button_msg(btn)))
def test_brake_allowed_when_controls_allowed(self):
self._engage_longitudinal()
self.assertTrue(self._tx(self._send_brake_msg(100)))
def test_brake_blocked_without_controls(self):
self.safety.set_controls_allowed(False)
self.assertFalse(self._tx(self._send_brake_msg(100)))
def test_brake_limit_enforced(self):
self._engage_longitudinal()
self.assertTrue(self._tx(self._send_brake_msg(400)))
self.assertFalse(self._tx(self._send_brake_msg(401)))
def test_brake_whitelist_requires_paddle_scheduler_selector(self):
self.safety.set_safety_hooks(
CarParams.SafetyModel.gm,
GMSafetyFlags.HW_CAM |
GMSafetyFlags.FLAG_GM_NO_ACC |
GMSafetyFlags.FLAG_GM_PEDAL_LONG |
GMSafetyFlags.FLAG_GM_GAS_INTERCEPTOR |
self.EXTRA_SAFETY_PARAM)
self.safety.init_tests()
self._engage_longitudinal()
self.assertFalse(self._tx(self._send_brake_msg(100)))
class TestGmCcLongitudinalSafety(TestGmCameraSafety):
TX_MSGS = [[0x180, 0], [0x370, 0], [0x1E1, 0], [0x3D1, 0], [0xBD, 0], [0x1F5, 0], [0x184, 2], [0x1E1, 2]]
FWD_BLACKLISTED_ADDRS = {2: [0x180], 0: [0x184, 0x3D1]} # block LKAS, PSCMStatus, and stock cruise status