mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-24 09:43:47 +08:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d1a99f2af | |||
| a4d61232c6 | |||
| 5881c17c59 | |||
| d5e971c32b | |||
| 3024ed42f5 | |||
| 09a073c242 | |||
| 231ef85bed | |||
| 740668c973 | |||
| b343c6aa98 | |||
| cc1a0e4697 | |||
| c32a5fda39 | |||
| af14724e78 | |||
| c85a34f324 | |||
| 70260c0ed2 | |||
| 0110bec1af | |||
| 94d881211c | |||
| cea5b9cada | |||
| 4269bb2309 | |||
| 5cc94240cd | |||
| 201458c3d6 | |||
| 1af7a5ba65 | |||
| fb3d1a1392 | |||
| d650757ff9 | |||
| 91e273e489 | |||
| d620145cdc | |||
| 23a5bedabc | |||
| ea45cbf382 | |||
| a1f7b451a6 | |||
| 1fabfd04bd | |||
| 8630693f7a | |||
| 6df0333380 | |||
| 8708ab2496 | |||
| 01ed483036 | |||
| 23fb048ba6 | |||
| bee7628900 | |||
| 9b84afc5a8 | |||
| 5f94a5b9e0 | |||
| 41d69c31e7 | |||
| f6c89beebd | |||
| 62c1345b2a | |||
| 97bfbfbb0d | |||
| 67c9a8b52e | |||
| 2f253174e9 | |||
| 5a39fa4ac1 | |||
| 8a54eb5733 | |||
| a1edc6d6e7 | |||
| 05ded9d10b | |||
| d387c7934f | |||
| 4c9d393e37 | |||
| c14680edcb | |||
| 95ea985218 | |||
| 376aeff9e3 | |||
| 680c3acaf0 | |||
| 9cc2c20cdc | |||
| 8c81eadf5d | |||
| 88a29c67d0 | |||
| 6fe8f8b722 | |||
| b5b942a108 | |||
| 5e88e5beff | |||
| 8f27c4937c | |||
| c95f8fa8fc | |||
| 8b55472a90 | |||
| 9bcfeb1cf3 | |||
| 93713aaacf | |||
| 602641856d | |||
| 1f0d2092a3 | |||
| 587dc140b7 | |||
| 63ee52b4aa | |||
| eb19b336e2 | |||
| 06f95d0dcd | |||
| cd7f2f8bc6 | |||
| 82af86e04c | |||
| 6934f375d5 | |||
| 09afc383fd | |||
| 19f7685744 | |||
| 6477ba6a5e |
@@ -49,8 +49,8 @@ A_CRUISE_MIN_SPORT = A_CRUISE_MIN * 2
|
||||
A_CRUISE_MAX_BP_CUSTOM = [0.0, 5., 10., 15., 20., 25., 40.]
|
||||
A_CRUISE_MAX_VALS_ECO_EV = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
|
||||
A_CRUISE_MAX_VALS_SPORT_EV = [1.25, 1.25, 1.25, 1.25, 1.5, 1.5, 2.0]
|
||||
A_CRUISE_MAX_VALS_ECO_GAS = [6.0, 1.40, 0.90, 0.65, 0.60, 0.55, 0.42]
|
||||
A_CRUISE_MAX_VALS_SPORT_GAS = [6.0, 1.50, 1.00, 0.72, 0.65, 0.60, 0.45]
|
||||
A_CRUISE_MAX_VALS_ECO_GAS = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2]
|
||||
A_CRUISE_MAX_VALS_SPORT_GAS = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6]
|
||||
|
||||
def get_max_accel_eco(v_ego, ev_tuning=True):
|
||||
cruise_vals = A_CRUISE_MAX_VALS_ECO_EV if ev_tuning else A_CRUISE_MAX_VALS_ECO_GAS
|
||||
|
||||
Regular → Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -120,14 +120,14 @@ def get_city_center(latitude, longitude):
|
||||
|
||||
def update_branch_commits(now):
|
||||
points = []
|
||||
for branch in ["FrogPilot", "FrogPilot-Staging", "FrogPilot-Testing"]:
|
||||
try:
|
||||
response = requests.get(f"https://api.github.com/repos/FrogAi/FrogPilot/commits/{branch}")
|
||||
response.raise_for_status()
|
||||
sha = response.json()["sha"]
|
||||
points.append(Point("branch_commits").field("commit", sha).tag("branch", branch).time(now))
|
||||
except Exception as e:
|
||||
print(f"Failed to fetch commit for {branch}: {e}")
|
||||
branch = get_build_metadata().channel # Current running branch
|
||||
try:
|
||||
response = requests.get(f"https://api.github.com/repos/firestar5683/StarPilot/commits/{branch}")
|
||||
response.raise_for_status()
|
||||
sha = response.json()["sha"]
|
||||
points.append(Point("branch_commits").field("commit", sha).tag("branch", branch).time(now))
|
||||
except Exception as e:
|
||||
print(f"Failed to fetch commit for {branch}: {e}")
|
||||
|
||||
return points
|
||||
|
||||
|
||||
@@ -82,12 +82,6 @@ VAL_TABLE_ HandsOffSWDetectionMode 2 "Failed" 1 "Enabled" 0 "Disabled" ;
|
||||
|
||||
BO_ 189 EBCMRegenPaddle: 7 K17_EBCM
|
||||
SG_ RegenPaddle : 7|4@0+ (1,0) [0|0] "" NEO
|
||||
SG_ Byte1 : 8|8@1+ (1,0) [0|255] "" NEO
|
||||
SG_ Byte2 : 16|8@1+ (1,0) [0|255] "" NEO
|
||||
SG_ Byte3 : 24|8@1+ (1,0) [0|255] "" NEO
|
||||
SG_ Byte4 : 32|8@1+ (1,0) [0|255] "" NEO
|
||||
SG_ Byte5 : 40|8@1+ (1,0) [0|255] "" NEO
|
||||
SG_ Byte6 : 48|8@1+ (1,0) [0|255] "" NEO
|
||||
|
||||
BO_ 190 ECMAcceleratorPos: 6 K20_ECM
|
||||
SG_ BrakePedalPos : 15|8@0+ (1,0) [0|0] "sticky" NEO
|
||||
@@ -198,15 +192,10 @@ BO_ 500 SportMode: 6 XXX
|
||||
SG_ SportMode : 15|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 501 ECMPRDNL2: 8 K20_ECM
|
||||
SG_ Byte0 : 0|8@1+ (1,0) [0|255] "" NEO
|
||||
SG_ Byte1 : 8|8@1+ (1,0) [0|255] "" NEO
|
||||
SG_ Byte2 : 16|8@1+ (1,0) [0|255] "" NEO
|
||||
SG_ PRNDL2 : 27|4@0+ (1,0) [0|255] "" NEO
|
||||
SG_ Byte4 : 32|8@1+ (1,0) [0|255] "" NEO
|
||||
SG_ ManualMode : 41|1@0+ (1,0) [0|1] "" NEO
|
||||
SG_ TransmissionState : 48|4@1+ (1,0) [0|7] "" NEO
|
||||
SG_ Byte7 : 56|8@1+ (1,0) [0|255] "" NEO
|
||||
|
||||
SG_ PRNDL2 : 27|4@0+ (1,0) [0|255] "" NEO
|
||||
SG_ ManualMode : 41|1@0+ (1,0) [0|1] "" NEO
|
||||
|
||||
BO_ 532 BRAKE_RELATED: 6 XXX
|
||||
SG_ UserBrakePressure : 0|9@0+ (1,0) [0|511] "" XXX
|
||||
|
||||
@@ -381,6 +370,6 @@ VAL_ 715 GasRegenCmdActive 1 "Active" 0 "Inactive" ;
|
||||
VAL_ 320 Intellibeam 1 "Active" 0 "Inactive" ;
|
||||
VAL_ 320 HighBeamsActive 1 "Active" 0 "Inactive" ;
|
||||
VAL_ 320 HighBeamsTemporary 1 "Active" 0 "Inactive" ;
|
||||
VAL_ 501 PRNDL2 7 "L2" 6 "L" 5 "L3" 4 "D" 3 "N" 2 "R" 1 "P" 0 "Shifting";
|
||||
VAL_ 501 PRNDL2 6 "L" 4 "D" 3 "N" 2 "R" 1 "P" 0 "Shifting";
|
||||
VAL_ 501 TransmissionState 11 "Shifting" 10 "Reverse" 9 "Forward" 8 "Disengaged";
|
||||
VAL_ 501 ManualMode 1 "Active" 0 "Inactive"
|
||||
|
||||
@@ -202,9 +202,9 @@ void ignition_can_hook(CANPacket_t *to_push) {
|
||||
int len = GET_LEN(to_push);
|
||||
|
||||
// GM exception
|
||||
if ((addr == 0xC9) && (len == 8)) {
|
||||
// Matches SystemPowerMode (1=Run, 0=Off)
|
||||
ignition_can = (GET_BYTE(to_push, 6) & 0x10U) != 0U;
|
||||
if ((addr == 0x1F1) && (len == 8)) {
|
||||
// SystemPowerMode (2=Run, 3=Crank Request)
|
||||
ignition_can = (GET_BYTE(to_push, 0) & 0x2U) != 0U;
|
||||
ignition_can_cnt = 0U;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ int safety_fwd_hook(int bus_num, int addr) {
|
||||
}
|
||||
|
||||
bool get_longitudinal_allowed(void) {
|
||||
return controls_allowed && !gas_pressed;
|
||||
return controls_allowed && !gas_pressed_prev;
|
||||
}
|
||||
|
||||
// Given a CRC-8 poly, generate a static lookup table to use with a fast CRC-8
|
||||
|
||||
@@ -10,16 +10,16 @@ const SteeringLimits GM_STEERING_LIMITS = {
|
||||
};
|
||||
|
||||
const LongitudinalLimits GM_ASCM_LONG_LIMITS = {
|
||||
.max_gas = 7168,
|
||||
.min_gas = 5500,
|
||||
.inactive_gas = 5500,
|
||||
.max_gas = 3072,
|
||||
.min_gas = 1404,
|
||||
.inactive_gas = 1404,
|
||||
.max_brake = 400,
|
||||
};
|
||||
|
||||
const LongitudinalLimits GM_CAM_LONG_LIMITS = {
|
||||
.max_gas = 7496,
|
||||
.min_gas = 5610,
|
||||
.inactive_gas = 5650,
|
||||
.max_gas = 3400,
|
||||
.min_gas = 1514,
|
||||
.inactive_gas = 1554,
|
||||
.max_brake = 400,
|
||||
};
|
||||
|
||||
@@ -29,23 +29,23 @@ const int GM_STANDSTILL_THRSLD = 10; // 0.311kph
|
||||
|
||||
// panda interceptor threshold needs to be equivalent to openpilot threshold to avoid controls mismatches
|
||||
// If thresholds are mismatched then it is possible for panda to see the gas fall and rise while openpilot is in the pre-enabled state
|
||||
const int GM_GAS_INTERCEPTOR_THRESHOLD = 595; // (675 + 355) / 2 ratio between offset and gain from dbc file
|
||||
const int GM_GAS_INTERCEPTOR_THRESHOLD = 515; // (675 + 355) / 2 ratio between offset and gain from dbc file
|
||||
#define GM_GET_INTERCEPTOR(msg) (((GET_BYTE((msg), 0) << 8) + GET_BYTE((msg), 1) + (GET_BYTE((msg), 2) << 8) + GET_BYTE((msg), 3)) / 2U) // avg between 2 tracks
|
||||
|
||||
const CanMsg GM_ASCM_TX_MSGS[] = {{0x180, 0, 4}, {0x409, 0, 7}, {0x40A, 0, 7}, {0x2CB, 0, 8}, {0x370, 0, 6}, {0x200, 0, 6}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
|
||||
const CanMsg GM_ASCM_TX_MSGS[] = {{0x180, 0, 4}, {0x409, 0, 7}, {0x40A, 0, 7}, {0x2CB, 0, 8}, {0x370, 0, 6}, {0x200, 0, 6}, // pt bus
|
||||
{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}, {0x200, 0, 6}, {0x1E1, 0, 7}, // 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}, // pt bus
|
||||
{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
|
||||
const CanMsg GM_SDGM_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, // 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}, {0x1E1, 0, 7}, // pt bus
|
||||
{0x184, 2, 8}, {0x1E1, 2, 7}}; // camera bus
|
||||
|
||||
// TODO: do checksum and counter checks. Add correct timestep, 0.1s for now.
|
||||
@@ -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) != 0U;
|
||||
brake_pressed = GET_BIT(to_push, 40U);
|
||||
}
|
||||
|
||||
if (addr == 0xC9) {
|
||||
@@ -229,7 +229,7 @@ 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, 1) & 0x1U) << 13) + ((GET_BYTE(to_send, 2) & 0xFFU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3);
|
||||
int gas_regen = ((GET_BYTE(to_send, 2) & 0x7FU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3);
|
||||
|
||||
bool violation = false;
|
||||
// Allow apply bit in pre-enabled and overriding states
|
||||
@@ -256,22 +256,6 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!controls_allowed && regen_apply) {
|
||||
tx = false;
|
||||
}
|
||||
}
|
||||
|
||||
// PRNDL2 regen check (7 for Gen0, Gen1. 5 For Gen2)
|
||||
if (addr == 0x1F5) {
|
||||
uint8_t prndl2 = GET_BYTE(to_send, 3) & 0xF;
|
||||
bool prndl_apply = (prndl2 == 7) || (prndl2 == 5);
|
||||
if (!controls_allowed && prndl_apply) {
|
||||
tx = false;
|
||||
}
|
||||
}
|
||||
return tx;
|
||||
}
|
||||
|
||||
@@ -313,9 +297,9 @@ static safety_config gm_init(uint16_t param) {
|
||||
gm_force_ascm = GET_FLAG(param, GM_PARAM_HW_ASCM_LONG);
|
||||
|
||||
if (gm_hw == GM_ASCM || gm_force_ascm) {
|
||||
gm_long_limits = &GM_ASCM_LONG_LIMITS;
|
||||
gm_long_limits = &GM_ASCM_LONG_LIMITS;
|
||||
} else if ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM)) {
|
||||
gm_long_limits = &GM_CAM_LONG_LIMITS;
|
||||
gm_long_limits = &GM_CAM_LONG_LIMITS;
|
||||
} else {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
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
|
||||
@@ -11,11 +7,10 @@ from openpilot.common.params_pyx import Params
|
||||
from opendbc.can.packer import CANPacker
|
||||
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, create_gas_interceptor_command
|
||||
from openpilot.selfdrive.car.gm import gmcan
|
||||
from openpilot.selfdrive.car.gm.values import CAR, DBC, AccState, CanBus, CarControllerParams, CruiseButtons, GMFlags, CC_ONLY_CAR, SDGM_CAR, EV_CAR, CC_REGEN_PADDLE_CAR
|
||||
from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, CarControllerParams, CruiseButtons, GMFlags, CC_ONLY_CAR, SDGM_CAR, EV_CAR
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import apply_deadzone
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
NetworkLocation = car.CarParams.NetworkLocation
|
||||
@@ -27,15 +22,7 @@ TransmissionType = car.CarParams.TransmissionType
|
||||
CAMERA_CANCEL_DELAY_FRAMES = 10
|
||||
# Enforce a minimum interval between steering messages to avoid a fault
|
||||
MIN_STEER_MSG_INTERVAL_MS = 15
|
||||
# Two‑sided spacing tuned for ~33 Hz steer; target a 10 ms wide window per interval
|
||||
# Paddle spoofing and scheduling constants
|
||||
PADDLE_STEER_GAP_MIN_NS = 5_000_000 # ≥5 ms each side (EPS guard)
|
||||
PADDLE_STEER_GAP_MAX_NS = 12_000_000 # cap for long intervals
|
||||
PADDLE_GAP_TARGET_NS = 5_000_000 # aim per‑side gap even if interval//2 − early is larger
|
||||
PADDLE_NONBLOCK_GAP_NS = 1_000_000 # ≥1 ms since last paddle send
|
||||
PADDLE_SLOT_EARLY_NS = 1_000_000 # allow firing up to 1 ms before slot
|
||||
OVERFLOW_THRESH = 1.00 # fire one extra slot whenever credits ≥ 1.0
|
||||
PADDLE_TARGET_HZ = 42.0 # desired paddle rate (Hz) when regen active; steer is ~33 Hz
|
||||
|
||||
# Constants for pitch compensation
|
||||
BRAKE_PITCH_FACTOR_BP = [5., 10.] # [m/s] smoothly revert to planned accel at low speeds
|
||||
BRAKE_PITCH_FACTOR_V = [0., 1.] # [unitless in [0,1]]; don't touch
|
||||
@@ -51,11 +38,6 @@ class CarController(CarControllerBase):
|
||||
self.apply_speed = 0
|
||||
self.frame = 0
|
||||
self.last_steer_frame = 0
|
||||
self.last_steer_ts_ns = 0
|
||||
self.last_regen_active = False
|
||||
self.prev_steer_ts_ns = 0
|
||||
self.last_spoof_ts_ns = 0
|
||||
self.last_paddle_ts_ns = 0
|
||||
self.last_button_frame = 0
|
||||
self.cancel_counter = 0
|
||||
self.pedal_steady = 0.
|
||||
@@ -66,101 +48,33 @@ 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
|
||||
self.aego = 0.0
|
||||
self.regen_paddle_timer = 0
|
||||
self.planner_regen_hold = False
|
||||
|
||||
@staticmethod
|
||||
def calc_pedal_command(accel: float, long_active: bool) -> float:
|
||||
if not long_active: return 0.
|
||||
|
||||
zero = 0.15625 # 40/256
|
||||
if accel > 0.:
|
||||
# Scales the accel from 0-1 to 0.156-1
|
||||
pedal_gas = clip(((1 - zero) * accel + zero), 0., 1.)
|
||||
else:
|
||||
# if accel is negative, -0.1 -> 0.015625
|
||||
pedal_gas = clip(zero + accel, 0., zero) # Make brake the same size as gas, but clip to regen
|
||||
|
||||
# Midpoint + overflow spoof accumulator and flags
|
||||
self.spoof_accum = 0.0
|
||||
self.spoof_mid_sent = False
|
||||
self.spoof_over_sent = False
|
||||
self.last_interval_ns = 0
|
||||
|
||||
def calc_pedal_command(self, accel: float, long_active: bool, car_velocity) -> Tuple[float, bool]:
|
||||
if not long_active:
|
||||
self.planner_regen_hold = False
|
||||
return 0., False
|
||||
|
||||
# Regen paddle hysteresis (frame-based): hold 10 frames, with decrement dead-zone
|
||||
if not hasattr(self, 'regen_paddle_timer'):
|
||||
self.regen_paddle_timer = 0 # frames
|
||||
|
||||
# Regen paddle hysteresis (frame‑based): count frames when decelerating hard, decrement only when truly released
|
||||
if self.aego < -0.7:
|
||||
self.regen_paddle_timer += 1
|
||||
elif self.aego > -0.3:
|
||||
self.regen_paddle_timer = max(self.regen_paddle_timer - 1, 0)
|
||||
# else: hold timer between -0.7 and -0.3
|
||||
|
||||
# Base paddle press hysteresis
|
||||
self.regen_paddle_pressed = self.regen_paddle_timer >= 10 # 10 frames
|
||||
press_regen_paddle = self.regen_paddle_pressed or self.planner_regen_hold
|
||||
|
||||
|
||||
# Regen gain ratios from bin-averaged 60–0 deceleration sweep; Calculates stronger decel from paddle
|
||||
speed_mps = [0.559, 1.678, 2.797, 3.916, 5.035, 6.154, 7.273, 8.392, 9.511, 10.63,
|
||||
11.749, 12.868, 13.987, 15.106, 16.225, 17.344, 18.463, 19.582, 20.701, 21.820,
|
||||
22.939, 24.058, 25.177, 26.296]
|
||||
regen_gain_ratio = [
|
||||
1.000000, 1.057308, 1.131123, 1.220611, 1.270247, 1.300253, 1.339543, 1.361002,
|
||||
1.388410, 1.403253, 1.414721, 1.430949, 1.420289, 1.436787, 1.434116, 1.436805,
|
||||
1.417508, 1.402213, 1.395360, 1.360921, 1.342030, 1.292219, 1.270048, 1.239172
|
||||
]
|
||||
|
||||
gain = interp(car_velocity, speed_mps, regen_gain_ratio)
|
||||
pedaloffset = interp(car_velocity, [0., 3, 6, 30], [0.10, 0.175, 0.240, 0.240])
|
||||
|
||||
# Compute raw pedal gas
|
||||
raw_pedal_gas = clip((pedaloffset + (accel / gain) * 0.6), 0.0, 1.0) if press_regen_paddle else clip((pedaloffset + accel * 0.6), 0.0, 1.0)
|
||||
|
||||
# --- Immediate application of raw pedal gas, no blending ---
|
||||
pedal_gas = raw_pedal_gas
|
||||
# Safety cap: ramp from 22% at 0 m/s to 37.25% at 10 mph (4.47 m/s), then allow full throttle
|
||||
pedal_gas_max = interp(car_velocity, [0.0, 4.47, 4.48], [0.22, 0.3725, 1.0])
|
||||
pedal_gas = clip(pedal_gas, 0.0, pedal_gas_max)
|
||||
return pedal_gas, press_regen_paddle
|
||||
return pedal_gas
|
||||
|
||||
|
||||
def update(self, CC, CS, now_nanos, frogpilot_toggles):
|
||||
self.CS = CS
|
||||
self.aego = CS.out.aEgo
|
||||
actuators = CC.actuators
|
||||
accel = brake_accel = actuators.accel
|
||||
press_regen_paddle = False
|
||||
|
||||
# Planner-driven regen hold: gate by car support and OP long active, use commanded accel thresholds
|
||||
if (self.CP.enableGasInterceptor and self.CP.carFingerprint in CC_REGEN_PADDLE_CAR
|
||||
and self.CP.openpilotLongitudinalControl and CC.longActive):
|
||||
# Match original hysteresis intent: vehicle can usually stop without paddle up to ~1.0 m/s^2
|
||||
# Use the same thresholds as the aEgo-based hysteresis, but on commanded accel for preemption
|
||||
planner_press_threshold = -0.7
|
||||
planner_release_threshold = -0.3
|
||||
if accel <= planner_press_threshold:
|
||||
self.planner_regen_hold = True
|
||||
elif accel >= planner_release_threshold:
|
||||
self.planner_regen_hold = False
|
||||
else:
|
||||
self.planner_regen_hold = False
|
||||
|
||||
hud_control = CC.hudControl
|
||||
hud_alert = hud_control.visualAlert
|
||||
hud_v_cruise = hud_control.setSpeed
|
||||
@@ -169,138 +83,6 @@ class CarController(CarControllerBase):
|
||||
|
||||
# Send CAN commands.
|
||||
can_sends = []
|
||||
paddle_sends = []
|
||||
|
||||
raw_regen_active = (
|
||||
self.CP.carFingerprint in CC_REGEN_PADDLE_CAR and
|
||||
self.CP.openpilotLongitudinalControl and
|
||||
CC.longActive and
|
||||
self.CP.enableGasInterceptor and
|
||||
(self.regen_paddle_timer >= 10 or self.planner_regen_hold) # hysteresis or planner hint
|
||||
)
|
||||
regen_active = raw_regen_active
|
||||
|
||||
# === Spoof scheduling: midpoint + overflow (~target Hz) ===
|
||||
# Rising-edge reset on regen start
|
||||
if raw_regen_active and not self.last_regen_active:
|
||||
self.prev_steer_ts_ns = self.last_steer_ts_ns
|
||||
self.last_spoof_ts_ns = 0
|
||||
self.spoof_accum = 0.0
|
||||
self.spoof_mid_sent = False
|
||||
self.spoof_over_sent = False
|
||||
|
||||
if raw_regen_active:
|
||||
# Interval between last two bus-0 steer sends
|
||||
interval_ns = self.last_steer_ts_ns - self.prev_steer_ts_ns
|
||||
|
||||
# Adaptive two‑sided gap sized to the current steer interval, but capped to a target so the window stays wide enough
|
||||
gap_ns = (PADDLE_STEER_GAP_MIN_NS if interval_ns <= 0 else
|
||||
max(PADDLE_STEER_GAP_MIN_NS,
|
||||
min(PADDLE_STEER_GAP_MAX_NS,
|
||||
min((interval_ns // 2) - PADDLE_SLOT_EARLY_NS, PADDLE_GAP_TARGET_NS))))
|
||||
|
||||
# New steer interval? clear per-interval flags and add credits to reach target Hz
|
||||
if interval_ns != self.last_interval_ns:
|
||||
self.spoof_mid_sent = False
|
||||
self.spoof_over_sent = False
|
||||
self.last_interval_ns = interval_ns
|
||||
# Add credits once per new steer interval to reach the desired paddle rate
|
||||
if interval_ns > 0:
|
||||
steer_hz = 1e9 / float(interval_ns)
|
||||
extra_needed = max(0.0, (PADDLE_TARGET_HZ / steer_hz) - 1.0) # e.g., 42/33 − 1 ≈ 0.2727
|
||||
self.spoof_accum += extra_needed
|
||||
|
||||
# Midpoint spoof: one per interval
|
||||
if not self.spoof_mid_sent and interval_ns > 0:
|
||||
midpoint_ns = self.prev_steer_ts_ns + interval_ns // 2
|
||||
cloudlog.error("PADDLE MID: Δafter=%.1fms Δbefore=%.1fms credits=%.3f timer=%d",
|
||||
(now_nanos - self.last_steer_ts_ns) * 1e-6,
|
||||
(now_nanos - self.prev_steer_ts_ns) * 1e-6,
|
||||
self.spoof_accum,
|
||||
self.regen_paddle_timer)
|
||||
# Compute spacing to last and next steer (two-sided guard)
|
||||
next_steer_ts_ns = self.last_steer_ts_ns + interval_ns if interval_ns > 0 else 0
|
||||
delta_after_ns = now_nanos - self.last_steer_ts_ns
|
||||
delta_before_ns = (next_steer_ts_ns - now_nanos) if interval_ns > 0 else 1_000_000_000
|
||||
if (CS.out.vEgo > 2.68
|
||||
and now_nanos >= (midpoint_ns - PADDLE_SLOT_EARLY_NS)
|
||||
and delta_after_ns >= gap_ns
|
||||
and delta_before_ns >= gap_ns):
|
||||
# Non-blocking 1 ms spacing for paddle frames
|
||||
if now_nanos - self.last_paddle_ts_ns >= PADDLE_NONBLOCK_GAP_NS:
|
||||
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, True))
|
||||
paddle_sends.append(gmcan.create_regen_paddle_command(self.packer_pt, CanBus.POWERTRAIN, True))
|
||||
self.last_paddle_ts_ns = now_nanos
|
||||
self.last_spoof_ts_ns = now_nanos
|
||||
self.spoof_mid_sent = True
|
||||
|
||||
# Overflow spoof: insert extra when accumulator allows
|
||||
if self.spoof_accum >= OVERFLOW_THRESH and not self.spoof_over_sent and interval_ns > 0:
|
||||
slot2_ns = self.prev_steer_ts_ns + (interval_ns * 2) // 3
|
||||
cloudlog.error("PADDLE OFL: Δafter=%.1fms Δbefore=%.1fms credits=%.3f thresh=%.1f timer=%d",
|
||||
(now_nanos - self.last_steer_ts_ns) * 1e-6,
|
||||
(now_nanos - self.prev_steer_ts_ns) * 1e-6,
|
||||
self.spoof_accum,
|
||||
OVERFLOW_THRESH,
|
||||
self.regen_paddle_timer)
|
||||
# Two-sided spacing relative to steer
|
||||
next_steer_ts_ns = self.last_steer_ts_ns + interval_ns if interval_ns > 0 else 0
|
||||
delta_after_ns = now_nanos - self.last_steer_ts_ns
|
||||
delta_before_ns = (next_steer_ts_ns - now_nanos) if interval_ns > 0 else 1_000_000_000
|
||||
if (CS.out.vEgo > 2.68
|
||||
and now_nanos >= (slot2_ns - PADDLE_SLOT_EARLY_NS)
|
||||
and delta_after_ns >= gap_ns
|
||||
and delta_before_ns >= gap_ns):
|
||||
# Non-blocking 1 ms spacing for paddle frames
|
||||
if now_nanos - self.last_paddle_ts_ns >= PADDLE_NONBLOCK_GAP_NS:
|
||||
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, True))
|
||||
paddle_sends.append(gmcan.create_regen_paddle_command(self.packer_pt, CanBus.POWERTRAIN, True))
|
||||
self.last_paddle_ts_ns = now_nanos
|
||||
self.last_spoof_ts_ns = now_nanos
|
||||
self.spoof_over_sent = True
|
||||
self.spoof_accum -= OVERFLOW_THRESH
|
||||
# === End Spoof scheduling ===
|
||||
|
||||
# === Off-pulse scheduling on regen release ===
|
||||
if not raw_regen_active and self.last_regen_active:
|
||||
# schedule two off-slots at 1/3 and 2/3 of the last steer interval
|
||||
if self.prev_steer_ts_ns and self.last_steer_ts_ns:
|
||||
intv = self.last_steer_ts_ns - self.prev_steer_ts_ns
|
||||
self.off_schedule_ns = [
|
||||
self.prev_steer_ts_ns + intv // 3,
|
||||
self.prev_steer_ts_ns + (2 * intv) // 3
|
||||
]
|
||||
self.off_sent = [False, False]
|
||||
|
||||
if hasattr(self, "off_schedule_ns"):
|
||||
for i, t_ns in enumerate(self.off_schedule_ns):
|
||||
if not self.off_sent[i] and now_nanos >= (t_ns - PADDLE_SLOT_EARLY_NS):
|
||||
cloudlog.error("PADDLE OFF %d: Δafter=%.1fms Δto_slot=%.1fms timer=%d",
|
||||
i,
|
||||
(now_nanos - self.last_steer_ts_ns) * 1e-6,
|
||||
(now_nanos - t_ns) * 1e-6,
|
||||
self.regen_paddle_timer)
|
||||
# Two-sided spacing to steer before sending
|
||||
interval_ns = self.last_steer_ts_ns - self.prev_steer_ts_ns
|
||||
gap_ns = (PADDLE_STEER_GAP_MIN_NS if interval_ns <= 0 else
|
||||
max(PADDLE_STEER_GAP_MIN_NS,
|
||||
min(PADDLE_STEER_GAP_MAX_NS,
|
||||
min((interval_ns // 2) - PADDLE_SLOT_EARLY_NS, PADDLE_GAP_TARGET_NS))))
|
||||
next_steer_ts_ns = self.last_steer_ts_ns + interval_ns if interval_ns > 0 else 0
|
||||
delta_after_ns = now_nanos - self.last_steer_ts_ns
|
||||
delta_before_ns = (next_steer_ts_ns - now_nanos) if interval_ns > 0 else 1_000_000_000
|
||||
if (delta_after_ns >= gap_ns and delta_before_ns >= gap_ns):
|
||||
# Non-blocking 1 ms spacing for paddle frames
|
||||
if now_nanos - self.last_paddle_ts_ns >= PADDLE_NONBLOCK_GAP_NS:
|
||||
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, False))
|
||||
paddle_sends.append(gmcan.create_regen_paddle_command(self.packer_pt, CanBus.POWERTRAIN, False))
|
||||
self.last_paddle_ts_ns = now_nanos
|
||||
self.off_sent[i] = True
|
||||
# clean up once both off pulses are sent
|
||||
if hasattr(self, "off_sent") and all(self.off_sent):
|
||||
del self.off_schedule_ns
|
||||
del self.off_sent
|
||||
# === End off-pulse scheduling ===
|
||||
|
||||
# Steering (Active: 50Hz, inactive: 10Hz)
|
||||
steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP
|
||||
@@ -330,35 +112,24 @@ class CarController(CarControllerBase):
|
||||
else:
|
||||
apply_steer = 0
|
||||
|
||||
if (self.CP.flags & GMFlags.CC_LONG.value) and CC.enabled and not CS.out.cruiseState.enabled: # Send 0 so Panda doesn't error
|
||||
apply_steer = 0
|
||||
|
||||
# shift previous steer timestamp
|
||||
self.prev_steer_ts_ns = self.last_steer_ts_ns
|
||||
self.last_steer_ts_ns = now_nanos
|
||||
self.last_steer_frame = self.frame
|
||||
self.apply_steer_last = apply_steer
|
||||
idx = self.lka_steering_cmd_counter % 4
|
||||
can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, CC.latActive))
|
||||
|
||||
# Update regen_active state and last_regen_paddle_pressed for next loop
|
||||
self.last_regen_active = regen_active
|
||||
self.last_regen_paddle_pressed = self.regen_paddle_pressed or self.planner_regen_hold
|
||||
|
||||
if paddle_sends:
|
||||
interval_ns = self.last_steer_ts_ns - self.prev_steer_ts_ns
|
||||
flush_gap_ns = (PADDLE_STEER_GAP_MIN_NS if interval_ns <= 0 else
|
||||
max(PADDLE_STEER_GAP_MIN_NS,
|
||||
min(PADDLE_STEER_GAP_MAX_NS,
|
||||
min((interval_ns // 2) - PADDLE_SLOT_EARLY_NS, PADDLE_GAP_TARGET_NS))))
|
||||
if now_nanos - self.last_steer_ts_ns >= flush_gap_ns:
|
||||
can_sends.extend(paddle_sends)
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
# Gas/regen, brakes, and UI commands - all at 25Hz
|
||||
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
|
||||
@@ -370,33 +141,21 @@ class CarController(CarControllerBase):
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
self.apply_brake = int(min(-100 * frogpilot_toggles.stopAccel, self.params.MAX_BRAKE))
|
||||
else:
|
||||
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
|
||||
# Normal operation
|
||||
if self.CP.carFingerprint in EV_CAR:
|
||||
self.params.update_ev_gas_brake_threshold(CS.out.vEgo)
|
||||
self.apply_gas = int(round(interp(accel, self.params.EV_GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(interp(brake_accel, self.params.EV_BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
else:
|
||||
accel_due_to_pitch = 0.0
|
||||
|
||||
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)))
|
||||
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
|
||||
|
||||
self.apply_gas = int(round(interp(accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
# Don't allow any gas above inactive regen while stopping
|
||||
# FIXME: brakes aren't applied immediately when enabling at a stop
|
||||
if stopping:
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
if self.CP.carFingerprint in CC_ONLY_CAR:
|
||||
# gas interceptor only used for full long control on cars without ACC
|
||||
interceptor_gas_cmd, press_regen_paddle = self.calc_pedal_command(actuators.accel, CC.longActive, CS.out.vEgo)
|
||||
interceptor_gas_cmd = self.calc_pedal_command(actuators.accel, CC.longActive)
|
||||
|
||||
if self.CP.enableGasInterceptor and self.apply_gas > self.params.INACTIVE_REGEN and CS.out.cruiseState.standstill:
|
||||
# "Tap" the accelerator pedal to re-engage ACC
|
||||
@@ -409,10 +168,7 @@ class CarController(CarControllerBase):
|
||||
if self.CP.flags & GMFlags.CC_LONG.value:
|
||||
if CC.longActive and CS.out.cruiseState.enabled and CS.out.vEgo > self.CP.minEnableSpeed:
|
||||
# Using extend instead of append since the message is only sent intermittently
|
||||
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, frogpilot_toggles))
|
||||
elif (CS.out.cruiseState.enabled and CC.enabled and self.frame % 52 == 0 and
|
||||
CS.cruise_buttons == CruiseButtons.UNPRESS and CS.out.gasPressed and CS.out.cruiseState.speed < CS.out.vEgo < hud_v_cruise):
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
|
||||
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators))
|
||||
if self.CP.enableGasInterceptor:
|
||||
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
@@ -435,7 +191,7 @@ class CarController(CarControllerBase):
|
||||
# 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))
|
||||
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))
|
||||
idx, CC.enabled, near_stop, at_full_stop, self.CP))
|
||||
|
||||
# Send dashboard UI commands (ACC status)
|
||||
send_fcw = hud_alert == VisualAlert.fcw
|
||||
@@ -446,18 +202,22 @@ class CarController(CarControllerBase):
|
||||
accel += self.accel_g
|
||||
|
||||
# Radar needs to know current speed and yaw rate (50hz),
|
||||
# and that ADAS is alive (5hz, previously 10hz)
|
||||
# and that ADAS is alive (10hz)
|
||||
if not self.CP.radarUnavailable:
|
||||
tt = self.frame * DT_CTRL
|
||||
time_and_headlights_step = 20
|
||||
time_and_headlights_step = 10
|
||||
if self.frame % time_and_headlights_step == 0:
|
||||
idx = (self.frame // time_and_headlights_step) % 4
|
||||
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
|
||||
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
|
||||
|
||||
speed_and_accelerometer_step = 2
|
||||
if self.frame % speed_and_accelerometer_step == 0:
|
||||
idx = (self.frame // speed_and_accelerometer_step) % 4
|
||||
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
|
||||
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
|
||||
|
||||
if self.CP.networkLocation == NetworkLocation.gateway and self.frame % (self.params.ADAS_KEEPALIVE_STEP * 2) == 0:
|
||||
if self.CP.networkLocation == NetworkLocation.gateway and self.frame % self.params.ADAS_KEEPALIVE_STEP == 0:
|
||||
can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN)
|
||||
|
||||
# TODO: integrate this with the code block below?
|
||||
@@ -485,7 +245,7 @@ class CarController(CarControllerBase):
|
||||
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
# Silence "Take Steering" alert sent by camera, forward PSCMStatus with HandsOffSWlDetectionStatus=1
|
||||
if self.frame % 20 == 0:
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status))
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
|
||||
@@ -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, CAR
|
||||
from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD, GMFlags, CC_ONLY_CAR, CAMERA_ACC_CAR, SDGM_CAR
|
||||
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
NetworkLocation = car.CarParams.NetworkLocation
|
||||
@@ -53,13 +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
|
||||
|
||||
# Track timestamps for OEM PRNDL2 and Regen Paddle messages (used to sync spoofing timing)
|
||||
self.prndl2_ts_nanos = pt_cp.ts_nanos["ECMPRDNL2"]["PRNDL2"]
|
||||
if self.CP.carFingerprint in CC_REGEN_PADDLE_CAR:
|
||||
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"]
|
||||
@@ -78,7 +71,10 @@ class CarState(CarStateBase):
|
||||
# sample rear wheel speeds, standstill=True if ECM allows engagement with brake
|
||||
ret.standstill = ret.wheelSpeeds.rl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
|
||||
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["ECMPRDNL2"]["PRNDL2"], None))
|
||||
if pt_cp.vl["ECMPRDNL2"]["ManualMode"] == 1:
|
||||
ret.gearShifter = self.parse_gear_shifter("T")
|
||||
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:
|
||||
ret.brake = pt_cp.vl["EBCMBrakePedalPosition"]["BrakePedalPosition"] / 0xd0
|
||||
@@ -96,11 +92,11 @@ 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) or (self.CP.carFingerprint in [CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_BOLT_CC] and self.CP.enableGasInterceptor)
|
||||
self.single_pedal_mode = ret.gearShifter == GearShifter.low or pt_cp.vl["EVDriveMode"]["SinglePedalModeActive"] == 1 or (ret.regenBraking and GearShifter.manumatic)
|
||||
|
||||
if self.CP.enableGasInterceptor:
|
||||
ret.gas = (pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2.
|
||||
threshold = 23 if self.CP.carFingerprint in CAMERA_ACC_CAR else 4 # Panda 595 threshold = 23.65. Set lower to avoid panda blocking messages and GasInterceptor faulting.
|
||||
threshold = 10 if self.CP.carFingerprint in CAMERA_ACC_CAR else 4 # Panda 515 threshold = 10.88. Set lower to avoid panda blocking messages and GasInterceptor faulting.
|
||||
ret.gasPressed = ret.gas > threshold
|
||||
else:
|
||||
ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254.
|
||||
@@ -163,11 +159,7 @@ 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
|
||||
# Try ECM first for cars that might have it (like most GMs), fall back to ASCM
|
||||
try:
|
||||
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
|
||||
except:
|
||||
ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0
|
||||
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
|
||||
|
||||
if self.CP.enableBsm:
|
||||
if self.CP.carFingerprint not in SDGM_CAR:
|
||||
@@ -210,7 +202,7 @@ class CarState(CarStateBase):
|
||||
messages += [
|
||||
("AEBCmd", 10),
|
||||
]
|
||||
# Include ASCMActiveCruiseControlStatus for all non-SDGM fwdCamera cars
|
||||
if CP.carFingerprint not in CC_ONLY_CAR:
|
||||
messages += [
|
||||
("ASCMActiveCruiseControlStatus", 25),
|
||||
]
|
||||
@@ -238,7 +230,7 @@ class CarState(CarStateBase):
|
||||
]
|
||||
else:
|
||||
messages += [
|
||||
("ECMPRDNL2", 40),
|
||||
("ECMPRDNL2", 10),
|
||||
("AcceleratorPedal2", 33),
|
||||
("ECMEngineStatus", 100),
|
||||
("BCMTurnSignals", 1),
|
||||
@@ -260,7 +252,7 @@ class CarState(CarStateBase):
|
||||
|
||||
if CP.transmissionType == TransmissionType.direct:
|
||||
messages += [
|
||||
("EBCMRegenPaddle", 40),
|
||||
("EBCMRegenPaddle", 50),
|
||||
("EVDriveMode", 0),
|
||||
]
|
||||
|
||||
|
||||
+25
-48
@@ -66,6 +66,7 @@ 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]
|
||||
@@ -176,69 +177,45 @@ def create_lka_icon_command(bus, active, critical, steer):
|
||||
dat = b"\x00\x00\x00"
|
||||
return make_can_msg(0x104c006c, dat, bus)
|
||||
|
||||
def create_regen_paddle_command(packer, bus, press_regen_paddle):
|
||||
regen_paddle_value = 2 if press_regen_paddle else 0
|
||||
values = {
|
||||
"RegenPaddle": regen_paddle_value,
|
||||
"Byte1": 0,
|
||||
"Byte2": 0,
|
||||
"Byte3": 0,
|
||||
"Byte4": 0,
|
||||
"Byte5": 0,
|
||||
"Byte6": 0
|
||||
}
|
||||
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
|
||||
RATE_UP_MAX = 0.04
|
||||
RATE_DOWN_MAX = 0.04
|
||||
else:
|
||||
_CV = CV.MS_TO_MPH
|
||||
RATE_UP_MAX = 0.2
|
||||
RATE_DOWN_MAX = 0.2
|
||||
|
||||
def create_gm_cc_spam_command(packer, controller, CS, actuators, frogpilot_toggles):
|
||||
accel = actuators.accel
|
||||
Vego = CS.out.vEgo
|
||||
accel = actuators.accel * _CV # m/s/s to mph/s
|
||||
speedSetPoint = int(round(CS.out.cruiseState.speed * _CV))
|
||||
|
||||
cruiseBtn = CruiseButtons.INIT
|
||||
if abs(accel) <= 0.15:
|
||||
rate = 1
|
||||
else:
|
||||
rate = 0.2
|
||||
|
||||
MS_CONVERT = CV.MS_TO_KPH if frogpilot_toggles.is_metric else CV.MS_TO_MPH
|
||||
|
||||
speedSetPoint = int(round(CS.out.cruiseState.speed * MS_CONVERT))
|
||||
|
||||
if accel > 0:
|
||||
DesiredSetPoint = int(round((Vego * 1.01 + 3 * accel) * MS_CONVERT)) # 1.01 factor to match cluster speed better
|
||||
else: # accel <= 0
|
||||
DesiredSetPoint = int(round((Vego * 1.01 + 3 * accel) * MS_CONVERT))
|
||||
|
||||
if CS.CP.minEnableSpeed - (DesiredSetPoint / MS_CONVERT) > 3.25:
|
||||
if speedSetPoint == CS.CP.minEnableSpeed and accel < -1:
|
||||
cruiseBtn = CruiseButtons.CANCEL
|
||||
controller.apply_speed = 0
|
||||
elif DesiredSetPoint < speedSetPoint and speedSetPoint > CS.CP.minEnableSpeed * MS_CONVERT + 1:
|
||||
rate = 0.04
|
||||
elif accel < 0:
|
||||
cruiseBtn = CruiseButtons.DECEL_SET
|
||||
if speedSetPoint > (CS.out.vEgo * _CV) + 3.0: # If accel is changing directions, bring set speed to current speed as fast as possible
|
||||
rate = RATE_DOWN_MAX
|
||||
else:
|
||||
rate = max(-1 / accel, RATE_DOWN_MAX)
|
||||
controller.apply_speed = speedSetPoint - 1
|
||||
elif DesiredSetPoint > speedSetPoint:
|
||||
elif accel > 0:
|
||||
cruiseBtn = CruiseButtons.RES_ACCEL
|
||||
if speedSetPoint < (CS.out.vEgo * _CV) - 3.0:
|
||||
rate = RATE_UP_MAX
|
||||
else:
|
||||
rate = max(1 / accel, RATE_UP_MAX)
|
||||
controller.apply_speed = speedSetPoint + 1
|
||||
else:
|
||||
cruiseBtn = CruiseButtons.INIT
|
||||
controller.apply_speed = speedSetPoint
|
||||
rate = float('inf')
|
||||
|
||||
# Check rlogs closely - our message shouldn't show up on the pt bus for us
|
||||
# Or bus 2, since we're forwarding... but I think it does
|
||||
# TODO: Cleanup the timing - normal is every 30ms...
|
||||
if (cruiseBtn != CruiseButtons.INIT) and ((controller.frame - controller.last_button_frame) * DT_CTRL > rate):
|
||||
controller.last_button_frame = controller.frame
|
||||
idx = (CS.buttons_counter + 1) % 4 # Need to predict the next idx for '22-23 EUV
|
||||
|
||||
@@ -27,22 +27,10 @@ CAM_MSG = 0x320 # AEBCmd
|
||||
ACCELERATOR_POS_MSG = 0xbe
|
||||
|
||||
NON_LINEAR_TORQUE_PARAMS = {
|
||||
CAR.CHEVROLET_BOLT_EUV: {
|
||||
"left": [2.6531724862969748, 1.1, 0.1719764879840985, 0.0],
|
||||
"right": [2.8531724862969748, 1.0, 0.1469764879840985, 0.0],
|
||||
},
|
||||
CAR.CHEVROLET_BOLT_CC: {
|
||||
"left": [2.6531724862969748, 1.1, 0.1719764879840985, 0.0],
|
||||
"right": [2.8531724862969748, 1.0, 0.1469764879840985, 0.0],
|
||||
},
|
||||
CAR.GMC_ACADIA: {
|
||||
"left": [4.78003305, 1.0, 0.3122, 0.05591772],
|
||||
"right": [4.78003305, 1.0, 0.3122, 0.05591772],
|
||||
},
|
||||
CAR.CHEVROLET_SILVERADO: {
|
||||
"left": [3.8, 0.81, 0.24, 0.0465122],
|
||||
"right": [3.8, 0.81, 0.24, 0.0465122],
|
||||
},
|
||||
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]
|
||||
}
|
||||
|
||||
|
||||
@@ -76,12 +64,10 @@ class CarInterface(CarInterfaceBase):
|
||||
# This has big effect on the stability about 0 (noise when going straight)
|
||||
non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint)
|
||||
assert non_linear_torque_params, "The params are not defined"
|
||||
# Left is positive
|
||||
side_key = "left" if lateral_acceleration >= 0 else "right"
|
||||
a, b, c, d = non_linear_torque_params[side_key]
|
||||
a, b, c, _ = non_linear_torque_params
|
||||
sig_input = a * lateral_acceleration
|
||||
sig = np.sign(sig_input) * (1 / (1 + exp(-fabs(sig_input))) - 0.5)
|
||||
steer_torque = (sig * b) + (lateral_acceleration * c) + d
|
||||
steer_torque = (sig * b) + (lateral_acceleration * c)
|
||||
return float(steer_torque)
|
||||
|
||||
lataccel_values = np.arange(-5.0, 5.0, 0.01)
|
||||
@@ -131,37 +117,31 @@ class CarInterface(CarInterfaceBase):
|
||||
if PEDAL_MSG in fingerprint[0]:
|
||||
ret.enableGasInterceptor = True
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR
|
||||
# When a pedal interceptor is present, always use normal longitudinal (block stock cruise)
|
||||
experimental_long = False
|
||||
|
||||
if candidate in EV_CAR:
|
||||
ret.transmissionType = TransmissionType.direct
|
||||
else:
|
||||
ret.transmissionType = TransmissionType.automatic
|
||||
|
||||
ret.longitudinalTuning.kiBP = [5., 35., 60.]
|
||||
ret.longitudinalTuning.kiBP = [5., 35.]
|
||||
|
||||
if candidate in CAMERA_ACC_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.experimentalLongitudinalAvailable = candidate not in CC_ONLY_CAR
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.radarUnavailable = True # no radar
|
||||
# Only use pcmCruise if no pedal interceptor (bolt_cc style behavior)
|
||||
ret.pcmCruise = not ret.enableGasInterceptor
|
||||
ret.pcmCruise = True
|
||||
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
|
||||
|
||||
# Tuning for experimental long
|
||||
ret.longitudinalTuning.kiV = [0.5, 0.5, 0.5]
|
||||
ret.longitudinalTuning.kiV = [2.0, 1.5]
|
||||
ret.vEgoStopping = 0.1
|
||||
ret.vEgoStarting = 0.1
|
||||
|
||||
ret.stoppingDecelRate = 1.0 # reach brake quickly after enabling
|
||||
ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.vEgoStarting = 0.25
|
||||
ret.stopAccel = -0.25
|
||||
|
||||
if ret.experimentalLongitudinalAvailable and experimental_long:
|
||||
ret.pcmCruise = False
|
||||
@@ -169,7 +149,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
|
||||
elif candidate in SDGM_CAR:
|
||||
ret.longitudinalTuning.kiV = [0., 0., 0.] # TODO: tuning
|
||||
ret.longitudinalTuning.kiV = [0., 0.] # TODO: tuning
|
||||
ret.experimentalLongitudinalAvailable = False
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.pcmCruise = True
|
||||
@@ -188,7 +168,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
|
||||
|
||||
# Tuning
|
||||
ret.longitudinalTuning.kiV = [0.5, 0.5, 0.5]
|
||||
ret.longitudinalTuning.kiV = [2.4, 1.5]
|
||||
|
||||
if ret.enableGasInterceptor:
|
||||
# Need to set ASCM long limits when using pedal interceptor, instead of camera ACC long limits
|
||||
@@ -242,19 +222,11 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
# Bolt-only lateral tuning overrides
|
||||
ret.lateralTuning.torque.kp = 1.03
|
||||
ret.lateralTuning.torque.ki = 1.07
|
||||
ret.lateralTuning.torque.kd = 0.93
|
||||
ret.lateralTuning.torque.kfDEPRECATED = 0.02
|
||||
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
|
||||
|
||||
if candidate == CAR.CHEVROLET_SILVERADO:
|
||||
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
|
||||
# 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
|
||||
@@ -314,13 +286,13 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.stoppingControl = True
|
||||
ret.autoResumeSng = True
|
||||
|
||||
if candidate in CC_ONLY_CAR or (candidate in CAMERA_ACC_CAR and ret.enableGasInterceptor): #pedal interceptor tuning
|
||||
if candidate in CC_ONLY_CAR:
|
||||
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
|
||||
ret.longitudinalTuning.kiBP = [0., 3., 6., 35.]
|
||||
ret.longitudinalTuning.kiV = [0.125, 0.175, 0.225, 0.33]
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.25
|
||||
ret.longitudinalTuning.kiBP = [0.0, 5., 35.]
|
||||
ret.longitudinalTuning.kiV = [0.0, 0.35, 0.5]
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.15
|
||||
ret.stoppingDecelRate = 0.8
|
||||
else: # Pedal used for SNG, ACC for longitudinal control otherwise
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
@@ -337,15 +309,16 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
|
||||
ret.pcmCruise = False
|
||||
|
||||
if not ret.enableGasInterceptor and candidate in CC_ONLY_CAR: #redneck tuning
|
||||
ret.longitudinalTuning.kpBP = [10.7, 10.8, 28.] # 10.7 m/s == 24 mph
|
||||
ret.longitudinalTuning.kpV = [0., 5., 2.] # set lower end to 0 since we can't drive below that speed
|
||||
ret.longitudinalTuning.deadzoneBP = [0., 1.]
|
||||
ret.longitudinalTuning.deadzoneV = [0.9, 0.9] # == 2 km/h/s, 1.25 mph/s
|
||||
ret.longitudinalActuatorDelay = 1. # TODO: measure this
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
ret.longitudinalTuning.kiV = [0.1]
|
||||
ret.stoppingDecelRate = 11.18 # == 25 mph/s (.04 rate)
|
||||
ret.stoppingDecelRate = 11.18 # == 25 mph/s (.04 rate)
|
||||
|
||||
ret.longitudinalTuning.deadzoneBP = [0.]
|
||||
ret.longitudinalTuning.deadzoneV = [0.56] # == 2 km/h/s, 1.25 mph/s
|
||||
ret.longitudinalActuatorDelay = 1. # TODO: measure this
|
||||
|
||||
ret.longitudinalTuning.kpBP = [10.7, 10.8, 28.] # 10.7 m/s == 24 mph
|
||||
ret.longitudinalTuning.kpV = [0., 20., 20.] # set lower end to 0 since we can't drive below that speed
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
ret.longitudinalTuning.kiV = [0.1]
|
||||
|
||||
if candidate in CC_ONLY_CAR:
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_ACC
|
||||
|
||||
+14
-28
@@ -33,53 +33,41 @@ class CarControllerParams:
|
||||
# Our controller should still keep the 2 second average above
|
||||
# -3.5 m/s^2 as per planner limits
|
||||
ACCEL_MAX = 2. # m/s^2
|
||||
ACCEL_MAX_PLUS = 4. # m/s^2
|
||||
ACCEL_MIN = -4. # m/s^2
|
||||
|
||||
def __init__(self, CP):
|
||||
# Gas/brake lookups
|
||||
self.ZERO_GAS = 6150 # Coasting
|
||||
self.ZERO_GAS = 2048 # 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:
|
||||
self.MAX_GAS = 8848
|
||||
self.MAX_GAS_PLUS = 8848
|
||||
self.MAX_ACC_REGEN = 5610
|
||||
self.INACTIVE_REGEN = 5650
|
||||
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.
|
||||
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 = 8191
|
||||
self.MAX_GAS_PLUS = 8191
|
||||
self.MAX_ACC_REGEN = 5500
|
||||
self.INACTIVE_REGEN = 5500
|
||||
self.MAX_GAS = 3400
|
||||
self.MAX_ACC_REGEN = 1514
|
||||
self.INACTIVE_REGEN = 1554
|
||||
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 = 7168 # 8292 uses new bit, possible but not tested. Matches Twilsonco tw-main max
|
||||
self.MAX_ACC_REGEN = 5500 # Max ACC regen is slightly less than max paddle regen
|
||||
self.INACTIVE_REGEN = 5500
|
||||
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 = -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
|
||||
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_BP_PLUS = [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, 0.]
|
||||
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, max_regen_acceleration]
|
||||
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
|
||||
@@ -88,11 +76,10 @@ class CarControllerParams:
|
||||
|
||||
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):
|
||||
package: str = "Adaptive Cruise Control (ACC)"
|
||||
@@ -175,7 +162,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=2994, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0),
|
||||
GMCarSpecs(mass=2450, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0),
|
||||
)
|
||||
CHEVROLET_EQUINOX = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Equinox 2019-22")],
|
||||
@@ -331,7 +318,6 @@ 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_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}
|
||||
|
||||
@@ -286,11 +286,11 @@ class CarController(CarControllerBase):
|
||||
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.speed = self.speed
|
||||
new_actuators.accel = self.accel
|
||||
new_actuators.gas = self.gas
|
||||
new_actuators.brake = self.brake
|
||||
new_actuators.steer = self.last_steer
|
||||
new_actuators.speed = float(self.speed)
|
||||
new_actuators.accel = float(self.accel)
|
||||
new_actuators.gas = float(self.gas)
|
||||
new_actuators.brake = float(self.brake)
|
||||
new_actuators.steer = float(self.last_steer)
|
||||
new_actuators.steerOutputCan = apply_steer
|
||||
|
||||
self.frame += 1
|
||||
|
||||
@@ -76,14 +76,14 @@ def create_brake_command(packer, CAN, apply_brake, pump_on, pcm_override, pcm_ca
|
||||
return packer.make_can_msg("BRAKE_COMMAND", CAN.pt, values)
|
||||
|
||||
|
||||
def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint):
|
||||
def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint, gas_force):
|
||||
commands = []
|
||||
min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0]
|
||||
|
||||
control_on = 5 if enabled else 0
|
||||
gas_command = gas if active and accel > min_gas_accel else -30000
|
||||
gas_command = gas if active and gas_force > min_gas_accel else -30000
|
||||
accel_command = accel if active else 0
|
||||
braking = 1 if active and accel < min_gas_accel else 0
|
||||
braking = 1 if active and gas_force < min_gas_accel else 0
|
||||
standstill = 1 if active and stopping_counter > 0 else 0
|
||||
standstill_release = 1 if active and stopping_counter == 0 else 0
|
||||
|
||||
|
||||
@@ -85,10 +85,10 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.longitudinalActuatorDelay = 0.5 # s
|
||||
if candidate in HONDA_BOSCH_RADARLESS:
|
||||
ret.stopAccel = CarControllerParams.BOSCH_ACCEL_MIN # stock uses -4.0 m/s^2 once stopped but limited by safety model
|
||||
else:
|
||||
# default longitudinal tuning for all hondas
|
||||
ret.longitudinalTuning.kiBP = [0., 5., 35.]
|
||||
ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5]
|
||||
|
||||
# default longitudinal tuning for all hondas
|
||||
ret.longitudinalTuning.kiBP = [0., 5., 35.]
|
||||
ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5]
|
||||
|
||||
eps_modified = False
|
||||
for fw in car_fw:
|
||||
@@ -117,6 +117,9 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
|
||||
if candidate == CAR.HONDA_CIVIC_BOSCH:
|
||||
CarControllerParams.BOSCH_GAS_LOOKUP_V = [0, 750]
|
||||
|
||||
elif candidate == CAR.HONDA_ACCORD:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
|
||||
|
||||
@@ -37,12 +37,12 @@ EventName = car.CarEvent.EventName
|
||||
MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS
|
||||
ACCEL_MAX = 2.0
|
||||
ACCEL_MIN = -3.5
|
||||
FRICTION_THRESHOLD = 0.12
|
||||
FRICTION_THRESHOLD = 0.09
|
||||
|
||||
def get_friction_threshold(v_ego):
|
||||
# Interpolate friction threshold
|
||||
# Interpolate friction threshold from 0.09 at 50 mph to 0.15 at 75 mph
|
||||
from openpilot.common.numpy_fast import interp
|
||||
return interp(v_ego, [1 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.12, 0.3])
|
||||
return interp(v_ego, [1 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.12, 0.25])
|
||||
|
||||
TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.toml')
|
||||
TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.toml')
|
||||
|
||||
@@ -43,9 +43,9 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"CADILLAC_ESCALADE" = [1.899999976158142, 1.842270016670227, 0.1120000034570694]
|
||||
"CADILLAC_ESCALADE_ESV_2019" = [1.15, 1.3, 0.2]
|
||||
"CADILLAC_XT4" = [1.45, 1.6, 0.2]
|
||||
"CHEVROLET_BOLT_EUV" = [2.0, 2.0, 0.09]
|
||||
"CHEVROLET_BOLT_EUV" = [2.0, 2.0, 0.05]
|
||||
"CHEVROLET_MALIBU_CC" = [1.85, 1.85, 0.075]
|
||||
"CHEVROLET_SILVERADO" = [2.014, 1.9, 0.125]
|
||||
"CHEVROLET_SILVERADO" = [1.9, 1.9, 0.112]
|
||||
"CHEVROLET_TRAILBLAZER" = [1.33, 1.9, 0.16]
|
||||
"CHEVROLET_TRAVERSE" = [1.33, 1.33, 0.18]
|
||||
"CHEVROLET_EQUINOX" = [2.5, 2.5, 0.05]
|
||||
|
||||
@@ -44,7 +44,7 @@ CAMERA_OFFSET = 0.04
|
||||
REPLAY = "REPLAY" in os.environ
|
||||
SIMULATION = "SIMULATION" in os.environ
|
||||
TESTING_CLOSET = "TESTING_CLOSET" in os.environ
|
||||
IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd", "micd", "soundd"}
|
||||
IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd"}
|
||||
|
||||
ThermalStatus = log.DeviceState.ThermalStatus
|
||||
State = log.ControlsState.OpenpilotState
|
||||
@@ -117,6 +117,9 @@ class Controls:
|
||||
self.is_metric = self.params.get_bool("IsMetric")
|
||||
self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled")
|
||||
|
||||
# detect sound card presence and ensure successful init
|
||||
sounds_available = HARDWARE.get_sound_card_online()
|
||||
|
||||
car_recognized = self.CP.carName != 'mock'
|
||||
|
||||
# cleanup old params
|
||||
@@ -170,6 +173,8 @@ class Controls:
|
||||
else:
|
||||
self.startup_event = get_startup_event(car_recognized, not self.CP.passive, len(self.CP.carFw) > 0)
|
||||
|
||||
if not sounds_available:
|
||||
self.events.add(EventName.soundsUnavailable, static=True)
|
||||
if not car_recognized:
|
||||
self.events.add(EventName.carUnrecognized, static=True)
|
||||
if len(self.CP.carFw) > 0:
|
||||
|
||||
@@ -779,6 +779,11 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
||||
Priority.LOWER, VisualAlert.none, AudibleAlert.none, .2, creation_delay=600.)
|
||||
},
|
||||
|
||||
EventName.soundsUnavailable: {
|
||||
ET.PERMANENT: NormalPermanentAlert("Speaker not found", "Reboot your Device"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Speaker not found"),
|
||||
},
|
||||
|
||||
EventName.tooDistracted: {
|
||||
ET.NO_ENTRY: NoEntryAlert("Distraction Level Too High"),
|
||||
},
|
||||
@@ -977,7 +982,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.full,
|
||||
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5),
|
||||
ET.USER_DISABLE: ImmediateDisableAlert("Wrong Gear"),
|
||||
ET.USER_DISABLE: ImmediateDisableAlert("Reverse Gear"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Reverse Gear"),
|
||||
},
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD, get_friction_
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED, get_friction
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
|
||||
from openpilot.selfdrive.car.gm.values import CAR as GM_CAR
|
||||
from openpilot.selfdrive.controls.lib.pid import PIDController
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
|
||||
@@ -22,8 +21,8 @@ from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_G
|
||||
# Additionally, there is friction in the steering wheel that needs
|
||||
# to be overcome to move it at all, this is compensated for too.
|
||||
|
||||
KP = 0.7
|
||||
KI = 0.35
|
||||
KP = 0.6
|
||||
KI = 0.3
|
||||
|
||||
INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30]
|
||||
KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP]
|
||||
@@ -37,12 +36,6 @@ JERK_LOOKAHEAD_SECONDS = 0.19
|
||||
JERK_GAIN = 0.22
|
||||
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
|
||||
VERSION = 2
|
||||
DEBUG_TORQUE_TUNE = False
|
||||
FF_SCALE_BLEND_LAT_ACCEL = 0.05
|
||||
DEADZONE_BOOST_LAT_ACCEL = 0.08
|
||||
UNWIND_D_DES_THRESHOLD = -1.0
|
||||
UNWIND_LAT_ACCEL_NEAR_ZERO = 0.3
|
||||
BOLT_CARS = (GM_CAR.CHEVROLET_BOLT_EUV, GM_CAR.CHEVROLET_BOLT_CC)
|
||||
|
||||
class LatControlTorque(LatControl):
|
||||
def __init__(self, CP, CI, dt):
|
||||
@@ -62,21 +55,6 @@ class LatControlTorque(LatControl):
|
||||
self.previous_measurement = 0.0
|
||||
self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * (MAX_LAT_JERK_UP - 0.5)), self.dt)
|
||||
self.low_speed_reset_threshold = max(CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED)
|
||||
self.debug_counter = 0
|
||||
self.prev_desired_lateral_accel = 0.0
|
||||
|
||||
self.is_bolt = CP.carFingerprint in BOLT_CARS
|
||||
self.torque_ff_scale_pos = 1.0
|
||||
self.torque_ff_scale_neg = 1.0
|
||||
self.torque_deadzone_boost_neg = 0.0
|
||||
self.torque_ki_mult = 1.0
|
||||
if self.is_bolt:
|
||||
self.torque_ff_scale_pos = float(self.torque_params.kp)
|
||||
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:
|
||||
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):
|
||||
self.torque_params.latAccelFactor = latAccelFactor
|
||||
@@ -98,7 +76,6 @@ class LatControlTorque(LatControl):
|
||||
self.previous_measurement = 0.0
|
||||
self.measurement_rate_filter.x = 0.0
|
||||
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
|
||||
self.prev_desired_lateral_accel = 0.0
|
||||
else:
|
||||
if self.prev_steering_pressed and not CS.steeringPressed:
|
||||
self.pid.i *= self.steer_release_i_decay
|
||||
@@ -117,10 +94,6 @@ class LatControlTorque(LatControl):
|
||||
desired_lateral_jerk = np.clip(self.jerk_filter.update(raw_lateral_jerk), -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
|
||||
gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
|
||||
setpoint = expected_lateral_accel + desired_lateral_jerk * lat_delay
|
||||
desired_lateral_accel_rate = (setpoint - self.prev_desired_lateral_accel) / self.dt
|
||||
unwind_detected = (desired_lateral_accel_rate < UNWIND_D_DES_THRESHOLD and
|
||||
abs(setpoint) < UNWIND_LAT_ACCEL_NEAR_ZERO)
|
||||
self.prev_desired_lateral_accel = setpoint
|
||||
|
||||
measurement = measured_curvature * CS.vEgo ** 2
|
||||
measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt)
|
||||
@@ -137,25 +110,11 @@ class LatControlTorque(LatControl):
|
||||
ff = gravity_adjusted_future_lateral_accel
|
||||
# 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:
|
||||
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
|
||||
friction = get_friction(error_with_lsf + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone,
|
||||
get_friction_threshold(CS.vEgo), self.torque_params)
|
||||
ff += friction
|
||||
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 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
|
||||
deadzone_boost_active = True
|
||||
ff += get_friction(error_with_lsf + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, get_friction_threshold(CS.vEgo), self.torque_params)
|
||||
|
||||
if CS.vEgo < self.low_speed_reset_threshold:
|
||||
self.pid.reset()
|
||||
freeze_integrator = (steer_limited_by_safety or CS.steeringPressed or
|
||||
CS.vEgo < self.low_speed_reset_threshold or unwind_detected)
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < self.low_speed_reset_threshold
|
||||
output_lataccel = self.pid.update(pid_log.error, error_rate=-measurement_rate, speed=CS.vEgo, feedforward=ff, freeze_integrator=freeze_integrator)
|
||||
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
|
||||
|
||||
@@ -170,12 +129,6 @@ class LatControlTorque(LatControl):
|
||||
pid_log.desiredLateralJerk = float(desired_lateral_jerk)
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
|
||||
|
||||
if DEBUG_TORQUE_TUNE and self.is_bolt:
|
||||
self.debug_counter += 1
|
||||
if self.debug_counter % 50 == 0:
|
||||
print(f"bolt_torque ff_scale={ff_scale:.3f} pos={self.torque_ff_scale_pos:.3f} "
|
||||
f"neg={self.torque_ff_scale_neg:.3f} deadzone_boost_active={deadzone_boost_active}")
|
||||
|
||||
self.prev_steering_pressed = CS.steeringPressed
|
||||
|
||||
# TODO left is positive in this convention
|
||||
|
||||
@@ -17,9 +17,9 @@ from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_UNSET, CONTR
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
LON_MPC_STEP = 0.2 # first step is 0.2s
|
||||
A_CRUISE_MIN = -1.0
|
||||
A_CRUISE_MAX_BP = [0.0, 5., 10., 15., 20., 25., 40.]
|
||||
A_CRUISE_MAX_VALS = [1.125, 1.125, 1.125, 1.125, 1.25, 1.25, 1.5]
|
||||
A_CRUISE_MIN = -1.2
|
||||
A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6]
|
||||
A_CRUISE_MAX_BP = [0., 10.0, 25., 40.]
|
||||
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
|
||||
ALLOW_THROTTLE_THRESHOLD = 0.5
|
||||
MIN_ALLOW_THROTTLE_SPEED = 2.5
|
||||
|
||||
Reference in New Issue
Block a user