Compare commits

...

15 Commits

Author SHA1 Message Date
firestarsdog 890876c233 Nothing to see 2026-02-24 20:06:22 -05:00
firestar5683 981cc7d30b Lat Tuning 2026-02-24 17:42:20 -06:00
firestar5683 ccadc60b13 Update tinygrad_modeld.py 2026-02-24 17:33:37 -06:00
firestar5683 a9defe7a3d Update latcontrol_torque.py 2026-02-23 21:31:00 -06:00
firestar5683 8edaf3e2e3 Revert "SC"
This reverts commit 43332bf3b3.
2026-02-23 20:44:07 -06:00
firestar5683 43332bf3b3 SC 2026-02-23 13:24:18 -06:00
firestar5683 5230a5264b 7mph engage 2026-02-22 21:06:13 -06:00
firestarsdog 50eec7fe22 Spoof CruiseSpeed Non-Acc 2026-02-22 21:06:12 -06:00
firestar5683 bcdd694154 CEM Transition 2026-02-22 21:06:12 -06:00
firestar5683 ee8028ebcd oops 2026-02-22 21:06:12 -06:00
firestar5683 90e38339e7 Cleanup fingerprint 2026-02-22 21:06:12 -06:00
firestar5683 1ad0de4e6d Update interface.py 2026-02-18 14:34:49 -06:00
firestar5683 dd58cc48ee Revert "GMC Sierra Print"
This reverts commit a7f5f3b21e.
2026-02-18 14:34:49 -06:00
firestar5683 13fbf4b90d GMC Sierra Print 2026-02-18 14:34:49 -06:00
firestar5683 dfd6f30f2c JC's follow changes 2026-02-18 14:34:49 -06:00
18 changed files with 264 additions and 79 deletions
@@ -1,4 +1,6 @@
#!/usr/bin/env python3
import time
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.realtime import DT_MDL
from openpilot.common.numpy_fast import interp
@@ -32,6 +34,10 @@ class ConditionalExperimentalMode:
LIGHT_BOOST_LOW = 1.15
LIGHT_BOOST_HIGH = 1.2
# Small latch to avoid frame-to-frame mode chatter.
CEM_TRANSITION_GUARD_TIME = 0.50
CEM_TRANSITION_BUFFER_TIME = 0.25
@staticmethod
def get_speed_based_param(speed_mph, param_array):
"""Get parameter value based on current speed using smooth interpolation between breakpoints [0, 35, 55, 70]"""
@@ -49,8 +55,12 @@ class ConditionalExperimentalMode:
self.experimental_mode = False
self.stop_light_detected = False
self.prev_experimental_mode = False # For hysteresis
self.mode_hold_until = 0.0
self.mode_false_since = 0.0
def update(self, v_ego, sm, frogpilot_toggles):
now = time.monotonic()
if frogpilot_toggles.experimental_mode_via_press:
self.status_value = params_memory.get_int("CEStatus")
else:
@@ -58,28 +68,23 @@ class ConditionalExperimentalMode:
if self.status_value not in {1, 2} and not sm["carState"].standstill:
self.update_conditions(v_ego, sm, frogpilot_toggles)
new_experimental_mode = self.check_conditions(v_ego, sm, frogpilot_toggles)
# Add hysteresis to prevent rapid toggling
if new_experimental_mode and not self.prev_experimental_mode:
# Require weaker conditions to turn on
hysteresis_factor = 0.9
elif not new_experimental_mode and self.prev_experimental_mode:
# Require stronger conditions to turn off
hysteresis_factor = 1.2
else:
hysteresis_factor = 1.0
triggered = self.check_conditions(v_ego, sm, frogpilot_toggles)
if triggered:
self.mode_hold_until = now + self.CEM_TRANSITION_GUARD_TIME
self.mode_false_since = 0.0
elif self.mode_false_since == 0.0:
self.mode_false_since = now
# Apply hysteresis to key conditions
if hasattr(self, 'slow_lead_detected'):
self.slow_lead_detected = self.slow_lead_detected if hysteresis_factor == 1.0 else (self.slow_lead_filter.x >= scale_threshold(v_ego) * hysteresis_factor)
if hasattr(self, 'curve_detected'):
self.curve_detected = self.curve_detected if hysteresis_factor == 1.0 else (self.curvature_filter.x >= THRESHOLD * hysteresis_factor)
hold_active = now < self.mode_hold_until
transition_buffer_active = self.mode_false_since != 0.0 and (now - self.mode_false_since) < self.CEM_TRANSITION_BUFFER_TIME
self.experimental_mode = self.check_conditions(v_ego, sm, frogpilot_toggles)
self.experimental_mode = triggered or hold_active or transition_buffer_active
self.prev_experimental_mode = self.experimental_mode
params_memory.put_int("CEStatus", self.status_value if self.experimental_mode else 0)
else:
self.mode_hold_until = 0.0
self.mode_false_since = 0.0
self.experimental_mode = self.status_value == 2 or sm["carState"].standstill and self.experimental_mode and self.frogpilot_planner.model_stopped
self.stop_light_detected &= self.status_value not in {1, 2}
self.stop_light_filter.x = 0
+5 -3
View File
@@ -190,6 +190,8 @@ class ModelState:
# Add policy_generation attribute after loading policy_metadata
self.policy_generation = model_version or "v8"
self.is_v11 = (self.policy_generation == "v11")
self.is_v10 = (self.policy_generation == "v10")
self.is_v12 = (self.policy_generation == "v12")
self.is_v9 = (self.policy_generation == "v9")
self.mlsim = (self.policy_generation in ("v8", "v10", "v11", "v12"))
@@ -328,14 +330,14 @@ class ModelState:
self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :]
if self.prev_desired_curv_key is not None:
# v9 models expect zeros for prev_desired_curv(s); others use history
if self.is_v9:
# v9/v10/v11/v12 models expect zeros for prev_desired_curv(s); others use history
if self.is_v9 or self.is_v10 or self.is_v11 or self.is_v12:
self.numpy_inputs[self.prev_desired_curv_key][:] = 0 * self.full_prev_desired_curv[0, self.temporal_idxs]
else:
self.numpy_inputs[self.prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
if self.off_policy_enabled and self.off_policy_prev_desired_curv_key is not None:
if self.is_v9:
if self.is_v9 or self.is_v12:
self.off_policy_numpy_inputs[self.off_policy_prev_desired_curv_key][:] = 0 * self.full_prev_desired_curv[0, self.temporal_idxs]
else:
self.off_policy_numpy_inputs[self.off_policy_prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
+118 -16
View File
@@ -48,16 +48,16 @@ const CanMsg GM_ASCM_TX_MSGS[] = {{0x180, 0, 4}, {0x409, 0, 7}, {0x40A, 0, 7}, {
{0xA1, 1, 7}, {0x306, 1, 8}, {0x308, 1, 7}, {0x310, 1, 2}, // obs bus
{0x315, 2, 5}}; // ch bus
const CanMsg GM_CAM_TX_MSGS[] = {{0x180, 0, 4}, {0x200, 0, 6}, {0x1E1, 0, 7}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
const CanMsg GM_CAM_TX_MSGS[] = {{0x180, 0, 4}, {0x370, 0, 6}, {0x200, 0, 6}, {0x1E1, 0, 7}, {0x3D1, 0, 8}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
{0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus
const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x315, 0, 5}, {0x2CB, 0, 8}, {0x370, 0, 6}, {0x200, 0, 6}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x315, 0, 5}, {0x2CB, 0, 8}, {0x370, 0, 6}, {0x200, 0, 6}, {0x3D1, 0, 8}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
{0x315, 2, 5}, {0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus
const CanMsg GM_SDGM_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
{0x184, 2, 8}}; // camera bus
const CanMsg GM_CC_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
const CanMsg GM_CC_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x370, 0, 6}, {0x1E1, 0, 7}, {0x3D1, 0, 8}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
{0x184, 2, 8}, {0x1E1, 2, 7}}; // camera bus
// TODO: do checksum and counter checks. Add correct timestep, 0.1s for now.
@@ -86,6 +86,14 @@ const uint16_t GM_PARAM_HW_SDGM = 1024;
const uint16_t GM_PARAM_BOLT_2017 = 2048;
const uint16_t GM_PARAM_BOLT_2022_PEDAL = 4096;
const uint16_t GM_PARAM_REMOTE_START_BOOTS_COMMA = 8192;
const uint16_t GM_PARAM_PANDA_3D1_SCHED = 16384;
const uint32_t GM_3D1_PERIOD_US = 100000U;
const uint32_t GM_3D1_TX_OFFSET_US = 0U;
const uint32_t GM_3D1_LOCK_TOLERANCE_US = 20000U;
void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook);
void can_set_checksum(CANPacket_t *packet);
enum {
GM_BTN_UNPRESS = 1,
@@ -111,6 +119,41 @@ bool gm_bolt_2022_pedal = false;
bool gm_ascm_int = false;
bool gm_force_brake_c9 = false;
bool gm_remote_start_boots_comma = false;
bool gm_panda_3d1_sched = false;
bool gm_3d1_spoof_valid = false;
bool gm_3d1_internal_tx = false;
uint8_t gm_3d1_spoof_data[8] = {0U};
uint32_t gm_3d1_next_tx_us = 0U;
uint32_t gm_3d1_expected_stock_us = 0U;
uint32_t gm_3d1_last_stock_us = 0U;
bool gm_3d1_phase_locked = false;
static void gm_try_send_3d1_spoof(uint32_t now_us) {
if (!(gm_panda_3d1_sched && gm_3d1_spoof_valid && (gm_3d1_next_tx_us != 0U))) {
return;
}
if ((int32_t)(now_us - gm_3d1_next_tx_us) < 0) {
return;
}
CANPacket_t to_send = {0};
to_send.returned = 0U;
to_send.rejected = 0U;
to_send.extended = 0U;
to_send.addr = 0x3D1U;
to_send.bus = 0U;
to_send.data_len_code = 8U;
(void)memcpy(to_send.data, gm_3d1_spoof_data, 8U);
can_set_checksum(&to_send);
gm_3d1_internal_tx = true;
can_send(&to_send, 0U, false);
gm_3d1_internal_tx = false;
gm_3d1_next_tx_us += GM_3D1_PERIOD_US;
}
static void gm_rx_hook(const CANPacket_t *to_push) {
if (GET_BUS(to_push) == 0U) {
@@ -183,12 +226,35 @@ static void gm_rx_hook(const CANPacket_t *to_push) {
// Cruise check for CC only cars
if ((addr == 0x3D1) && !gm_has_acc) {
uint32_t now_us = microsecond_timer_get();
gm_3d1_last_stock_us = now_us;
bool cruise_engaged = (GET_BYTE(to_push, 4) >> 7) != 0U;
if (gm_cc_long) {
pcm_cruise_check(cruise_engaged);
} else {
cruise_engaged_prev = cruise_engaged;
}
if (gm_panda_3d1_sched) {
if (!gm_3d1_phase_locked) {
gm_3d1_phase_locked = true;
gm_3d1_expected_stock_us = now_us + GM_3D1_PERIOD_US;
} else {
int32_t phase_err_us = (int32_t)(now_us - gm_3d1_expected_stock_us);
if (phase_err_us < 0) {
phase_err_us = -phase_err_us;
}
if ((uint32_t)phase_err_us <= GM_3D1_LOCK_TOLERANCE_US) {
gm_3d1_expected_stock_us += GM_3D1_PERIOD_US;
} else {
gm_3d1_expected_stock_us = now_us + GM_3D1_PERIOD_US;
}
}
gm_3d1_next_tx_us = now_us + GM_3D1_TX_OFFSET_US;
gm_try_send_3d1_spoof(now_us);
}
}
if (addr == 0xBD) {
@@ -291,6 +357,28 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
}
}
// Cruise status spoofing only for non-ACC (CC-only) paths
if (addr == 0x3D1) {
bool allowed_cruise_status = !gm_has_acc;
if (!allowed_cruise_status) {
tx = false;
} else if (gm_panda_3d1_sched) {
if (gm_3d1_internal_tx) {
tx = true;
} else {
uint32_t now_us = microsecond_timer_get();
(void)memcpy(gm_3d1_spoof_data, to_send->data, 8U);
gm_3d1_spoof_valid = true;
if (gm_3d1_next_tx_us == 0U) {
gm_3d1_next_tx_us = now_us + GM_3D1_TX_OFFSET_US;
}
bool stock_stale = (gm_3d1_last_stock_us == 0U) || (get_ts_elapsed(now_us, gm_3d1_last_stock_us) > 300000U);
bool scheduler_ready = gm_3d1_phase_locked && !stock_stale;
tx = !scheduler_ready;
}
}
}
// REGEN PADDLE
if (addr == 0xBD) {
bool regen_apply = GET_BIT(to_send, 7) || GET_BIT(to_send, 6) || GET_BIT(to_send, 5) || GET_BIT(to_send, 4);
@@ -317,26 +405,32 @@ static int gm_fwd_hook(int bus_num, int addr) {
if (bus_num == 0) {
// block PSCMStatus; forwarded through openpilot to hide an alert from the camera
bool is_pscm_msg = (addr == 0x184);
if (!is_pscm_msg) {
// For non-ACC camera/SDGM paths, keep stock ECMCruiseControl off camera side
// so openpilot's spoofed 0x3D1 is the only cruise-status source there.
bool is_ecm_cruise_status_msg = (addr == 0x3D1) && !gm_has_acc;
if (!is_pscm_msg && !is_ecm_cruise_status_msg) {
bus_fwd = 2;
}
}
if (bus_num == 2) {
bool is_lkas_msg = (addr == 0x180);
bool block_msg = false;
if (gm_bolt_2022_pedal) {
// Block 0x370 only for experimental long without pedal interceptor
bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB);
if (gm_cam_long && !enable_gas_interceptor) {
is_acc_msg = is_acc_msg || (addr == 0x370);
}
block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long);
} else {
// block lkas message and acc messages if gm_cam_long, forward all others
bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370);
block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long);
bool is_acc_status_msg = (addr == 0x370);
bool is_acc_actuation_msg = (addr == 0x315) || (addr == 0x2CB);
// Block steering if we are controlling LKA
bool block_msg = is_lkas_msg;
// Block Dashboard Status if we are in Native Long OR Pedal Long
if (gm_cam_long || gm_pedal_long) {
block_msg |= is_acc_status_msg;
}
// Block Native Actuation ONLY if we are in Native Long (not Pedal)
if (gm_cam_long) {
block_msg |= is_acc_actuation_msg;
}
if (!block_msg) {
bus_fwd = 0;
}
@@ -376,6 +470,14 @@ static safety_config gm_init(uint16_t param) {
gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC);
gm_force_brake_c9 = GET_FLAG(param, GM_PARAM_FORCE_BRAKE_C9);
gm_remote_start_boots_comma = GET_FLAG(param, GM_PARAM_REMOTE_START_BOOTS_COMMA);
gm_panda_3d1_sched = GET_FLAG(param, GM_PARAM_PANDA_3D1_SCHED) && gm_pedal_long && !gm_has_acc && !gm_bolt_2022_pedal;
gm_3d1_spoof_valid = false;
gm_3d1_internal_tx = false;
gm_3d1_next_tx_us = 0U;
gm_3d1_expected_stock_us = 0U;
gm_3d1_last_stock_us = 0U;
gm_3d1_phase_locked = false;
safety_config ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_ASCM_TX_MSGS);
if (gm_hw == GM_CAM) {
+1
View File
@@ -243,6 +243,7 @@ class Panda:
FLAG_GM_BOLT_2017 = 2048
FLAG_GM_BOLT_2022_PEDAL = 4096
FLAG_GM_REMOTE_START_BOOTS_COMMA = 8192
FLAG_GM_PANDA_3D1_SCHED = 16384
FLAG_FORD_LONG_CONTROL = 1
FLAG_FORD_CANFD = 2
+1 -1
View File
@@ -386,7 +386,7 @@ class TestGmInterceptorSafety(common.GasInterceptorSafetyTest, TestGmCameraSafet
class TestGmCcLongitudinalSafety(TestGmCameraSafety):
TX_MSGS = [[384, 0], [481, 0], [0x1F5, 0], [388, 2]]
TX_MSGS = [[384, 0], [481, 0], [0x3D1, 0], [0x1F5, 0], [388, 2]]
FWD_BLACKLISTED_ADDRS = {2: [384], 0: [388]} # block LKAS message and PSCMStatus
BUTTONS_BUS = 0 # tx only
+13 -9
View File
@@ -70,8 +70,9 @@ interface_names = _get_interface_names()
interfaces = load_interfaces(interface_names)
def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict]]:
def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict], dict[int, set[int]]]:
finger = gen_empty_fingerprint()
nonzero_addrs = {bus: set() for bus in finger}
candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1
frame = 0
car_fingerprint = None
@@ -86,7 +87,10 @@ def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict]]:
if can.src < 128:
if can.src not in finger:
finger[can.src] = {}
nonzero_addrs[can.src] = set()
finger[can.src][can.address] = len(can.dat)
if any(can.dat):
nonzero_addrs[can.src].add(can.address)
for b in candidate_cars:
# Ignore extended messages and VIN query response.
@@ -107,7 +111,7 @@ def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict]]:
frame += 1
return car_fingerprint, finger
return car_fingerprint, finger, nonzero_addrs
# **** for use live only ****
@@ -164,7 +168,7 @@ def fingerprint(logcan, sendcan, num_pandas):
# CAN fingerprint
# drain CAN socket so we get the latest messages
messaging.drain_sock_raw(logcan)
car_fingerprint, finger = can_fingerprint(lambda: get_one_can(logcan))
car_fingerprint, finger, nonzero_addrs = can_fingerprint(lambda: get_one_can(logcan))
exact_match = True
source = car.CarParams.FingerprintSource.can
@@ -183,7 +187,7 @@ def fingerprint(logcan, sendcan, num_pandas):
fw_count=len(car_fw), ecu_responses=list(ecu_rx_addrs), vin_rx_addr=vin_rx_addr, vin_rx_bus=vin_rx_bus,
fingerprints=repr(finger), fw_query_time=fw_query_time, error=True)
return car_fingerprint, finger, vin, car_fw, source, exact_match
return car_fingerprint, finger, nonzero_addrs, vin, car_fw, source, exact_match
def get_car_interface(CP, FPCP):
@@ -250,7 +254,7 @@ def migrate_legacy_bolt_candidate(candidate: str) -> str:
def get_car(logcan, sendcan, experimental_long_allowed, params, num_pandas=1, frogpilot_toggles=None):
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
candidate, fingerprints, nonzero_addrs, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
if candidate is None or frogpilot_toggles.force_fingerprint:
if frogpilot_toggles.car_model is not None:
@@ -299,12 +303,12 @@ def get_car(logcan, sendcan, experimental_long_allowed, params, num_pandas=1, fr
if year_code in year_map:
vin_candidate = year_map[year_code]
if vin_candidate == GM_CAR.CHEVROLET_BOLT_ACC_2022_2023:
has_acc_msg = (
0x370 in fingerprints.get(GMCanBus.CAMERA, {}) or
0x370 in fingerprints.get(GMCanBus.POWERTRAIN, {})
has_acc_data = (
0x370 in nonzero_addrs.get(GMCanBus.CAMERA, set()) or
0x370 in nonzero_addrs.get(GMCanBus.POWERTRAIN, set())
)
has_pedal_msg = 0x201 in fingerprints.get(GMCanBus.POWERTRAIN, {})
if has_acc_msg:
if has_acc_data:
vin_candidate = GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL if has_pedal_msg else GM_CAR.CHEVROLET_BOLT_ACC_2022_2023
else:
vin_candidate = GM_CAR.CHEVROLET_BOLT_CC_2022_2023
+25 -7
View File
@@ -368,7 +368,21 @@ class CarController(CarControllerBase):
if now_nanos - self.last_steer_ts_ns >= flush_gap_ns:
can_sends.extend(paddle_sends)
spoof_ecm_cruise_cars = {
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_BOLT_CC_2019_2021,
CAR.CHEVROLET_BOLT_CC_2022_2023,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
non_acc_pedal_long = (self.CP.flags & GMFlags.PEDAL_LONG.value) and self.CP.carFingerprint in spoof_ecm_cruise_cars and self.CP.enableGasInterceptor
if non_acc_pedal_long and self.frame % 4 == 0:
spoof_enabled = True
spoof_set_speed_kph = hud_v_cruise * CV.MS_TO_KPH
can_sends.append(gmcan.create_ecm_cruise_control_command(
self.packer_pt, CanBus.POWERTRAIN, spoof_enabled, spoof_set_speed_kph))
if self.CP.openpilotLongitudinalControl:
# Gas/regen, brakes, and UI commands - all at 25Hz
if self.frame % 4 == 0:
stopping = actuators.longControlState == LongCtrlState.stopping
@@ -419,10 +433,10 @@ class CarController(CarControllerBase):
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
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)))
@@ -467,7 +481,7 @@ class CarController(CarControllerBase):
friction_brake_bus = CanBus.CHASSIS
# GM Camera exceptions
# TODO: can we always check the longControlState?
if self.CP.networkLocation == NetworkLocation.fwdCamera and self.CP.carFingerprint not in CC_ONLY_CAR:
if self.CP.networkLocation == NetworkLocation.fwdCamera:
at_full_stop = at_full_stop and stopping
friction_brake_bus = CanBus.POWERTRAIN
if self.CP.carFingerprint in SDGM_CAR:
@@ -488,10 +502,11 @@ class CarController(CarControllerBase):
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)
is_bolt_acc_pedal = self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL
if self.CP.carFingerprint not in CC_ONLY_CAR or is_bolt_acc_pedal:
send_fcw = hud_alert == VisualAlert.fcw
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled,
hud_v_cruise * CV.MS_TO_KPH, hud_control, send_fcw))
can_sends.append(gmcan.create_acc_dashboard_command(
self.packer_pt, CanBus.POWERTRAIN, CC.enabled, hud_v_cruise * CV.MS_TO_KPH, hud_control, send_fcw))
else:
# to keep accel steady for logs when not sending gas
accel += self.accel_g
@@ -501,7 +516,10 @@ class CarController(CarControllerBase):
if not self.CP.radarUnavailable:
send_adas = True
if self.CP.carFingerprint in kaofui_cars:
send_adas = (self.CP.networkLocation != NetworkLocation.fwdCamera) and (self.CP.carFingerprint not in SDGM_CAR)
if self.CP.carFingerprint in ASCM_INT:
send_adas = True
else:
send_adas = (self.CP.networkLocation != NetworkLocation.fwdCamera) and (self.CP.carFingerprint not in SDGM_CAR)
if send_adas:
tt = self.frame * DT_CTRL
+14 -8
View File
@@ -34,6 +34,8 @@ class CarState(CarStateBase):
self.single_pedal_mode = False
self.pedal_steady = 0.
self.ecm_cruise_control_ts_nanos = 0
self.accelerator_pedal2_ts_nanos = 0
def update(self, pt_cp, cam_cp, loopback_cp, frogpilot_toggles):
ret = car.CarState.new_message()
@@ -97,7 +99,9 @@ class CarState(CarStateBase):
else:
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["ECMPRDNL2"]["PRNDL2"], None))
if self.CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value:
no_accel_pos = bool(self.CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value)
if no_accel_pos:
if self.CP.carFingerprint in kaofui_state_cars:
ret.brake = pt_cp.vl.get("EBCMBrakePedalPosition", {}).get("BrakePedalPosition", 0) / 0xd0
else:
@@ -107,20 +111,17 @@ class CarState(CarStateBase):
ret.brake = pt_cp.vl.get("ECMAcceleratorPos", {}).get("BrakePedalPos", 0)
else:
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"]
if self.CP.carFingerprint == CAR.CHEVROLET_BLAZER:
# Blazer can miss light taps on analog threshold; include digital brake switch.
ret.brakePressed = (pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0) or (ret.brake >= 0.7)
elif self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_CC:
# Malibu CC: keep strict opgm behavior using BrakePedalPos >= 8.
if self.CP.carFingerprint in {CAR.CHEVROLET_MALIBU_CC} or (self.CP.carFingerprint == CAR.CHEVROLET_BLAZER and not no_accel_pos):
ret.brakePressed = ret.brake >= 8
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or (self.CP.networkLocation == NetworkLocation.fwdCamera):
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or ((self.CP.networkLocation == NetworkLocation.fwdCamera) and (self.CP.carFingerprint != CAR.CHEVROLET_BLAZER)):
ret.brakePressed = pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0
else:
# Some Volt 2016-17 have loose brake pedal push rod retainers which causes the ECM to believe
# that the brake is being intermittently pressed without user interaction.
# To avoid a cruise fault we need to use a conservative brake position threshold
# https://static.nhtsa.gov/odi/tsbs/2017/MC-10137629-9999.pdf
analog_thresh = 0.15 if (self.CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value) else 8
analog_thresh = 0.10 if no_accel_pos else 8
ret.brakePressed = ret.brake >= analog_thresh
# Regen braking is braking
@@ -194,6 +195,8 @@ class CarState(CarStateBase):
if self.CP.pcmCruise and self.CP.carFingerprint not in ASCM_INT:
ret.cruiseState.nonAdaptive = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCruiseState"] not in (2, 3)
if self.CP.carFingerprint in CC_ONLY_CAR:
self.ecm_cruise_control_ts_nanos = pt_cp.ts_nanos["ECMCruiseControl"]["CruiseActive"]
self.accelerator_pedal2_ts_nanos = pt_cp.ts_nanos["AcceleratorPedal2"]["CruiseState"]
ret.accFaulted = False
ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS
if self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
@@ -207,6 +210,9 @@ class CarState(CarStateBase):
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
except:
ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0
else:
self.ecm_cruise_control_ts_nanos = 0
self.accelerator_pedal2_ts_nanos = 0
if self.CP.enableBsm:
if not sdgm_non_volt:
+1 -1
View File
@@ -111,7 +111,7 @@ FINGERPRINTS = {
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
}],
CAR.CHEVROLET_BOLT_CC_2019_2021: [
# Chevy Bolt EV 2019-2021
# Chevy Bolt EV 2018-2021
# Bolt Premier no ACC 2018 + Pedal
{
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 577: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1616: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 2020: 8, 2023: 8, 2028: 8, 2031: 8
+17
View File
@@ -185,6 +185,23 @@ def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_con
return packer.make_can_msg("ASCMActiveCruiseControlStatus", bus, values)
def create_ecm_cruise_control_command(packer, bus, enabled, target_speed_kph):
dat = bytearray(8)
dat[0] = 0x01
# Match observed stock shape on non-ACC CC paths: byte4 is usually 0x00
# (with occasional 0x80 from stock state transitions). Keep this spoofed
# path at 0x00 to avoid plausibility mismatch on non-speed bits.
dat[4] = 0x00
set_speed_raw = 0
if enabled:
set_speed_raw = int(round(max(0., target_speed_kph) / 0.0625))
set_speed_raw = max(0, min(set_speed_raw, 0x0FFF))
dat[2] = (set_speed_raw >> 8) & 0xFF
dat[3] = set_speed_raw & 0xFF
return make_can_msg(0x3D1, bytes(dat), bus)
def create_adas_time_status(bus, tt, idx):
dat = [(tt >> 20) & 0xff, (tt >> 12) & 0xff, (tt >> 4) & 0xff,
+18 -2
View File
@@ -382,6 +382,14 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.torque.kd = 0.93
ret.lateralTuning.torque.kfDEPRECATED = 0.02
if candidate in (CAR.CHEVROLET_BOLT_CC_2019_2021,
CAR.CHEVROLET_BOLT_ACC_2022_2023,
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023):
# Apply 2019-style negative FF and Ki-mult tweaks to 2019-2021 and 2022 variants.
ret.lateralTuning.torque.ki *= 1.07
ret.lateralTuning.torque.kd *= 0.93
if candidate == CAR.CHEVROLET_BOLT_CC_2017:
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_BOLT_2017
@@ -399,8 +407,6 @@ class CarInterface(CarInterfaceBase):
# On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop
# with foot on brake to allow engagement, but this platform only has that check in the camera.
# TODO: check if this is split by EV/ICE with more platforms in the future
if ret.openpilotLongitudinalControl:
ret.minEnableSpeed = -1.
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (CAR.CHEVROLET_EQUINOX, CAR.CHEVROLET_EQUINOX_CC):
@@ -536,6 +542,16 @@ class CarInterface(CarInterfaceBase):
if ACCELERATOR_POS_MSG not in fingerprint.get(CanBus.POWERTRAIN, {}):
ret.flags |= GMFlags.NO_ACCELERATOR_POS_MSG.value
use_panda_3d1_sched = (
ret.openpilotLongitudinalControl and
ret.enableGasInterceptor and
bool(ret.flags & GMFlags.PEDAL_LONG.value) and
candidate in CC_ONLY_CAR and
candidate != CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL
)
if use_panda_3d1_sched:
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_PANDA_3D1_SCHED
return ret
# returns a car.CarState
+1 -2
View File
@@ -273,7 +273,7 @@ class CAR(Platforms):
CHEVROLET_VOLT.specs,
)
CHEVROLET_BOLT_CC_2019_2021 = GMPlatformConfig(
[GMCarDocs("Chevrolet Bolt EV 2019-2021 - No-ACC")],
[GMCarDocs("Chevrolet Bolt EV 2018-2021 - No-ACC")],
CHEVROLET_BOLT_ACC_2022_2023.specs,
)
CHEVROLET_BOLT_ACC_2022_2023_PEDAL = GMPlatformConfig(
@@ -285,7 +285,6 @@ class CAR(Platforms):
CHEVROLET_BOLT_CC_2022_2023 = GMPlatformConfig(
[
GMCarDocs("Chevrolet Bolt EV 2022-2023 - No-ACC"),
GMCarDocs("Chevrolet Bolt EV 2022-2023 - No-ACC"),
],
CHEVROLET_BOLT_ACC_2022_2023.specs,
)
+2 -2
View File
@@ -17,7 +17,7 @@ class TestCanFingerprint:
fingerprint_iter = iter([can])
empty_can = messaging.new_message('can', 0)
car_fingerprint, finger = can_fingerprint(lambda: next(fingerprint_iter, empty_can)) # noqa: B023
car_fingerprint, finger, _ = can_fingerprint(lambda: next(fingerprint_iter, empty_can)) # noqa: B023
assert car_fingerprint == car_model
assert finger[0] == fingerprint
@@ -56,6 +56,6 @@ class TestCanFingerprint:
frames += 1
return can # noqa: B023
car_fingerprint, _ = can_fingerprint(test)
car_fingerprint, _, _ = can_fingerprint(test)
assert car_fingerprint == car_model
assert frames == expected_frames + 2# TODO: fix extra frames
+3 -1
View File
@@ -44,7 +44,9 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"CADILLAC_ESCALADE_ESV_2019" = [1.15, 1.3, 0.2]
"CADILLAC_XT4" = [1.45, 1.6, 0.2]
"CADILLAC_XT6" = [1.33, 1.9, 0.16]
"CHEVROLET_BOLT_ACC_2022_2023" = [2.0, 2.0, 0.09]
"CHEVROLET_BOLT_ACC_2022_2023" = [2.0, 2.0, 0.13]
"CHEVROLET_BOLT_CC_2017" = [1.5, 2.0, 0.245]
"CHEVROLET_BOLT_CC_2019_2021" = [2.0, 2.0, 0.13]
"CHEVROLET_BLAZER" = [1.33, 1.33, 0.18]
"CHEVROLET_MALIBU_CC" = [1.58, 1.8422651988094612, 0.205]
"CHEVROLET_SILVERADO" = [1.9, 1.9, 0.112]
@@ -64,10 +64,8 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"CHEVROLET_VOLT_ASCM" = "CHEVROLET_VOLT"
"GMC_ACADIA_ASCM" = "GMC_ACADIA"
"CHEVROLET_VOLT_2019" = "CHEVROLET_VOLT"
"CHEVROLET_BOLT_CC_2019_2021" = "CHEVROLET_BOLT_ACC_2022_2023"
"CHEVROLET_BOLT_ACC_2022_2023_PEDAL" = "CHEVROLET_BOLT_ACC_2022_2023"
"CHEVROLET_BOLT_CC_2022_2023" = "CHEVROLET_BOLT_ACC_2022_2023"
"CHEVROLET_BOLT_CC_2017" = "CHEVROLET_BOLT_ACC_2022_2023"
"CHEVROLET_EQUINOX_CC" = "CHEVROLET_EQUINOX"
"CHEVROLET_SUBURBAN" = "CHEVROLET_SILVERADO"
"CHEVROLET_SUBURBAN_CC" = "CHEVROLET_SILVERADO"
+2 -3
View File
@@ -147,8 +147,7 @@ class VCruiseHelper:
# initializing is handled by the PCM
if self.CP.pcmCruise:
return
initial = V_CRUISE_INITIAL_EXPERIMENTAL_MODE if experimental_mode and not frogpilot_toggles.conditional_experimental_mode else V_CRUISE_INITIAL
engage_floor_kph = max(V_CRUISE_MIN, 7.0 * CV.MPH_TO_KPH)
# 250kph or above probably means we never had a set speed
if any(b.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for b in CS.buttonEvents) and self.v_cruise_kph_last < 250:
@@ -157,7 +156,7 @@ class VCruiseHelper:
if desired_speed_limit != 0 and frogpilot_toggles.set_speed_limit:
self.v_cruise_kph = int(round(desired_speed_limit * CV.MS_TO_KPH))
else:
self.v_cruise_kph = int(round(clip(CS.vEgo * CV.MS_TO_KPH, initial, V_CRUISE_MAX)))
self.v_cruise_kph = int(round(clip(CS.vEgo * CV.MS_TO_KPH, engage_floor_kph, V_CRUISE_MAX)))
self.v_cruise_cluster_kph = self.v_cruise_kph
+18 -4
View File
@@ -43,11 +43,18 @@ DEADZONE_BOOST_LAT_ACCEL = 0.08
UNWIND_D_DES_THRESHOLD = -1.0
UNWIND_LAT_ACCEL_NEAR_ZERO = 0.3
BOLT_CARS = (
BOLT_2022_2023_CARS = (
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
)
BOLT_2019_2021_CARS = (
GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
)
BOLT_2017_CARS = (
GM_CAR.CHEVROLET_BOLT_CC_2017,
)
BOLT_CARS = BOLT_2022_2023_CARS + BOLT_2019_2021_CARS + BOLT_2017_CARS
class LatControlTorque(LatControl):
def __init__(self, CP, CI, dt):
@@ -71,6 +78,13 @@ class LatControlTorque(LatControl):
self.prev_desired_lateral_accel = 0.0
self.is_bolt = CP.carFingerprint in BOLT_CARS
self.is_bolt_2022_2023 = CP.carFingerprint in BOLT_2022_2023_CARS
self.is_bolt_2019_2021 = CP.carFingerprint in BOLT_2019_2021_CARS
self.is_bolt_2017 = CP.carFingerprint in BOLT_2017_CARS
# Keep Bolt-specific FF controls isolated by generation.
self.use_bolt_ff_scaling = self.is_bolt_2022_2023 or self.is_bolt_2019_2021
self.use_bolt_deadzone_boost = self.is_bolt_2022_2023 or self.is_bolt_2019_2021
self.use_bolt_ki_multiplier = self.is_bolt_2022_2023 or self.is_bolt_2019_2021
self.torque_ff_scale_pos = 1.0
self.torque_ff_scale_neg = 1.0
self.torque_deadzone_boost_neg = 0.0
@@ -80,7 +94,7 @@ class LatControlTorque(LatControl):
self.torque_ff_scale_neg = float(self.torque_params.ki)
self.torque_ki_mult = float(self.torque_params.kd)
self.torque_deadzone_boost_neg = float(getattr(self.torque_params, "kfDEPRECATED", 0.0))
if self.torque_ki_mult > 0.0 and self.torque_ki_mult != 1.0:
if self.use_bolt_ki_multiplier and self.torque_ki_mult > 0.0 and self.torque_ki_mult != 1.0:
self.pid._k_i = [self.pid._k_i[0], [k * self.torque_ki_mult for k in self.pid._k_i[1]]]
def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction):
@@ -143,13 +157,13 @@ class LatControlTorque(LatControl):
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
ff -= self.torque_params.latAccelOffset
ff_scale = 1.0
if self.is_bolt:
if self.use_bolt_ff_scaling:
ff_scale = np.interp(ff, [-FF_SCALE_BLEND_LAT_ACCEL, 0.0, FF_SCALE_BLEND_LAT_ACCEL],
[self.torque_ff_scale_neg, 1.0, self.torque_ff_scale_pos])
ff *= ff_scale
ff += get_friction(error_with_lsf + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, get_friction_threshold(CS.vEgo), self.torque_params)
deadzone_boost_active = False
if self.is_bolt and self.torque_deadzone_boost_neg > 0.0 and gravity_adjusted_future_lateral_accel < 0.0:
if self.use_bolt_deadzone_boost and self.torque_deadzone_boost_neg > 0.0 and gravity_adjusted_future_lateral_accel < 0.0:
if abs(gravity_adjusted_future_lateral_accel) < DEADZONE_BOOST_LAT_ACCEL:
boost_scale = np.interp(abs(gravity_adjusted_future_lateral_accel), [0.0, DEADZONE_BOOST_LAT_ACCEL], [1.0, 0.0])
ff -= self.torque_deadzone_boost_neg * boost_scale
@@ -348,7 +348,8 @@ class LongitudinalPlanner:
(self.a_desired > 0.0) and
self.stable_lead and
(uncertainty <= 0.425) and
(desire_entropy < 0.41)
(desire_entropy < 0.41) and
(v_ego > 5.0)
)
# dwell timers for robust gating
@@ -427,7 +428,8 @@ class LongitudinalPlanner:
self.a_desired = float(self.a_desired - pre_brake)
# Apply tiny feed-forward nudge when released and safe
if now_t < self.accel_nudge_until and self.a_desired > -0.1:
close_lead = self.lead_one.status and self.lead_one.dRel < 10.0
if now_t < self.accel_nudge_until and self.a_desired > -0.1 and not close_lead:
self.a_desired = float(min(self.a_desired + 0.12, get_max_accel(v_ego)))
# Small deadzone around zero accel to kill micro-dithers