Compare commits

..

103 Commits

Author SHA1 Message Date
firestar5683 b78cb017e6 User adjustable offsets 2026-02-07 23:33:48 -06:00
firestar5683 ecaae3f59b Integrator Smooth On Handoff 2026-02-06 15:48:27 -06:00
firestar5683 8e66bbf953 Lights 2026-02-05 22:40:53 -06:00
firestar5683 81924bcca1 New Models 2026-02-05 15:08:02 -06:00
firestar5683 e849c4ceeb Revert "merry christmas"
This reverts commit 8a9d0e5e6a.
2026-02-05 13:34:13 -06:00
firestar5683 12088b9e53 Increase Fault Resilience 2026-02-05 13:24:01 -06:00
firestarsdog fff9ef7a8e Stats 2026-02-02 01:07:28 -05:00
firestarsdog 433ef324e1 Stats 2026-02-01 22:46:33 -06:00
firestar5683 86d0954cfb friction threshold 2026-01-29 11:28:42 -06:00
firestar5683 63acba85dd tune 2026-01-29 11:28:42 -06:00
firestar5683 cfdcad5e54 lat updates 2026-01-28 23:52:15 -06:00
firestar5683 eaf78f7c99 Update override.toml 2026-01-19 21:18:47 -06:00
firestar5683 8903857cd5 Revert "Mac Update"
This reverts commit 6c20751a0b.
2026-01-19 11:45:02 -06:00
firestarsdog 7da0ff0875 Add SASCM to vehicle settings detection/stats 2026-01-19 10:38:11 -06:00
firestar5683 6c20751a0b Mac Update 2026-01-18 22:23:06 -06:00
firestarsdog f3c02aaf56 Stats 2026-01-18 22:16:51 -06:00
firestar5683 7abcd68c0d Update interface.py 2026-01-18 22:08:07 -06:00
firestar5683 f5d53574b3 Torque Controller Update 2026-01-18 21:58:49 -06:00
firestar5683 8072a0442b Update redneck 2026-01-15 22:54:24 -06:00
firestar5683 b71b07816e Split Tune 2026-01-15 19:26:03 -06:00
firestar5683 5e462fc725 fix redneck v2 2026-01-14 13:58:35 -06:00
firestar5683 a194a7c75e Remove lat smooth seconds 2026-01-14 13:50:49 -06:00
firestar5683 0664be6bd1 frogpilot migration 2026-01-12 22:12:21 -06:00
firestar5683 b33939dd5b More defaults 2026-01-12 22:04:25 -06:00
firestar5683 ac3ed7eece Update defaults 2026-01-12 22:01:19 -06:00
firestar5683 040593d359 Big Mac 2026-01-11 16:59:24 -06:00
firestar5683 20382af75a Try Higher Friction 2026-01-10 14:04:28 -06:00
firestar5683 3359805404 Autotune Off 2026-01-09 22:44:33 -06:00
firestar5683 37718176fb Revert "Pedal Braking Limits"
This reverts commit efd99af71e.
2025-12-31 20:56:00 -06:00
firestar5683 efd99af71e Pedal Braking Limits 2025-12-30 20:18:17 -06:00
firestar5683 6cf70ab06c merry christmas 2025-12-24 22:06:08 -06:00
firestar5683 4a06671a14 ds2 2025-12-24 21:30:28 -06:00
firestar5683 75f4d24e12 Try friction adjustment 2025-12-16 14:40:01 -06:00
firestar5683 08a9d254ce Update frogpilot_tracking.py 2025-12-15 00:56:38 -06:00
firestar5683 d3afbd4b15 minsteer speed 2025-12-14 14:47:44 -06:00
firestar5683 3d3870fbad Update Percentages 2025-12-13 19:14:57 -06:00
firestar5683 1acee6ea64 Trailer Load Gas Tuning 2025-12-12 10:51:18 -06:00
firestar5683 2eb5db43bc Live Friction 2025-12-11 16:55:55 -06:00
firestar5683 f184855906 Zero error 2025-12-11 15:12:40 -06:00
firestar5683 ae6ec1595e Updates
Update latcontrol_torque.py

interp friction threshold
2025-12-09 20:01:22 -06:00
firestar5683 e23776693c Change NoEntryAlert message to 'Reverse Gear' 2025-12-06 16:46:18 -06:00
firestar5683 56d7ac9704 Update events.py 2025-12-05 20:14:09 -06:00
firestar5683 8e90f233fb LattyBoi2.0 2025-12-03 23:31:34 -06:00
firestar5683 1fd0a80836 Latty Boi
Revert "Latty Boi"

This reverts commit af687e501cc4bdcda7840453d06595d7ea674148.

Reapply "Latty Boi"

This reverts commit ff5566d4439f5e4997fe83f4f21d0be62b75d75d.
2025-12-02 20:30:23 -06:00
firestar5683 392333c87b Recovery Power 2025-12-02 17:20:24 -06:00
firestar5683 f9daea40bd New planplus 2025-12-02 12:10:22 -06:00
Woohyun Rho 5581ea22e7 Update 2025-11-28 17:13:20 -06:00
firestar5683 d72a3996f6 New Lateral Changes 2025-11-18 20:50:34 -06:00
firestarsdog 37f65bd382 Gen2ACC Distance Button Sync for CC_ONLY_CAR 2025-11-16 00:49:15 -05:00
firestar5683 40dee4661c Update 2025-11-15 14:46:12 -06:00
firestar5683 dcb0104208 Accel Tests 2025-11-14 20:33:42 -06:00
firestar5683 38d725ea03 Paddle Planner 2025-11-14 19:47:31 -05:00
firestar5683 5b69250cac Revert "Upstream Lateral"
This reverts commit faf1a6755d.
2025-11-10 18:33:23 -06:00
firestar5683 faf1a6755d Upstream Lateral
Revert "Upstream Lateral"

This reverts commit 20f7d6631bb152860781b46533bb0e96223be132.

Reapply "Upstream Lateral"

This reverts commit 2a7a563e219a1b688529d341919fc377fed5038e.

Update latcontrol_torque.py

more lateral

Update ui
2025-11-07 00:14:06 -06:00
firestar5683 2d51ca9538 Revert "Pedal Panda?"
This reverts commit 938fe7a088.
2025-10-30 22:36:32 -05:00
firestar5683 78223aa635 Pedal? 2025-10-28 16:48:48 -05:00
firestar5683 724ac83641 Update 2025-10-25 15:43:52 -05:00
firestar5683 21c28cd8c8 Medium Fanta 2025-10-22 22:27:58 -05:00
firestar5683 436a2f0c1a Update interfaces.py 2025-10-20 13:49:14 -05:00
firestar5683 f5b734d6e6 no nnff 2025-10-19 17:59:30 -05:00
firestar5683 424273284a Scene Complexity 2025-10-18 14:26:02 -05:00
firestar5683 48fc133cf9 Update carcontroller.py 2025-10-17 19:10:56 -05:00
firestar5683 e2722dd9ba Update interface.py 2025-10-17 18:14:14 -05:00
firestar5683 96e80fb926 CEM 2025-10-17 17:39:31 -05:00
firestar5683 d81515be22 lite 2025-10-17 17:13:56 -05:00
firestar5683 abe891f971 Redneck 2.0 2025-10-17 16:54:17 -05:00
firestar5683 b45630fd71 Modify torque tuning parameters in interfaces.py
Adjusted torque tuning parameters for improved performance.
2025-10-17 16:54:08 -05:00
firestar5683 559184621b Hurts Donut 2025-10-16 23:58:42 -05:00
firestar5683 2e3b62432c Smoothy Boi 2025-10-16 23:36:34 -05:00
firestar5683 50cb5778bd lat3 2025-10-15 22:21:26 -05:00
firestar5683 dda6f78dcc oopsie doopsie 2025-10-12 00:12:21 -05:00
firestar5683 66df2e23aa New Lateral Changes 2025-10-10 21:00:05 -05:00
firestar5683 69cd237ef4 Fix Standard 2025-10-10 19:24:48 -05:00
firestar5683 938fe7a088 Pedal Panda? 2025-10-10 13:18:20 -05:00
firestar5683 40019815c1 pedal
Revert "pedal"

This reverts commit 164a4ac9ff39c244b23d118f38bedb097867fa2e.

f
2025-10-09 12:22:10 -05:00
firestar5683 5125403753 Update interface.py 2025-10-08 23:25:07 -05:00
firestar5683 7cea8cd192 error? 2025-10-08 23:13:38 -05:00
firestar5683 2bada45e97 Update interface.py 2025-10-08 23:00:27 -05:00
firestar5683 39cdd2f5d3 Fix Pedal 2025-10-08 22:36:11 -05:00
firestar5683 939748dd03 Fix New Devices 2025-10-08 22:19:50 -05:00
firestar5683 20447a47e5 No positive P-response for long control if user-selected parameter set 2025-10-08 07:42:41 -05:00
firestar5683 0282d33d3b Update carstate.py 2025-10-08 07:14:12 -05:00
firestar5683 d452ee2a8d Silverado 2025-10-07 18:13:15 -05:00
firestar5683 bce304c6bc Update values.py 2025-10-07 17:50:23 -05:00
firestar5683 a03826d5fa Update carcontroller.py 2025-10-07 17:03:16 -05:00
firestar5683 0f24b3383e Update carcontroller.py 2025-10-07 15:49:57 -05:00
firestar5683 5c3e33bceb Automatic updates 2025-10-04 16:03:43 -05:00
firestar5683 0b4fd887c8 Steer Alerts 2025-10-04 01:49:03 -05:00
firestar5683 36499deb71 Update frogpilot_acceleration.py 2025-10-03 22:41:54 -05:00
firestar5683 c7464f41da SteerAlerts 2025-10-03 22:13:17 -05:00
firestar5683 cab3268601 Update carcontroller.py 2025-10-03 21:58:18 -05:00
firestar5683 b91119b5e6 Update carcontroller.py 2025-10-03 21:25:35 -05:00
firestar5683 db2cf740fe Donut DM 2025-10-03 20:49:14 -05:00
firestar5683 3da42a78a3 Update gm_global_a_powertrain_generated.dbc 2025-10-03 16:14:27 -05:00
firestar5683 ac72c0e2c0 Cruise Fault? 2025-10-03 16:07:08 -05:00
firestar5683 9b7bef0877 Update 2025-10-03 00:43:33 -05:00
firestar5683 5208dd65aa Reapply "Torque Panda"
This reverts commit d0d28e661b.
2025-10-03 00:17:34 -05:00
firestar5683 bbe340147b Panda 2025-10-03 00:10:38 -05:00
firestar5683 db579b7f3c Cruise Fault?
Revert "Cruise Fault?"

This reverts commit 8de3739597a6e729b3c55fc053a13add9129a1f4.
2025-10-02 23:25:12 -05:00
firestar5683 07cb470c25 firehose 2025-09-30 20:45:22 -05:00
firestar5683 851f219cef Update tinygrad_modeld.py 2025-09-30 14:30:43 -05:00
firestar5683 2123be2eca Torque 2025-09-30 10:43:37 -05:00
firestar5683 b1bd4d1f27 Dom 2025-09-30 10:30:26 -05:00
22 changed files with 574 additions and 169 deletions
+14 -3
View File
@@ -82,6 +82,12 @@ VAL_TABLE_ HandsOffSWDetectionMode 2 "Failed" 1 "Enabled" 0 "Disabled" ;
BO_ 189 EBCMRegenPaddle: 7 K17_EBCM BO_ 189 EBCMRegenPaddle: 7 K17_EBCM
SG_ RegenPaddle : 7|4@0+ (1,0) [0|0] "" NEO 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 BO_ 190 ECMAcceleratorPos: 6 K20_ECM
SG_ BrakePedalPos : 15|8@0+ (1,0) [0|0] "sticky" NEO SG_ BrakePedalPos : 15|8@0+ (1,0) [0|0] "sticky" NEO
@@ -192,10 +198,15 @@ BO_ 500 SportMode: 6 XXX
SG_ SportMode : 15|1@0+ (1,0) [0|1] "" XXX SG_ SportMode : 15|1@0+ (1,0) [0|1] "" XXX
BO_ 501 ECMPRDNL2: 8 K20_ECM BO_ 501 ECMPRDNL2: 8 K20_ECM
SG_ TransmissionState : 48|4@1+ (1,0) [0|7] "" NEO 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_ 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_ 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
BO_ 532 BRAKE_RELATED: 6 XXX BO_ 532 BRAKE_RELATED: 6 XXX
SG_ UserBrakePressure : 0|9@0+ (1,0) [0|511] "" XXX SG_ UserBrakePressure : 0|9@0+ (1,0) [0|511] "" XXX
@@ -370,6 +381,6 @@ VAL_ 715 GasRegenCmdActive 1 "Active" 0 "Inactive" ;
VAL_ 320 Intellibeam 1 "Active" 0 "Inactive" ; VAL_ 320 Intellibeam 1 "Active" 0 "Inactive" ;
VAL_ 320 HighBeamsActive 1 "Active" 0 "Inactive" ; VAL_ 320 HighBeamsActive 1 "Active" 0 "Inactive" ;
VAL_ 320 HighBeamsTemporary 1 "Active" 0 "Inactive" ; VAL_ 320 HighBeamsTemporary 1 "Active" 0 "Inactive" ;
VAL_ 501 PRNDL2 6 "L" 4 "D" 3 "N" 2 "R" 1 "P" 0 "Shifting"; VAL_ 501 PRNDL2 7 "L2" 6 "L" 5 "L3" 4 "D" 3 "N" 2 "R" 1 "P" 0 "Shifting";
VAL_ 501 TransmissionState 11 "Shifting" 10 "Reverse" 9 "Forward" 8 "Disengaged"; VAL_ 501 TransmissionState 11 "Shifting" 10 "Reverse" 9 "Forward" 8 "Disengaged";
VAL_ 501 ManualMode 1 "Active" 0 "Inactive" VAL_ 501 ManualMode 1 "Active" 0 "Inactive"
+3 -3
View File
@@ -202,9 +202,9 @@ void ignition_can_hook(CANPacket_t *to_push) {
int len = GET_LEN(to_push); int len = GET_LEN(to_push);
// GM exception // GM exception
if ((addr == 0x1F1) && (len == 8)) { if ((addr == 0xC9) && (len == 8)) {
// SystemPowerMode (2=Run, 3=Crank Request) // Matches SystemPowerMode (1=Run, 0=Off)
ignition_can = (GET_BYTE(to_push, 0) & 0x2U) != 0U; ignition_can = (GET_BYTE(to_push, 6) & 0x10U) != 0U;
ignition_can_cnt = 0U; ignition_can_cnt = 0U;
} }
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -88,7 +88,7 @@ int safety_fwd_hook(int bus_num, int addr) {
} }
bool get_longitudinal_allowed(void) { bool get_longitudinal_allowed(void) {
return controls_allowed && !gas_pressed_prev; return controls_allowed && !gas_pressed;
} }
// Given a CRC-8 poly, generate a static lookup table to use with a fast CRC-8 // Given a CRC-8 poly, generate a static lookup table to use with a fast CRC-8
+60 -18
View File
@@ -10,16 +10,16 @@ const SteeringLimits GM_STEERING_LIMITS = {
}; };
const LongitudinalLimits GM_ASCM_LONG_LIMITS = { const LongitudinalLimits GM_ASCM_LONG_LIMITS = {
.max_gas = 3072, .max_gas = 8191,
.min_gas = 1404, .min_gas = 5500,
.inactive_gas = 1404, .inactive_gas = 5500,
.max_brake = 400, .max_brake = 400,
}; };
const LongitudinalLimits GM_CAM_LONG_LIMITS = { const LongitudinalLimits GM_CAM_LONG_LIMITS = {
.max_gas = 3400, .max_gas = 8848,
.min_gas = 1514, .min_gas = 5610,
.inactive_gas = 1554, .inactive_gas = 5650,
.max_brake = 400, .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 // 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 // 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 = 515; // (675 + 355) / 2 ratio between offset and gain from dbc file const int GM_GAS_INTERCEPTOR_THRESHOLD = 595; // (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 #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}, // 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}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
{0xA1, 1, 7}, {0x306, 1, 8}, {0x308, 1, 7}, {0x310, 1, 2}, // obs bus {0xA1, 1, 7}, {0x306, 1, 8}, {0x308, 1, 7}, {0x310, 1, 2}, // obs bus
{0x315, 2, 5}}; // ch bus {0x315, 2, 5}}; // ch bus
const CanMsg GM_CAM_TX_MSGS[] = {{0x180, 0, 4}, {0x200, 0, 6}, {0x1E1, 0, 7}, // pt bus const CanMsg GM_CAM_TX_MSGS[] = {{0x180, 0, 4}, {0x200, 0, 6}, {0x1E1, 0, 7}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
{0x1E1, 2, 7}, {0x184, 2, 8}}; // camera 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}, // pt 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
{0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus {0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus
const CanMsg GM_SDGM_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, // pt 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 {0x184, 2, 8}}; // camera bus
const CanMsg GM_CC_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, // pt bus const CanMsg GM_CC_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
{0x184, 2, 8}, {0x1E1, 2, 7}}; // camera bus {0x184, 2, 8}, {0x1E1, 2, 7}}; // camera bus
// TODO: do checksum and counter checks. Add correct timestep, 0.1s for now. // 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))) { if ((addr == 0xC9) && ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM))) {
brake_pressed = GET_BIT(to_push, 40U); brake_pressed = GET_BIT(to_push, 40U) != 0U;
} }
if (addr == 0xC9) { if (addr == 0xC9) {
@@ -172,6 +172,13 @@ static void gm_rx_hook(const CANPacket_t *to_push) {
} }
} }
// Cruise check for ACC models with pedal interceptor - block stock ACC
if ((addr == 0x1C4) && gm_has_acc && enable_gas_interceptor) {
// When pedal interceptor is active on ACC models, ignore stock cruise state
// to prevent conflicts between pedal interceptor and stock ACC
cruise_engaged_prev = false;
}
if (addr == 0xBD) { if (addr == 0xBD) {
regen_braking = (GET_BYTE(to_push, 0) >> 4) != 0U; regen_braking = (GET_BYTE(to_push, 0) >> 4) != 0U;
} }
@@ -192,6 +199,12 @@ static void gm_rx_hook(const CANPacket_t *to_push) {
} }
generic_rx_checks(stock_ecu_detected); generic_rx_checks(stock_ecu_detected);
} }
// Cruise check for Gen2 Bolt (ASCMActiveCruiseControlStatus on bus 2)
int addr = GET_ADDR(to_push);
if ((addr == 0x370) && (GET_BUS(to_push) == 2U)) {
bool cruise_engaged = (GET_BYTE(to_push, 2) >> 7) != 0U; // ACCCmdActive
cruise_engaged_prev = cruise_engaged;
}
} }
static bool gm_tx_hook(const CANPacket_t *to_send) { static bool gm_tx_hook(const CANPacket_t *to_send) {
@@ -229,7 +242,7 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
// GAS/REGEN: safety check // GAS/REGEN: safety check
if (addr == 0x2CB) { if (addr == 0x2CB) {
bool apply = GET_BIT(to_send, 0U); bool apply = GET_BIT(to_send, 0U);
int gas_regen = ((GET_BYTE(to_send, 2) & 0x7FU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3); int gas_regen = ((GET_BYTE(to_send, 1) & 0x1U) << 13) + ((GET_BYTE(to_send, 2) & 0xFFU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3);
bool violation = false; bool violation = false;
// Allow apply bit in pre-enabled and overriding states // Allow apply bit in pre-enabled and overriding states
@@ -246,6 +259,11 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U; int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U;
bool allowed_btn = (button == GM_BTN_CANCEL) && cruise_engaged_prev; bool allowed_btn = (button == GM_BTN_CANCEL) && cruise_engaged_prev;
// For ACC cars with pedal interceptor, allow cancel even if cruise_engaged_prev is false
// (since we set it to false to prevent conflicts, but still need to cancel cruise)
if (gm_hw == GM_CAM && enable_gas_interceptor && button == GM_BTN_CANCEL) {
allowed_btn = true;
}
// For standard CC, allow spamming of SET / RESUME // For standard CC, allow spamming of SET / RESUME
if (gm_cc_long) { if (gm_cc_long) {
allowed_btn |= cruise_engaged_prev && (button == GM_BTN_SET || button == GM_BTN_RESUME || button == GM_BTN_UNPRESS); allowed_btn |= cruise_engaged_prev && (button == GM_BTN_SET || button == GM_BTN_RESUME || button == GM_BTN_UNPRESS);
@@ -256,6 +274,22 @@ 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; return tx;
} }
@@ -272,9 +306,13 @@ static int gm_fwd_hook(int bus_num, int addr) {
} }
if (bus_num == 2) { if (bus_num == 2) {
// block lkas message and acc messages if gm_cam_long, forward all others // block lkas message and acc messages
// Block 0x370 only for experimental long without pedal interceptor
bool is_lkas_msg = (addr == 0x180); bool is_lkas_msg = (addr == 0x180);
bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370); bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB);
if (gm_cam_long && !enable_gas_interceptor) {
is_acc_msg = is_acc_msg || (addr == 0x370);
}
bool block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long); bool block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long);
if (!block_msg) { if (!block_msg) {
bus_fwd = 0; bus_fwd = 0;
@@ -297,15 +335,19 @@ static safety_config gm_init(uint16_t param) {
gm_force_ascm = GET_FLAG(param, GM_PARAM_HW_ASCM_LONG); gm_force_ascm = GET_FLAG(param, GM_PARAM_HW_ASCM_LONG);
if (gm_hw == GM_ASCM || gm_force_ascm) { 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)) { } 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 { } else {
} }
gm_pedal_long = GET_FLAG(param, GM_PARAM_PEDAL_LONG); gm_pedal_long = GET_FLAG(param, GM_PARAM_PEDAL_LONG);
gm_cc_long = GET_FLAG(param, GM_PARAM_CC_LONG); gm_cc_long = GET_FLAG(param, GM_PARAM_CC_LONG);
gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG) && !gm_cc_long; gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG) && !gm_cc_long;
// Block ACC messages when pedal interceptor is active on ACC models
if (gm_hw == GM_CAM && enable_gas_interceptor) {
gm_cam_long = true;
}
gm_pcm_cruise = ((gm_hw == GM_CAM) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long) || (gm_hw == GM_SDGM); gm_pcm_cruise = ((gm_hw == GM_CAM) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long) || (gm_hw == GM_SDGM);
gm_skip_relay_check = GET_FLAG(param, GM_PARAM_NO_CAMERA); gm_skip_relay_check = GET_FLAG(param, GM_PARAM_NO_CAMERA);
gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC); gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC);
+281 -41
View File
@@ -1,3 +1,7 @@
from typing import Tuple
import time
import math
from openpilot.common.swaglog import cloudlog
from cereal import car from cereal import car
from openpilot.common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.filter_simple import FirstOrderFilter
@@ -7,10 +11,11 @@ from openpilot.common.params_pyx import Params
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, create_gas_interceptor_command 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 import gmcan
from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, CarControllerParams, CruiseButtons, GMFlags, CC_ONLY_CAR, SDGM_CAR, EV_CAR 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.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.controls.lib.drive_helpers import apply_deadzone from openpilot.selfdrive.controls.lib.drive_helpers import apply_deadzone
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.common.swaglog import cloudlog
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
NetworkLocation = car.CarParams.NetworkLocation NetworkLocation = car.CarParams.NetworkLocation
@@ -22,7 +27,15 @@ TransmissionType = car.CarParams.TransmissionType
CAMERA_CANCEL_DELAY_FRAMES = 10 CAMERA_CANCEL_DELAY_FRAMES = 10
# Enforce a minimum interval between steering messages to avoid a fault # Enforce a minimum interval between steering messages to avoid a fault
MIN_STEER_MSG_INTERVAL_MS = 15 MIN_STEER_MSG_INTERVAL_MS = 15
# Twosided 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 perside 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 # Constants for pitch compensation
BRAKE_PITCH_FACTOR_BP = [5., 10.] # [m/s] smoothly revert to planned accel at low speeds 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 BRAKE_PITCH_FACTOR_V = [0., 1.] # [unitless in [0,1]]; don't touch
@@ -38,6 +51,11 @@ class CarController(CarControllerBase):
self.apply_speed = 0 self.apply_speed = 0
self.frame = 0 self.frame = 0
self.last_steer_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.last_button_frame = 0
self.cancel_counter = 0 self.cancel_counter = 0
self.pedal_steady = 0. self.pedal_steady = 0.
@@ -48,33 +66,101 @@ class CarController(CarControllerBase):
self.params = CarControllerParams(self.CP) self.params = CarControllerParams(self.CP)
self.params_ = Params() 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_pt = CANPacker(DBC[self.CP.carFingerprint]['pt'])
self.packer_obj = CANPacker(DBC[self.CP.carFingerprint]['radar']) self.packer_obj = CANPacker(DBC[self.CP.carFingerprint]['radar'])
self.packer_ch = CANPacker(DBC[self.CP.carFingerprint]['chassis']) self.packer_ch = CANPacker(DBC[self.CP.carFingerprint]['chassis'])
# FrogPilot variables # FrogPilot variables
self.accel_g = 0.0 self.accel_g = 0.0
self.pitch = FirstOrderFilter(0., 0.09 * 4, DT_CTRL * 4) # runs at 25 Hz 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
return pedal_gas # 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 (framebased): 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 600 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
def update(self, CC, CS, now_nanos, frogpilot_toggles): def update(self, CC, CS, now_nanos, frogpilot_toggles):
self.CS = CS
self.aego = CS.out.aEgo
actuators = CC.actuators actuators = CC.actuators
accel = brake_accel = actuators.accel 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_control = CC.hudControl
hud_alert = hud_control.visualAlert hud_alert = hud_control.visualAlert
hud_v_cruise = hud_control.setSpeed hud_v_cruise = hud_control.setSpeed
@@ -83,6 +169,138 @@ class CarController(CarControllerBase):
# Send CAN commands. # Send CAN commands.
can_sends = [] 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 twosided 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) # Steering (Active: 50Hz, inactive: 10Hz)
steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP
@@ -112,24 +330,35 @@ class CarController(CarControllerBase):
else: else:
apply_steer = 0 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.last_steer_frame = self.frame
self.apply_steer_last = apply_steer self.apply_steer_last = apply_steer
idx = self.lka_steering_cmd_counter % 4 idx = self.lka_steering_cmd_counter % 4
can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, CC.latActive)) 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: if self.CP.openpilotLongitudinalControl:
# Gas/regen, brakes, and UI commands - all at 25Hz # Gas/regen, brakes, and UI commands - all at 25Hz
if self.frame % 4 == 0: if self.frame % 4 == 0:
stopping = actuators.longControlState == LongCtrlState.stopping 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 at_full_stop = CC.longActive and CS.out.standstill
near_stop = CC.longActive and (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE) near_stop = CC.longActive and (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE)
interceptor_gas_cmd = 0 interceptor_gas_cmd = 0
@@ -141,21 +370,33 @@ class CarController(CarControllerBase):
self.apply_gas = self.params.INACTIVE_REGEN self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = int(min(-100 * frogpilot_toggles.stopAccel, self.params.MAX_BRAKE)) self.apply_brake = int(min(-100 * frogpilot_toggles.stopAccel, self.params.MAX_BRAKE))
else: else:
# Normal operation if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
if self.CP.carFingerprint in EV_CAR: accel_due_to_pitch = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
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: else:
self.apply_gas = int(round(interp(accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V))) accel_due_to_pitch = 0.0
self.apply_brake = int(round(interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
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
# Don't allow any gas above inactive regen while stopping # Don't allow any gas above inactive regen while stopping
# FIXME: brakes aren't applied immediately when enabling at a stop # FIXME: brakes aren't applied immediately when enabling at a stop
if stopping: if stopping:
self.apply_gas = self.params.INACTIVE_REGEN self.apply_gas = self.params.INACTIVE_REGEN
if self.CP.carFingerprint in CC_ONLY_CAR: if self.CP.carFingerprint in CC_ONLY_CAR:
# gas interceptor only used for full long control on cars without ACC # gas interceptor only used for full long control on cars without ACC
interceptor_gas_cmd = self.calc_pedal_command(actuators.accel, CC.longActive) interceptor_gas_cmd, press_regen_paddle = self.calc_pedal_command(actuators.accel, CC.longActive, CS.out.vEgo)
if self.CP.enableGasInterceptor and self.apply_gas > self.params.INACTIVE_REGEN and CS.out.cruiseState.standstill: 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 # "Tap" the accelerator pedal to re-engage ACC
@@ -168,7 +409,10 @@ class CarController(CarControllerBase):
if self.CP.flags & GMFlags.CC_LONG.value: if self.CP.flags & GMFlags.CC_LONG.value:
if CC.longActive and CS.out.cruiseState.enabled and CS.out.vEgo > self.CP.minEnableSpeed: 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 # 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)) 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))
if self.CP.enableGasInterceptor: if self.CP.enableGasInterceptor:
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx)) can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
if self.CP.carFingerprint not in CC_ONLY_CAR: if self.CP.carFingerprint not in CC_ONLY_CAR:
@@ -191,7 +435,7 @@ class CarController(CarControllerBase):
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation # 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_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, 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 dashboard UI commands (ACC status)
send_fcw = hud_alert == VisualAlert.fcw send_fcw = hud_alert == VisualAlert.fcw
@@ -202,22 +446,18 @@ class CarController(CarControllerBase):
accel += self.accel_g accel += self.accel_g
# Radar needs to know current speed and yaw rate (50hz), # Radar needs to know current speed and yaw rate (50hz),
# and that ADAS is alive (10hz) # and that ADAS is alive (5hz, previously 10hz)
if not self.CP.radarUnavailable: if not self.CP.radarUnavailable:
tt = self.frame * DT_CTRL tt = self.frame * DT_CTRL
time_and_headlights_step = 10 time_and_headlights_step = 20
if self.frame % time_and_headlights_step == 0: if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4 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_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE)) 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_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, 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 == 0: if self.CP.networkLocation == NetworkLocation.gateway and self.frame % (self.params.ADAS_KEEPALIVE_STEP * 2) == 0:
can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN) can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN)
# TODO: integrate this with the code block below? # TODO: integrate this with the code block below?
@@ -227,7 +467,7 @@ class CarController(CarControllerBase):
) and CS.out.cruiseState.enabled: ) and CS.out.cruiseState.enabled:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04: if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
self.last_button_frame = self.frame self.last_button_frame = self.frame
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL)) can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
else: else:
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status. # While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
@@ -245,7 +485,7 @@ class CarController(CarControllerBase):
if self.CP.networkLocation == NetworkLocation.fwdCamera: if self.CP.networkLocation == NetworkLocation.fwdCamera:
# Silence "Take Steering" alert sent by camera, forward PSCMStatus with HandsOffSWlDetectionStatus=1 # Silence "Take Steering" alert sent by camera, forward PSCMStatus with HandsOffSWlDetectionStatus=1
if self.frame % 10 == 0: if self.frame % 20 == 0:
can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status)) can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status))
new_actuators = actuators.as_builder() new_actuators = actuators.as_builder()
+19 -11
View File
@@ -5,7 +5,7 @@ from openpilot.common.numpy_fast import mean
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car.interfaces import CarStateBase 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 from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD, GMFlags, CC_ONLY_CAR, CAMERA_ACC_CAR, SDGM_CAR, CC_REGEN_PADDLE_CAR, CAR
TransmissionType = car.CarParams.TransmissionType TransmissionType = car.CarParams.TransmissionType
NetworkLocation = car.CarParams.NetworkLocation NetworkLocation = car.CarParams.NetworkLocation
@@ -53,6 +53,13 @@ class CarState(CarStateBase):
self.moving_backward = (pt_cp.vl["EBCMWheelSpdRear"]["RLWheelDir"] == 2) or (pt_cp.vl["EBCMWheelSpdRear"]["RRWheelDir"] == 2) self.moving_backward = (pt_cp.vl["EBCMWheelSpdRear"]["RLWheelDir"] == 2) or (pt_cp.vl["EBCMWheelSpdRear"]["RRWheelDir"] == 2)
# Variables used for avoiding LKAS faults # 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 self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0
if self.loopback_lka_steering_cmd_updated: if self.loopback_lka_steering_cmd_updated:
self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"] self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"]
@@ -71,10 +78,7 @@ class CarState(CarStateBase):
# sample rear wheel speeds, standstill=True if ECM allows engagement with brake # 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.standstill = ret.wheelSpeeds.rl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
if pt_cp.vl["ECMPRDNL2"]["ManualMode"] == 1: ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["ECMPRDNL2"]["PRNDL2"], None))
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: if self.CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value:
ret.brake = pt_cp.vl["EBCMBrakePedalPosition"]["BrakePedalPosition"] / 0xd0 ret.brake = pt_cp.vl["EBCMBrakePedalPosition"]["BrakePedalPosition"] / 0xd0
@@ -92,11 +96,11 @@ class CarState(CarStateBase):
# Regen braking is braking # Regen braking is braking
if self.CP.transmissionType == TransmissionType.direct: if self.CP.transmissionType == TransmissionType.direct:
ret.regenBraking = pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0 ret.regenBraking = pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0
self.single_pedal_mode = ret.gearShifter == GearShifter.low or pt_cp.vl["EVDriveMode"]["SinglePedalModeActive"] == 1 or (ret.regenBraking and GearShifter.manumatic) self.single_pedal_mode = ret.gearShifter == GearShifter.low or pt_cp.vl["EVDriveMode"]["SinglePedalModeActive"] == 1 or (ret.regenBraking and GearShifter.manumatic) or (self.CP.carFingerprint in [CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_BOLT_CC] and self.CP.enableGasInterceptor)
if self.CP.enableGasInterceptor: if self.CP.enableGasInterceptor:
ret.gas = (pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2. ret.gas = (pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2.
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. 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.
ret.gasPressed = ret.gas > threshold ret.gasPressed = ret.gas > threshold
else: else:
ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254. ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254.
@@ -159,7 +163,11 @@ class CarState(CarStateBase):
if self.CP.carFingerprint in CC_ONLY_CAR: if self.CP.carFingerprint in CC_ONLY_CAR:
ret.accFaulted = False ret.accFaulted = False
ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0 # Try ASCM first for cars that might have it (like misfingerprinted Bolts), fall back to ECM
try:
ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0
except:
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
if self.CP.enableBsm: if self.CP.enableBsm:
if self.CP.carFingerprint not in SDGM_CAR: if self.CP.carFingerprint not in SDGM_CAR:
@@ -202,7 +210,7 @@ class CarState(CarStateBase):
messages += [ messages += [
("AEBCmd", 10), ("AEBCmd", 10),
] ]
if CP.carFingerprint not in CC_ONLY_CAR: # Include ASCMActiveCruiseControlStatus for all non-SDGM fwdCamera cars
messages += [ messages += [
("ASCMActiveCruiseControlStatus", 25), ("ASCMActiveCruiseControlStatus", 25),
] ]
@@ -230,7 +238,7 @@ class CarState(CarStateBase):
] ]
else: else:
messages += [ messages += [
("ECMPRDNL2", 10), ("ECMPRDNL2", 40),
("AcceleratorPedal2", 33), ("AcceleratorPedal2", 33),
("ECMEngineStatus", 100), ("ECMEngineStatus", 100),
("BCMTurnSignals", 1), ("BCMTurnSignals", 1),
@@ -252,7 +260,7 @@ class CarState(CarStateBase):
if CP.transmissionType == TransmissionType.direct: if CP.transmissionType == TransmissionType.direct:
messages += [ messages += [
("EBCMRegenPaddle", 50), ("EBCMRegenPaddle", 40),
("EVDriveMode", 0), ("EVDriveMode", 0),
] ]
+48 -25
View File
@@ -66,7 +66,6 @@ def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop):
"GasRegenFullStopActive": at_full_stop, "GasRegenFullStopActive": at_full_stop,
"GasRegenAlwaysOne": 1, "GasRegenAlwaysOne": 1,
"GasRegenAlwaysOne2": 1, "GasRegenAlwaysOne2": 1,
"GasRegenAlwaysOne3": 1,
} }
dat = packer.make_can_msg("ASCMGasRegenCmd", bus, values)[2] dat = packer.make_can_msg("ASCMGasRegenCmd", bus, values)[2]
@@ -177,45 +176,69 @@ def create_lka_icon_command(bus, active, critical, steer):
dat = b"\x00\x00\x00" dat = b"\x00\x00\x00"
return make_can_msg(0x104c006c, dat, bus) 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_gm_cc_spam_command(packer, controller, CS, actuators): def create_prndl2_command(packer, bus, press_regen_paddle):
if controller.params_.get_bool("IsMetric"): prndl2_value = 5 if press_regen_paddle else 6
_CV = CV.MS_TO_KPH manual_mode = 1 if press_regen_paddle else 0
RATE_UP_MAX = 0.04 values = {
RATE_DOWN_MAX = 0.04 "Byte0": 0x0C,
else: "Byte1": 0x0C,
_CV = CV.MS_TO_MPH "Byte2": 0x00,
RATE_UP_MAX = 0.2 "PRNDL2": prndl2_value,
RATE_DOWN_MAX = 0.2 "Byte4": 0x00,
"ManualMode": manual_mode,
"TransmissionState": 1,
"Byte7": 0x00
}
return packer.make_can_msg("ECMPRDNL2", bus, values)
accel = actuators.accel * _CV # m/s/s to mph/s def create_gm_cc_spam_command(packer, controller, CS, actuators, frogpilot_toggles):
speedSetPoint = int(round(CS.out.cruiseState.speed * _CV)) accel = actuators.accel
Vego = CS.out.vEgo
cruiseBtn = CruiseButtons.INIT cruiseBtn = CruiseButtons.INIT
if speedSetPoint == CS.CP.minEnableSpeed and accel < -1: 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:
cruiseBtn = CruiseButtons.CANCEL cruiseBtn = CruiseButtons.CANCEL
controller.apply_speed = 0 controller.apply_speed = 0
rate = 0.04 elif DesiredSetPoint < speedSetPoint and speedSetPoint > CS.CP.minEnableSpeed * MS_CONVERT + 1:
elif accel < 0:
cruiseBtn = CruiseButtons.DECEL_SET 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 controller.apply_speed = speedSetPoint - 1
elif accel > 0: elif DesiredSetPoint > speedSetPoint:
cruiseBtn = CruiseButtons.RES_ACCEL 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 controller.apply_speed = speedSetPoint + 1
else: else:
cruiseBtn = CruiseButtons.INIT
controller.apply_speed = speedSetPoint controller.apply_speed = speedSetPoint
rate = float('inf')
# Check rlogs closely - our message shouldn't show up on the pt bus for us # 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 # 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): if (cruiseBtn != CruiseButtons.INIT) and ((controller.frame - controller.last_button_frame) * DT_CTRL > rate):
controller.last_button_frame = controller.frame controller.last_button_frame = controller.frame
idx = (CS.buttons_counter + 1) % 4 # Need to predict the next idx for '22-23 EUV idx = (CS.buttons_counter + 1) % 4 # Need to predict the next idx for '22-23 EUV
+50 -26
View File
@@ -27,10 +27,22 @@ CAM_MSG = 0x320 # AEBCmd
ACCELERATOR_POS_MSG = 0xbe ACCELERATOR_POS_MSG = 0xbe
NON_LINEAR_TORQUE_PARAMS = { NON_LINEAR_TORQUE_PARAMS = {
CAR.CHEVROLET_BOLT_EUV: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178], CAR.CHEVROLET_BOLT_EUV: {
CAR.CHEVROLET_BOLT_CC: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178], "left": [2.6531724862969748, 1.1, 0.1719764879840985, 0.0],
CAR.GMC_ACADIA: [4.78003305, 1.0, 0.3122, 0.05591772], "right": [2.8531724862969748, 1.0, 0.1469764879840985, 0.0],
CAR.CHEVROLET_SILVERADO: [3.29974374, 1.0, 0.25571356, 0.0465122] },
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.29974374, 1.0, 0.25571356, 0.0465122],
"right": [3.29974374, 1.0, 0.25571356, 0.0465122],
},
} }
@@ -64,10 +76,12 @@ class CarInterface(CarInterfaceBase):
# This has big effect on the stability about 0 (noise when going straight) # 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) non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint)
assert non_linear_torque_params, "The params are not defined" assert non_linear_torque_params, "The params are not defined"
a, b, c, _ = non_linear_torque_params # Left is positive
side_key = "left" if lateral_acceleration >= 0 else "right"
a, b, c, d = non_linear_torque_params[side_key]
sig_input = a * lateral_acceleration sig_input = a * lateral_acceleration
sig = np.sign(sig_input) * (1 / (1 + exp(-fabs(sig_input))) - 0.5) sig = np.sign(sig_input) * (1 / (1 + exp(-fabs(sig_input))) - 0.5)
steer_torque = (sig * b) + (lateral_acceleration * c) steer_torque = (sig * b) + (lateral_acceleration * c) + d
return float(steer_torque) return float(steer_torque)
lataccel_values = np.arange(-5.0, 5.0, 0.01) lataccel_values = np.arange(-5.0, 5.0, 0.01)
@@ -117,31 +131,36 @@ class CarInterface(CarInterfaceBase):
if PEDAL_MSG in fingerprint[0]: if PEDAL_MSG in fingerprint[0]:
ret.enableGasInterceptor = True ret.enableGasInterceptor = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR 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: if candidate in EV_CAR:
ret.transmissionType = TransmissionType.direct ret.transmissionType = TransmissionType.direct
else: else:
ret.transmissionType = TransmissionType.automatic ret.transmissionType = TransmissionType.automatic
ret.longitudinalTuning.kiBP = [5., 35.] ret.longitudinalTuning.kiBP = [5., 35., 60.]
if candidate in CAMERA_ACC_CAR: if candidate in CAMERA_ACC_CAR:
ret.experimentalLongitudinalAvailable = candidate not in CC_ONLY_CAR ret.experimentalLongitudinalAvailable = candidate not in CC_ONLY_CAR
ret.networkLocation = NetworkLocation.fwdCamera ret.networkLocation = NetworkLocation.fwdCamera
ret.radarUnavailable = True # no radar ret.radarUnavailable = True # no radar
# Use pcmCruise by default; this may be overridden below if a pedal interceptor is detected
ret.pcmCruise = True ret.pcmCruise = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM 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.minEnableSpeed = 5 * CV.KPH_TO_MS
ret.minSteerSpeed = 10 * CV.KPH_TO_MS ret.minSteerSpeed = 10 * CV.KPH_TO_MS
# Tuning for experimental long # Tuning for experimental long
ret.longitudinalTuning.kiV = [2.0, 1.5] ret.longitudinalTuning.kiV = [0.5, 0.5, 0.5]
ret.vEgoStopping = 0.1 ret.vEgoStopping = 0.1
ret.vEgoStarting = 0.1 ret.vEgoStarting = 0.1
ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling ret.stoppingDecelRate = 1.0 # reach brake quickly after enabling
ret.vEgoStopping = 0.25 ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25 ret.vEgoStarting = 0.25
ret.stopAccel = -0.25
if ret.experimentalLongitudinalAvailable and experimental_long: if ret.experimentalLongitudinalAvailable and experimental_long:
ret.pcmCruise = False ret.pcmCruise = False
@@ -149,7 +168,7 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
elif candidate in SDGM_CAR: elif candidate in SDGM_CAR:
ret.longitudinalTuning.kiV = [0., 0.] # TODO: tuning ret.longitudinalTuning.kiV = [0., 0., 0.] # TODO: tuning
ret.experimentalLongitudinalAvailable = False ret.experimentalLongitudinalAvailable = False
ret.networkLocation = NetworkLocation.fwdCamera ret.networkLocation = NetworkLocation.fwdCamera
ret.pcmCruise = True ret.pcmCruise = True
@@ -168,7 +187,7 @@ class CarInterface(CarInterfaceBase):
ret.minSteerSpeed = 7 * CV.MPH_TO_MS ret.minSteerSpeed = 7 * CV.MPH_TO_MS
# Tuning # Tuning
ret.longitudinalTuning.kiV = [2.4, 1.5] ret.longitudinalTuning.kiV = [0.5, 0.5, 0.5]
if ret.enableGasInterceptor: if ret.enableGasInterceptor:
# Need to set ASCM long limits when using pedal interceptor, instead of camera ACC long limits # Need to set ASCM long limits when using pedal interceptor, instead of camera ACC long limits
@@ -222,11 +241,17 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) 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: if ret.enableGasInterceptor:
# ACC Bolts use pedal for full longitudinal control, not just sng # ACC Bolts use pedal for full longitudinal control, not just sng
ret.flags |= GMFlags.PEDAL_LONG.value ret.flags |= GMFlags.PEDAL_LONG.value
elif candidate == CAR.CHEVROLET_SILVERADO: if candidate == CAR.CHEVROLET_SILVERADO:
# On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop # 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. # 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 # TODO: check if this is split by EV/ICE with more platforms in the future
@@ -286,13 +311,13 @@ class CarInterface(CarInterfaceBase):
ret.stoppingControl = True ret.stoppingControl = True
ret.autoResumeSng = True ret.autoResumeSng = True
if candidate in CC_ONLY_CAR: if candidate in CC_ONLY_CAR: #pedal interceptor tuning
ret.flags |= GMFlags.PEDAL_LONG.value ret.flags |= GMFlags.PEDAL_LONG.value
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_PEDAL_LONG ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_PEDAL_LONG
# Note: Low speed, stop and go not tested. Should be fairly smooth on highway # Note: Low speed, stop and go not tested. Should be fairly smooth on highway
ret.longitudinalTuning.kiBP = [0.0, 5., 35.] ret.longitudinalTuning.kiBP = [0., 3., 6., 35.]
ret.longitudinalTuning.kiV = [0.0, 0.35, 0.5] ret.longitudinalTuning.kiV = [0.125, 0.175, 0.225, 0.33]
ret.longitudinalTuning.kfDEPRECATED = 0.15 ret.longitudinalTuning.kfDEPRECATED = 0.25
ret.stoppingDecelRate = 0.8 ret.stoppingDecelRate = 0.8
else: # Pedal used for SNG, ACC for longitudinal control otherwise else: # Pedal used for SNG, ACC for longitudinal control otherwise
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
@@ -309,16 +334,15 @@ class CarInterface(CarInterfaceBase):
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
ret.pcmCruise = False ret.pcmCruise = False
ret.stoppingDecelRate = 11.18 # == 25 mph/s (.04 rate) 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.deadzoneBP = [0.] ret.longitudinalTuning.kpV = [0., 5., 2.] # set lower end to 0 since we can't drive below that speed
ret.longitudinalTuning.deadzoneV = [0.56] # == 2 km/h/s, 1.25 mph/s ret.longitudinalTuning.deadzoneBP = [0., 1.]
ret.longitudinalActuatorDelay = 1. # TODO: measure this ret.longitudinalTuning.deadzoneV = [0.9, 0.9] # == 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.kiBP = [0.]
ret.longitudinalTuning.kpV = [0., 20., 20.] # set lower end to 0 since we can't drive below that speed ret.longitudinalTuning.kiV = [0.1]
ret.longitudinalTuning.kiBP = [0.] ret.stoppingDecelRate = 11.18 # == 25 mph/s (.04 rate)
ret.longitudinalTuning.kiV = [0.1]
if candidate in CC_ONLY_CAR: if candidate in CC_ONLY_CAR:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_ACC ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_ACC
+28 -14
View File
@@ -33,41 +33,53 @@ class CarControllerParams:
# Our controller should still keep the 2 second average above # Our controller should still keep the 2 second average above
# -3.5 m/s^2 as per planner limits # -3.5 m/s^2 as per planner limits
ACCEL_MAX = 2. # m/s^2 ACCEL_MAX = 2. # m/s^2
ACCEL_MAX_PLUS = 4. # m/s^2
ACCEL_MIN = -4. # m/s^2 ACCEL_MIN = -4. # m/s^2
def __init__(self, CP): def __init__(self, CP):
# Gas/brake lookups # Gas/brake lookups
self.ZERO_GAS = 2048 # Coasting self.ZERO_GAS = 6150 # Coasting
self.MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen 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: if CP.carFingerprint in CAMERA_ACC_CAR and CP.carFingerprint not in CC_ONLY_CAR:
self.MAX_GAS = 3400 self.MAX_GAS = 8848
self.MAX_ACC_REGEN = 1514 self.MAX_GAS_PLUS = 8848
self.INACTIVE_REGEN = 1554 self.MAX_ACC_REGEN = 5610
self.INACTIVE_REGEN = 5650
# Camera ACC vehicles have no regen while enabled. # Camera ACC vehicles have no regen while enabled.
# Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly # Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly
max_regen_acceleration = 0. 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: elif CP.carFingerprint in SDGM_CAR:
self.MAX_GAS = 3400 self.MAX_GAS = 7496
self.MAX_ACC_REGEN = 1514 self.MAX_GAS_PLUS = 7496
self.INACTIVE_REGEN = 1554 self.MAX_ACC_REGEN = 5610
self.INACTIVE_REGEN = 5650
max_regen_acceleration = 0. max_regen_acceleration = 0.
self.BRAKE_SWITCH = self.ZERO_GAS
else: else:
self.MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill. self.MAX_GAS = 8191 # Safety limit, not ACC max. Stock ACC >8192 from standstill.
self.MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen self.MAX_GAS_PLUS = 8191 # 8292 uses new bit, possible but not tested. Matches Twilsonco tw-main max
self.INACTIVE_REGEN = 1404 self.MAX_ACC_REGEN = 7110 # Max ACC regen is slightly less than max paddle regen
self.INACTIVE_REGEN = 5500
# ICE has much less engine braking force compared to regen in EVs, # ICE has much less engine braking force compared to regen in EVs,
# lower threshold removes some braking deadzone # lower threshold removes some braking deadzone
max_regen_acceleration = -1. if CP.carFingerprint in EV_CAR else -0.1 max_regen_acceleration = -3. if CP.carFingerprint in EV_CAR else -0.1
self.BRAKE_SWITCH_MAX = self.MAX_ACC_REGEN if CP.carFingerprint in EV_CAR else self.ZERO_GAS
self.GAS_LOOKUP_BP = [max_regen_acceleration, 0., self.ACCEL_MAX] 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 = [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, max_regen_acceleration] self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, 0.]
self.BRAKE_LOOKUP_V = [self.MAX_BRAKE, 0.] self.BRAKE_LOOKUP_V = [self.MAX_BRAKE, 0.]
self.BRAKE_SWITCH_LOOKUP_BP = [0.5, 10]
self.BRAKE_SWITCH_LOOKUP_V = [self.ZERO_GAS, self.BRAKE_SWITCH_MAX]
# determined by letting Volt regen to a stop in L gear from 89mph, # 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 # and by letting off gas and allowing car to creep, for determining
# the positive threshold values at very low speed # the positive threshold values at very low speed
@@ -76,10 +88,11 @@ class CarControllerParams:
def update_ev_gas_brake_threshold(self, v_ego): 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) 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 = [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] self.EV_BRAKE_LOOKUP_BP = [self.ACCEL_MIN, gas_brake_threshold]
@dataclass @dataclass
class GMCarDocs(CarDocs): class GMCarDocs(CarDocs):
package: str = "Adaptive Cruise Control (ACC)" package: str = "Adaptive Cruise Control (ACC)"
@@ -162,7 +175,7 @@ class CAR(Platforms):
GMCarDocs("Chevrolet Silverado 1500 2020-21", "Safety Package II"), 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"), GMCarDocs("GMC Sierra 1500 2020-21", "Driver Alert Package II", video_link="https://youtu.be/5HbNoBLzRwE"),
], ],
GMCarSpecs(mass=2450, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0), GMCarSpecs(mass=2994, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0),
) )
CHEVROLET_EQUINOX = GMPlatformConfig( CHEVROLET_EQUINOX = GMPlatformConfig(
[GMCarDocs("Chevrolet Equinox 2019-22")], [GMCarDocs("Chevrolet Equinox 2019-22")],
@@ -318,6 +331,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
EV_CAR = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC} 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 = {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_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 # We're integrated at the Safety Data Gateway Module on these cars
SDGM_CAR = {CAR.CADILLAC_XT4, CAR.CHEVROLET_TRAVERSE, CAR.BUICK_BABYENCLAVE} SDGM_CAR = {CAR.CADILLAC_XT4, CAR.CHEVROLET_TRAVERSE, CAR.BUICK_BABYENCLAVE}
+5 -5
View File
@@ -286,11 +286,11 @@ class CarController(CarControllerBase):
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
new_actuators = actuators.as_builder() new_actuators = actuators.as_builder()
new_actuators.speed = float(self.speed) new_actuators.speed = self.speed
new_actuators.accel = float(self.accel) new_actuators.accel = self.accel
new_actuators.gas = float(self.gas) new_actuators.gas = self.gas
new_actuators.brake = float(self.brake) new_actuators.brake = self.brake
new_actuators.steer = float(self.last_steer) new_actuators.steer = self.last_steer
new_actuators.steerOutputCan = apply_steer new_actuators.steerOutputCan = apply_steer
self.frame += 1 self.frame += 1
+3 -3
View File
@@ -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) return packer.make_can_msg("BRAKE_COMMAND", CAN.pt, values)
def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint, gas_force): def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint):
commands = [] commands = []
min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0] min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0]
control_on = 5 if enabled else 0 control_on = 5 if enabled else 0
gas_command = gas if active and gas_force > min_gas_accel else -30000 gas_command = gas if active and accel > min_gas_accel else -30000
accel_command = accel if active else 0 accel_command = accel if active else 0
braking = 1 if active and gas_force < min_gas_accel else 0 braking = 1 if active and accel < min_gas_accel else 0
standstill = 1 if active and stopping_counter > 0 else 0 standstill = 1 if active and stopping_counter > 0 else 0
standstill_release = 1 if active and stopping_counter == 0 else 0 standstill_release = 1 if active and stopping_counter == 0 else 0
+4 -7
View File
@@ -85,10 +85,10 @@ class CarInterface(CarInterfaceBase):
ret.longitudinalActuatorDelay = 0.5 # s ret.longitudinalActuatorDelay = 0.5 # s
if candidate in HONDA_BOSCH_RADARLESS: 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 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 # default longitudinal tuning for all hondas
ret.longitudinalTuning.kiBP = [0., 5., 35.] ret.longitudinalTuning.kiBP = [0., 5., 35.]
ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5] ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5]
eps_modified = False eps_modified = False
for fw in car_fw: for fw in car_fw:
@@ -117,9 +117,6 @@ 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.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]] 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: 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 ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
+3 -3
View File
@@ -37,12 +37,12 @@ EventName = car.CarEvent.EventName
MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS
ACCEL_MAX = 2.0 ACCEL_MAX = 2.0
ACCEL_MIN = -3.5 ACCEL_MIN = -3.5
FRICTION_THRESHOLD = 0.09 FRICTION_THRESHOLD = 0.12
def get_friction_threshold(v_ego): def get_friction_threshold(v_ego):
# Interpolate friction threshold from 0.09 at 50 mph to 0.15 at 75 mph # Interpolate friction threshold
from openpilot.common.numpy_fast import interp from openpilot.common.numpy_fast import interp
return interp(v_ego, [1 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.12, 0.25]) return interp(v_ego, [1 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.12, 0.3])
TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.toml') 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') TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.toml')
+1 -1
View File
@@ -43,7 +43,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"CADILLAC_ESCALADE" = [1.899999976158142, 1.842270016670227, 0.1120000034570694] "CADILLAC_ESCALADE" = [1.899999976158142, 1.842270016670227, 0.1120000034570694]
"CADILLAC_ESCALADE_ESV_2019" = [1.15, 1.3, 0.2] "CADILLAC_ESCALADE_ESV_2019" = [1.15, 1.3, 0.2]
"CADILLAC_XT4" = [1.45, 1.6, 0.2] "CADILLAC_XT4" = [1.45, 1.6, 0.2]
"CHEVROLET_BOLT_EUV" = [2.0, 2.0, 0.05] "CHEVROLET_BOLT_EUV" = [2.0, 2.0, 0.09]
"CHEVROLET_MALIBU_CC" = [1.85, 1.85, 0.075] "CHEVROLET_MALIBU_CC" = [1.85, 1.85, 0.075]
"CHEVROLET_SILVERADO" = [1.9, 1.9, 0.112] "CHEVROLET_SILVERADO" = [1.9, 1.9, 0.112]
"CHEVROLET_TRAILBLAZER" = [1.33, 1.9, 0.16] "CHEVROLET_TRAILBLAZER" = [1.33, 1.9, 0.16]
+1 -1
View File
@@ -199,7 +199,7 @@ class Controls:
self.event_names_to_clear = set() self.event_names_to_clear = set()
self.has_menu = self.CP.carName == "gm" and not (self.CP.flags & GMFlags.NO_CAMERA.value or self.CP.carFingerprint in CC_ONLY_CAR) self.has_menu = self.CP.carName == "gm" and not (self.CP.flags & GMFlags.NO_CAMERA.value)
self.frogpilot_AM = AlertManager() self.frogpilot_AM = AlertManager()
self.frogpilot_events = Events(frogpilot=True) self.frogpilot_events = Events(frogpilot=True)
+1 -1
View File
@@ -982,7 +982,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
"", "",
AlertStatus.normal, AlertSize.full, AlertStatus.normal, AlertSize.full,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5), Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5),
ET.USER_DISABLE: ImmediateDisableAlert("Reverse Gear"), ET.USER_DISABLE: ImmediateDisableAlert("Wrong Gear"),
ET.NO_ENTRY: NoEntryAlert("Reverse Gear"), ET.NO_ENTRY: NoEntryAlert("Reverse Gear"),
}, },
+49 -3
View File
@@ -7,6 +7,7 @@ from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD, get_friction_
from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED, get_friction from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED, get_friction
from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED 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.pid import PIDController
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
@@ -21,8 +22,8 @@ from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_G
# Additionally, there is friction in the steering wheel that needs # Additionally, there is friction in the steering wheel that needs
# to be overcome to move it at all, this is compensated for too. # to be overcome to move it at all, this is compensated for too.
KP = 0.6 KP = 0.7
KI = 0.3 KI = 0.35
INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30] 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] KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP]
@@ -36,6 +37,13 @@ JERK_LOOKAHEAD_SECONDS = 0.19
JERK_GAIN = 0.22 JERK_GAIN = 0.22
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0 LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
VERSION = 2 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): class LatControlTorque(LatControl):
def __init__(self, CP, CI, dt): def __init__(self, CP, CI, dt):
@@ -55,6 +63,21 @@ class LatControlTorque(LatControl):
self.previous_measurement = 0.0 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.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.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): def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction):
self.torque_params.latAccelFactor = latAccelFactor self.torque_params.latAccelFactor = latAccelFactor
@@ -76,6 +99,7 @@ class LatControlTorque(LatControl):
self.previous_measurement = 0.0 self.previous_measurement = 0.0
self.measurement_rate_filter.x = 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.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: else:
if self.prev_steering_pressed and not CS.steeringPressed: if self.prev_steering_pressed and not CS.steeringPressed:
self.pid.i *= self.steer_release_i_decay self.pid.i *= self.steer_release_i_decay
@@ -94,6 +118,10 @@ class LatControlTorque(LatControl):
desired_lateral_jerk = np.clip(self.jerk_filter.update(raw_lateral_jerk), -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP) 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 gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
setpoint = expected_lateral_accel + desired_lateral_jerk * lat_delay 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 = measured_curvature * CS.vEgo ** 2
measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt) measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt)
@@ -110,11 +138,23 @@ class LatControlTorque(LatControl):
ff = gravity_adjusted_future_lateral_accel ff = gravity_adjusted_future_lateral_accel
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll # latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
ff -= self.torque_params.latAccelOffset 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
ff += get_friction(error_with_lsf + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, get_friction_threshold(CS.vEgo), self.torque_params) 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 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
if CS.vEgo < self.low_speed_reset_threshold: if CS.vEgo < self.low_speed_reset_threshold:
self.pid.reset() self.pid.reset()
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < self.low_speed_reset_threshold freeze_integrator = (steer_limited_by_safety or CS.steeringPressed or
CS.vEgo < self.low_speed_reset_threshold or unwind_detected)
output_lataccel = self.pid.update(pid_log.error, error_rate=-measurement_rate, speed=CS.vEgo, feedforward=ff, freeze_integrator=freeze_integrator) 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) output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
@@ -129,6 +169,12 @@ class LatControlTorque(LatControl):
pid_log.desiredLateralJerk = float(desired_lateral_jerk) 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)) 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 self.prev_steering_pressed = CS.steeringPressed
# TODO left is positive in this convention # 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 from openpilot.common.swaglog import cloudlog
LON_MPC_STEP = 0.2 # first step is 0.2s LON_MPC_STEP = 0.2 # first step is 0.2s
A_CRUISE_MIN = -1.2 A_CRUISE_MIN = -1.0
A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6] A_CRUISE_MAX_BP = [0.0, 5., 10., 15., 20., 25., 40.]
A_CRUISE_MAX_BP = [0., 10.0, 25., 40.] A_CRUISE_MAX_VALS = [1.125, 1.125, 1.125, 1.125, 1.25, 1.25, 1.5]
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N] CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
ALLOW_THROTTLE_THRESHOLD = 0.5 ALLOW_THROTTLE_THRESHOLD = 0.5
MIN_ALLOW_THROTTLE_SPEED = 2.5 MIN_ALLOW_THROTTLE_SPEED = 2.5