Compare commits

...

109 Commits

Author SHA1 Message Date
firestar5683 259ac19499 Honda 2026-02-17 21:02:15 -06:00
firestar5683 1a77897c3a no sascm
Revert "no sascm"

This reverts commit 147e752007c6a4f80eec551934bc1cf86510e6dc.

Update interface.py
2026-02-17 20:49:33 -06:00
firestar5683 b5d39c4bc1 Truck Tuning 2026-02-17 19:57:35 -06:00
firestar5683 d4d78c9435 Update frogpilot_acceleration.py 2026-02-17 12:28:29 -06:00
firestar5683 2f29d7ee04 EV Tuning 2026-02-16 23:43:46 -06:00
firestar5683 338a07973b Truck Tuning 2026-02-16 23:39:45 -06:00
firestar5683 f4da6a3e3b silverado 2026-02-16 17:28:35 -06:00
firestar5683 5fbaebc91c brake mismatches 2026-02-16 13:25:23 -06:00
firestarsdog 7dbdb33ed1 Gen2ACC Distance Button Sync for CC_ONLY_CAR 2026-02-15 23:33:15 -06:00
firestar5683 a1cd4954b1 cancel acc bolt with pedal 2026-02-15 23:15:48 -06:00
firestar5683 2bbffc2a38 cruisestatus acc pedal 2026-02-14 22:44:33 -06:00
firestar5683 4d7aee4935 pedal 2026-02-14 15:58:32 -06:00
firestarsdog 41b055d140 Update values.py 2026-02-14 16:24:10 -05:00
firestarsdog bec0c04334 Evil +? 2026-02-14 13:02:22 -05:00
firestar5683 a8499d20fc Separate Non-ACC vs ACC + Pedal 2026-02-13 23:48:41 -06:00
firestar5683 6d0c17f43a Holiday 2026-02-13 22:45:30 -06:00
firestar5683 6dec58237b Fingerprinting 2026-02-13 22:35:10 -06:00
firestarsdog 6a47be1631 Hondadays 2026-02-13 21:38:01 -05:00
firestarsdog 8ea080b4cb Honda attempt from bugsink 2026-02-13 15:35:17 -05:00
firestarsdog a51503f2e4 BugSink Catch - human_lane_changes 2026-02-13 11:16:12 -05:00
firestar5683 07c722553d The Great Merge 2026-02-13 01:52:05 -06:00
firestar5683 14b1793c79 User adjustable offsets 2026-02-07 23:32:26 -06:00
firestar5683 055c223ded Integrator Smooth On Handoff 2026-02-06 15:46:56 -06:00
firestar5683 4d406112f7 Lights 2026-02-05 22:39:42 -06:00
firestar5683 e03f28bba0 New Models 2026-02-05 15:05:18 -06:00
firestar5683 f0cc963c0e Revert "merry christmas"
This reverts commit 8a9d0e5e6a.
2026-02-05 13:33:40 -06:00
firestar5683 cd39ce0305 Increase Fault Resilience 2026-02-05 13:15:39 -06:00
firestarsdog 749b5e06fb Stats 2026-02-02 01:04:19 -05:00
firestarsdog 23121cca0e Stats 2026-02-01 22:46:13 -06:00
firestar5683 6f0502e862 friction threshold 2026-01-29 11:28:30 -06:00
firestar5683 04c59fb18e Lat Updates 2026-01-28 23:51:36 -06:00
firestar5683 844dce2030 Update interface.py 2026-01-20 22:08:07 -06:00
firestar5683 aa77db1fc9 Update override.toml 2026-01-19 21:18:53 -06:00
firestar5683 bee9b2b046 Revert "Mac Update"
This reverts commit 1b5d776464.
2026-01-19 11:43:31 -06:00
firestarsdog 469cd5e528 Add SASCM to vehicle settings detection/stats 2026-01-19 10:32:21 -06:00
firestar5683 23f52644ae Update interface.py 2026-01-18 23:45:59 -06:00
firestar5683 e76b82603b Update interface.py 2026-01-18 23:44:42 -06:00
firestar5683 1b5d776464 Mac Update 2026-01-18 22:22:06 -06:00
firestarsdog 61f4e8b142 Stats 2026-01-18 22:17:42 -06:00
firestar5683 f0fefb6e13 Torque Controller Update 2026-01-18 21:58:43 -06:00
firestar5683 25a7090c3d Update redneck 2026-01-15 22:54:15 -06:00
firestar5683 b0de4d64b4 Split Tune 2026-01-15 19:17:55 -06:00
firestar5683 77ae5b176a fix redneck v2 2026-01-14 13:58:22 -06:00
firestar5683 89de28682c Remove lat smooth seconds 2026-01-14 13:49:35 -06:00
firestar5683 a1d4dcb9a0 frogpilot migration 2026-01-12 22:10:40 -06:00
firestar5683 c8deae75b7 More defaults 2026-01-12 22:04:19 -06:00
firestar5683 8653fb41f2 Update defaults 2026-01-12 22:00:04 -06:00
firestar5683 c064197fde Big Mac 2026-01-11 16:58:25 -06:00
firestar5683 7f878ec1dc Try Higher Friction 2026-01-10 14:04:32 -06:00
firestar5683 140bdd59fb Autotune Off 2026-01-09 22:40:45 -06:00
firestar5683 6f728f935a Revert "Pedal Braking Limits"
This reverts commit 568b72095f.
2025-12-31 20:55:13 -06:00
firestar5683 568b72095f Pedal Braking Limits 2025-12-30 20:17:59 -06:00
firestar5683 0aaf4d1336 merry christmas 2025-12-24 22:04:58 -06:00
firestar5683 bb3f0a3354 ds2 2025-12-24 21:28:40 -06:00
firestar5683 f2aba355b9 Try friction adjustment 2025-12-16 14:38:29 -06:00
firestar5683 e6513efb9b Update frogpilot_tracking.py 2025-12-15 00:55:51 -06:00
firestar5683 872a2c58aa minsteer speed 2025-12-14 14:46:27 -06:00
firestar5683 1630560715 Update Percentages 2025-12-13 19:13:44 -06:00
firestar5683 4cfd893837 Trailer Load Gas Tuning 2025-12-12 10:50:11 -06:00
firestar5683 0e93ceef00 Live Friction 2025-12-11 16:54:11 -06:00
firestar5683 d626f0fe49 Zero error 2025-12-11 15:12:29 -06:00
firestar5683 8dae3d375c Updates
Update latcontrol_torque.py

interp friction threshold
2025-12-09 19:58:46 -06:00
firestar5683 721d39ef1f Change NoEntryAlert message to 'Reverse Gear' 2025-12-06 16:44:30 -06:00
firestar5683 a97c5742b7 Update events.py 2025-12-05 20:13:49 -06:00
firestar5683 488f821983 LattyBoi2.0 2025-12-03 23:30:35 -06:00
firestar5683 190dc98180 Latty Boi
Revert "Latty Boi"

This reverts commit af687e501cc4bdcda7840453d06595d7ea674148.

Reapply "Latty Boi"

This reverts commit ff5566d4439f5e4997fe83f4f21d0be62b75d75d.
2025-12-02 20:29:48 -06:00
firestar5683 0b459d9f0b Recovery Power 2025-12-02 17:18:43 -06:00
firestar5683 3cd594bbe4 New planplus 2025-12-02 12:09:05 -06:00
Woohyun Rho f31082ce2b Update 2025-11-28 17:10:45 -06:00
firestar5683 b51d7ae35d Update interface.py 2025-11-18 20:48:48 -06:00
firestar5683 884fcb8315 New Lateral Changes 2025-11-18 20:48:21 -06:00
firestar5683 77597e60b2 Update 2025-11-15 14:44:07 -06:00
firestar5683 f2fbc8f30b Accel Tests 2025-11-14 20:32:47 -06:00
firestar5683 fffea614a4 Paddle Planner 2025-11-14 01:26:06 -06:00
firestar5683 e7e593cdb2 Revert "Upstream Lateral"
This reverts commit 97f8736605.
2025-11-10 23:07:02 -06:00
firestar5683 97f8736605 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:13:34 -06:00
firestar5683 48efc5b97a Update manager.py 2025-10-27 00:58:28 -05:00
firestar5683 4cca6553e4 Update 2025-10-25 15:35:55 -05:00
firestar5683 6eda52b562 Medium Fanta 2025-10-22 22:18:08 -05:00
firestar5683 de9cbc5de6 Update interfaces.py 2025-10-20 13:49:08 -05:00
firestar5683 fee241dc0b no nnff 2025-10-19 17:59:36 -05:00
firestar5683 2094be9a7a Scene Complexity 2025-10-18 14:24:48 -05:00
firestar5683 6ce476025d Update carcontroller.py 2025-10-17 19:10:41 -05:00
firestar5683 dd6c62dbf6 Update interface.py 2025-10-17 18:13:24 -05:00
firestar5683 2ab19dbd4c CEM 2025-10-17 17:39:06 -05:00
firestar5683 3241fbdd8c lite 2025-10-17 17:13:37 -05:00
firestar5683 1f0350e66a Redneck 2.0 2025-10-17 16:55:56 -05:00
firestar5683 ce46776439 Modify torque tuning parameters in interfaces.py
Adjusted torque tuning parameters for improved performance.
2025-10-17 16:53:54 -05:00
firestar5683 afb209a1a8 Hurts Donut 2025-10-16 23:57:08 -05:00
firestar5683 b4e29d4ed6 Smoothy Boi 2025-10-16 23:35:26 -05:00
firestar5683 dada0f2ae6 New change 2025-10-15 22:59:04 -05:00
firestar5683 4532e7f5d1 lat3 2025-10-15 22:20:35 -05:00
firestar5683 78de442ee1 New Lateral Changes 2025-10-10 20:59:23 -05:00
firestar5683 953b343e06 Fix Standard 2025-10-10 19:24:24 -05:00
firestar5683 5b26be2e3c error? 2025-10-08 23:13:32 -05:00
firestar5683 4abe50405c Update interface.py 2025-10-08 23:07:37 -05:00
firestar5683 f2e705413c Fix New Devices 2025-10-08 22:19:43 -05:00
firestar5683 da56f26105 No positive P-response for long control if user-selected parameter set 2025-10-08 07:42:32 -05:00
firestar5683 ab39266180 Automatic updates 2025-10-04 16:04:35 -05:00
firestar5683 dac85dfd3a Steer Alerts 2025-10-04 01:49:22 -05:00
firestar5683 7e539cde38 Update frogpilot_acceleration.py 2025-10-03 22:42:08 -05:00
firestar5683 16a79fbc3d SteerAlerts
Revert "SteerAlerts"

This reverts commit cbaba399c5b9caac5a8faf3917f0929d747a0acc.

Update controlsd.py
2025-10-03 22:14:25 -05:00
firestar5683 9cdf0120cf Donut DM 2025-10-03 20:50:00 -05:00
firestar5683 aa0f0aaac8 Update gm_global_a_powertrain_generated.dbc 2025-10-03 16:41:22 -05:00
firestar5683 aefaafb616 Update 2025-10-03 00:43:40 -05:00
firestar5683 388f63168f firehose 2025-09-30 20:45:47 -05:00
firestar5683 7fe40f6e7f Update 2025-09-30 14:30:04 -05:00
firestar5683 eb2218e733 Dom 2025-09-30 09:25:14 -05:00
firestar5683 aa9364a311 Tinygrad 2025-09-30 08:56:38 -05:00
706 changed files with 282439 additions and 21107 deletions
+3 -2
View File
@@ -532,11 +532,12 @@ struct CarParams {
useSteeringAngle @0 :Bool;
kp @1 :Float32;
ki @2 :Float32;
kd @8 : Float32;
friction @3 :Float32;
kf @4 :Float32;
steeringAngleDeadzoneDeg @5 :Float32;
latAccelFactor @6 :Float32;
latAccelOffset @7 :Float32;
kfDEPRECATED @4 :Float32;
}
struct LongitudinalPIDTuning {
@@ -544,7 +545,7 @@ struct CarParams {
kpV @1 :List(Float32);
kiBP @2 :List(Float32);
kiV @3 :List(Float32);
kf @6 :Float32;
kfDEPRECATED @6 :Float32;
deadzoneBP @4 :List(Float32);
deadzoneV @5 :List(Float32);
}
+30 -33
View File
@@ -105,11 +105,7 @@ struct FrogPilotCarParams @0xf35cc4560bbf6ec2 {
isHDA2 @3 :Bool;
openpilotLongitudinalControlDisabled @4 :Bool;
safetyConfigs @5 :List(SafetyConfig);
lateralTuning :union {
pid @6 :Car.CarParams.LateralPIDTuning;
torque @7 :Car.CarParams.LateralTorqueTuning;
}
canUseSASCM @6 :Bool;
struct SafetyConfig {
safetyParam @0 :UInt16;
@@ -193,34 +189,35 @@ struct FrogPilotPlan @0xa1680744031fdb2d {
cscTraining @4 :Bool;
dangerJerk @5 :Float32;
desiredFollowDistance @6 :Int64;
experimentalMode @7 :Bool;
forcingStop @8 :Bool;
forcingStopLength @9 :Float32;
frogpilotEvents @10 :List(FrogPilotCarEvent);
increasedStoppedDistance @11 :Float32;
lateralCheck @12 :Bool;
laneWidthLeft @13 :Float32;
laneWidthRight @14 :Float32;
maxAcceleration @15 :Float32;
minAcceleration @16 :Float32;
redLight @17 :Bool;
roadCurvature @18 :Float32;
slcMapSpeedLimit @19 :Float32;
slcMapboxSpeedLimit @20 :Float32;
slcNextSpeedLimit @21 :Float32;
slcOverriddenSpeed @22 :Float32;
slcSpeedLimit @23 :Float32;
slcSpeedLimitOffset @24 :Float32;
slcSpeedLimitSource @25 :Text;
speedJerk @26 :Float32;
speedJerkStock @27 :Float32;
speedLimitChanged @28 :Bool;
tFollow @29 :Float32;
themeUpdated @30 :Bool;
togglesUpdated @31 :Bool;
trackingLead @32 :Bool;
unconfirmedSlcSpeedLimit @33 :Float32;
vCruise @34 :Float32;
disableThrottle @7 :Bool;
experimentalMode @8 :Bool;
forcingStop @9 :Bool;
forcingStopLength @10 :Float32;
frogpilotEvents @11 :List(FrogPilotCarEvent);
increasedStoppedDistance @12 :Float32;
lateralCheck @13 :Bool;
laneWidthLeft @14 :Float32;
laneWidthRight @15 :Float32;
maxAcceleration @16 :Float32;
minAcceleration @17 :Float32;
redLight @18 :Bool;
roadCurvature @19 :Float32;
slcMapSpeedLimit @20 :Float32;
slcMapboxSpeedLimit @21 :Float32;
slcNextSpeedLimit @22 :Float32;
slcOverriddenSpeed @23 :Float32;
slcSpeedLimit @24 :Float32;
slcSpeedLimitOffset @25 :Float32;
slcSpeedLimitSource @26 :Text;
speedJerk @27 :Float32;
speedJerkStock @28 :Float32;
speedLimitChanged @29 :Bool;
tFollow @30 :Float32;
themeUpdated @31 :Bool;
togglesUpdated @32 :Bool;
trackingLead @33 :Bool;
unconfirmedSlcSpeedLimit @34 :Float32;
vCruise @35 :Float32;
}
struct FrogPilotRadarState @0xcb9fd56c7057593a {
+66 -49
View File
@@ -5262,17 +5262,17 @@ const ::capnp::_::RawSchema s_9622723fcbd14c2e = {
0, 5, i_9622723fcbd14c2e, nullptr, nullptr, { &s_9622723fcbd14c2e, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<147> b_80366e0e804ecc1d = {
static const ::capnp::_::AlignedData<163> b_80366e0e804ecc1d = {
{ 0, 0, 0, 0, 5, 0, 6, 0,
29, 204, 78, 128, 14, 110, 54, 128,
20, 0, 0, 0, 1, 0, 4, 0,
20, 0, 0, 0, 1, 0, 5, 0,
218, 169, 170, 144, 36, 55, 105, 140,
0, 0, 7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
21, 0, 0, 0, 66, 1, 0, 0,
37, 0, 0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
33, 0, 0, 0, 199, 1, 0, 0,
33, 0, 0, 0, 255, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
99, 97, 114, 46, 99, 97, 112, 110,
@@ -5281,63 +5281,70 @@ static const ::capnp::_::AlignedData<147> b_80366e0e804ecc1d = {
114, 97, 108, 84, 111, 114, 113, 117,
101, 84, 117, 110, 105, 110, 103, 0,
0, 0, 0, 0, 1, 0, 1, 0,
32, 0, 0, 0, 3, 0, 4, 0,
36, 0, 0, 0, 3, 0, 4, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
209, 0, 0, 0, 138, 0, 0, 0,
237, 0, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
212, 0, 0, 0, 3, 0, 1, 0,
224, 0, 0, 0, 2, 0, 1, 0,
240, 0, 0, 0, 3, 0, 1, 0,
252, 0, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
221, 0, 0, 0, 26, 0, 0, 0,
249, 0, 0, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
216, 0, 0, 0, 3, 0, 1, 0,
228, 0, 0, 0, 2, 0, 1, 0,
244, 0, 0, 0, 3, 0, 1, 0,
0, 1, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
225, 0, 0, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
220, 0, 0, 0, 3, 0, 1, 0,
232, 0, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
229, 0, 0, 0, 74, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
228, 0, 0, 0, 3, 0, 1, 0,
240, 0, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
237, 0, 0, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
232, 0, 0, 0, 3, 0, 1, 0,
244, 0, 0, 0, 2, 0, 1, 0,
5, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
241, 0, 0, 0, 202, 0, 0, 0,
253, 0, 0, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
248, 0, 0, 0, 3, 0, 1, 0,
4, 1, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
4, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 122, 0, 0, 0,
1, 1, 0, 0, 74, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 3, 0, 1, 0,
12, 1, 0, 0, 2, 0, 1, 0,
7, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
8, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
9, 1, 0, 0, 122, 0, 0, 0,
9, 1, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
8, 1, 0, 0, 3, 0, 1, 0,
20, 1, 0, 0, 2, 0, 1, 0,
5, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
17, 1, 0, 0, 202, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
24, 1, 0, 0, 3, 0, 1, 0,
36, 1, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
33, 1, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
32, 1, 0, 0, 3, 0, 1, 0,
44, 1, 0, 0, 2, 0, 1, 0,
7, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
41, 1, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
40, 1, 0, 0, 3, 0, 1, 0,
52, 1, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
49, 1, 0, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
44, 1, 0, 0, 3, 0, 1, 0,
56, 1, 0, 0, 2, 0, 1, 0,
117, 115, 101, 83, 116, 101, 101, 114,
105, 110, 103, 65, 110, 103, 108, 101,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -5373,7 +5380,8 @@ static const ::capnp::_::AlignedData<147> b_80366e0e804ecc1d = {
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
107, 102, 0, 0, 0, 0, 0, 0,
107, 102, 68, 69, 80, 82, 69, 67,
65, 84, 69, 68, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -5403,6 +5411,14 @@ static const ::capnp::_::AlignedData<147> b_80366e0e804ecc1d = {
0, 0, 0, 0, 0, 0, 0, 0,
108, 97, 116, 65, 99, 99, 101, 108,
79, 102, 102, 115, 101, 116, 0, 0,
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
107, 100, 0, 0, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -5413,14 +5429,14 @@ static const ::capnp::_::AlignedData<147> b_80366e0e804ecc1d = {
};
::capnp::word const* const bp_80366e0e804ecc1d = b_80366e0e804ecc1d.words;
#if !CAPNP_LITE
static const uint16_t m_80366e0e804ecc1d[] = {3, 4, 2, 1, 6, 7, 5, 0};
static const uint16_t i_80366e0e804ecc1d[] = {0, 1, 2, 3, 4, 5, 6, 7};
static const uint16_t m_80366e0e804ecc1d[] = {3, 8, 4, 2, 1, 6, 7, 5, 0};
static const uint16_t i_80366e0e804ecc1d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
const ::capnp::_::RawSchema s_80366e0e804ecc1d = {
0x80366e0e804ecc1d, b_80366e0e804ecc1d.words, 147, nullptr, m_80366e0e804ecc1d,
0, 8, i_80366e0e804ecc1d, nullptr, nullptr, { &s_80366e0e804ecc1d, nullptr, nullptr, 0, 0, nullptr }, false
0x80366e0e804ecc1d, b_80366e0e804ecc1d.words, 163, nullptr, m_80366e0e804ecc1d,
0, 9, i_80366e0e804ecc1d, nullptr, nullptr, { &s_80366e0e804ecc1d, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<151> b_c342cefc303e9b8e = {
static const ::capnp::_::AlignedData<152> b_c342cefc303e9b8e = {
{ 0, 0, 0, 0, 5, 0, 6, 0,
142, 155, 62, 48, 252, 206, 66, 195,
20, 0, 0, 0, 1, 0, 1, 0,
@@ -5486,10 +5502,10 @@ static const ::capnp::_::AlignedData<151> b_c342cefc303e9b8e = {
4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
53, 1, 0, 0, 26, 0, 0, 0,
53, 1, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
48, 1, 0, 0, 3, 0, 1, 0,
60, 1, 0, 0, 2, 0, 1, 0,
52, 1, 0, 0, 3, 0, 1, 0,
64, 1, 0, 0, 2, 0, 1, 0,
107, 112, 66, 80, 0, 0, 0, 0,
14, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -5564,7 +5580,8 @@ static const ::capnp::_::AlignedData<151> b_c342cefc303e9b8e = {
14, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
107, 102, 0, 0, 0, 0, 0, 0,
107, 102, 68, 69, 80, 82, 69, 67,
65, 84, 69, 68, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -5578,7 +5595,7 @@ static const ::capnp::_::AlignedData<151> b_c342cefc303e9b8e = {
static const uint16_t m_c342cefc303e9b8e[] = {4, 5, 6, 2, 3, 0, 1};
static const uint16_t i_c342cefc303e9b8e[] = {0, 1, 2, 3, 4, 5, 6};
const ::capnp::_::RawSchema s_c342cefc303e9b8e = {
0xc342cefc303e9b8e, b_c342cefc303e9b8e.words, 151, nullptr, m_c342cefc303e9b8e,
0xc342cefc303e9b8e, b_c342cefc303e9b8e.words, 152, nullptr, m_c342cefc303e9b8e,
0, 7, i_c342cefc303e9b8e, nullptr, nullptr, { &s_c342cefc303e9b8e, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
+32 -13
View File
@@ -626,7 +626,7 @@ struct CarParams::LateralTorqueTuning {
class Pipeline;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(80366e0e804ecc1d, 4, 0)
CAPNP_DECLARE_STRUCT_HEADER(80366e0e804ecc1d, 5, 0)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
@@ -3140,7 +3140,7 @@ public:
inline float getFriction() const;
inline float getKf() const;
inline float getKfDEPRECATED() const;
inline float getSteeringAngleDeadzoneDeg() const;
@@ -3148,6 +3148,8 @@ public:
inline float getLatAccelOffset() const;
inline float getKd() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
@@ -3188,8 +3190,8 @@ public:
inline float getFriction();
inline void setFriction(float value);
inline float getKf();
inline void setKf(float value);
inline float getKfDEPRECATED();
inline void setKfDEPRECATED(float value);
inline float getSteeringAngleDeadzoneDeg();
inline void setSteeringAngleDeadzoneDeg(float value);
@@ -3200,6 +3202,9 @@ public:
inline float getLatAccelOffset();
inline void setLatAccelOffset(float value);
inline float getKd();
inline void setKd(float value);
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
@@ -3261,7 +3266,7 @@ public:
inline bool hasDeadzoneV() const;
inline ::capnp::List<float, ::capnp::Kind::PRIMITIVE>::Reader getDeadzoneV() const;
inline float getKf() const;
inline float getKfDEPRECATED() const;
private:
::capnp::_::StructReader _reader;
@@ -3339,8 +3344,8 @@ public:
inline void adoptDeadzoneV(::capnp::Orphan< ::capnp::List<float, ::capnp::Kind::PRIMITIVE>>&& value);
inline ::capnp::Orphan< ::capnp::List<float, ::capnp::Kind::PRIMITIVE>> disownDeadzoneV();
inline float getKf();
inline void setKf(float value);
inline float getKfDEPRECATED();
inline void setKfDEPRECATED(float value);
private:
::capnp::_::StructBuilder _builder;
@@ -7791,16 +7796,16 @@ inline void CarParams::LateralTorqueTuning::Builder::setFriction(float value) {
::capnp::bounded<3>() * ::capnp::ELEMENTS, value);
}
inline float CarParams::LateralTorqueTuning::Reader::getKf() const {
inline float CarParams::LateralTorqueTuning::Reader::getKfDEPRECATED() const {
return _reader.getDataField<float>(
::capnp::bounded<4>() * ::capnp::ELEMENTS);
}
inline float CarParams::LateralTorqueTuning::Builder::getKf() {
inline float CarParams::LateralTorqueTuning::Builder::getKfDEPRECATED() {
return _builder.getDataField<float>(
::capnp::bounded<4>() * ::capnp::ELEMENTS);
}
inline void CarParams::LateralTorqueTuning::Builder::setKf(float value) {
inline void CarParams::LateralTorqueTuning::Builder::setKfDEPRECATED(float value) {
_builder.setDataField<float>(
::capnp::bounded<4>() * ::capnp::ELEMENTS, value);
}
@@ -7847,6 +7852,20 @@ inline void CarParams::LateralTorqueTuning::Builder::setLatAccelOffset(float val
::capnp::bounded<7>() * ::capnp::ELEMENTS, value);
}
inline float CarParams::LateralTorqueTuning::Reader::getKd() const {
return _reader.getDataField<float>(
::capnp::bounded<8>() * ::capnp::ELEMENTS);
}
inline float CarParams::LateralTorqueTuning::Builder::getKd() {
return _builder.getDataField<float>(
::capnp::bounded<8>() * ::capnp::ELEMENTS);
}
inline void CarParams::LateralTorqueTuning::Builder::setKd(float value) {
_builder.setDataField<float>(
::capnp::bounded<8>() * ::capnp::ELEMENTS, value);
}
inline bool CarParams::LongitudinalPIDTuning::Reader::hasKpBP() const {
return !_reader.getPointerField(
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
@@ -8075,16 +8094,16 @@ inline ::capnp::Orphan< ::capnp::List<float, ::capnp::Kind::PRIMITIVE>> CarPara
::capnp::bounded<5>() * ::capnp::POINTERS));
}
inline float CarParams::LongitudinalPIDTuning::Reader::getKf() const {
inline float CarParams::LongitudinalPIDTuning::Reader::getKfDEPRECATED() const {
return _reader.getDataField<float>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline float CarParams::LongitudinalPIDTuning::Builder::getKf() {
inline float CarParams::LongitudinalPIDTuning::Builder::getKfDEPRECATED() {
return _builder.getDataField<float>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
}
inline void CarParams::LongitudinalPIDTuning::Builder::setKf(float value) {
inline void CarParams::LongitudinalPIDTuning::Builder::setKfDEPRECATED(float value) {
_builder.setDataField<float>(
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
}
+197 -252
View File
@@ -642,12 +642,12 @@ const ::capnp::_::RawSchema s_aedffd8f31e7b55d = {
};
#endif // !CAPNP_LITE
CAPNP_DEFINE_ENUM(EventName_aedffd8f31e7b55d, aedffd8f31e7b55d);
static const ::capnp::_::AlignedData<132> b_f35cc4560bbf6ec2 = {
static const ::capnp::_::AlignedData<139> b_f35cc4560bbf6ec2 = {
{ 0, 0, 0, 0, 5, 0, 6, 0,
194, 110, 191, 11, 86, 196, 92, 243,
13, 0, 0, 0, 1, 0, 1, 0,
89, 10, 85, 29, 102, 186, 38, 181,
2, 0, 7, 0, 0, 0, 0, 0,
1, 0, 7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
21, 0, 0, 0, 2, 1, 0, 0,
33, 0, 0, 0, 23, 0, 0, 0,
@@ -707,13 +707,13 @@ static const ::capnp::_::AlignedData<132> b_f35cc4560bbf6ec2 = {
0, 0, 0, 0, 0, 0, 0, 0,
224, 0, 0, 0, 3, 0, 1, 0,
252, 0, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
73, 118, 234, 203, 210, 73, 201, 252,
249, 0, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
249, 0, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
248, 0, 0, 0, 3, 0, 1, 0,
4, 1, 0, 0, 2, 0, 1, 0,
99, 97, 110, 85, 115, 101, 80, 101,
100, 97, 108, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
@@ -773,20 +773,26 @@ static const ::capnp::_::AlignedData<132> b_f35cc4560bbf6ec2 = {
14, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
108, 97, 116, 101, 114, 97, 108, 84,
117, 110, 105, 110, 103, 0, 0, 0, }
99, 97, 110, 85, 115, 101, 83, 65,
83, 67, 77, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, }
};
::capnp::word const* const bp_f35cc4560bbf6ec2 = b_f35cc4560bbf6ec2.words;
#if !CAPNP_LITE
static const ::capnp::_::RawSchema* const d_f35cc4560bbf6ec2[] = {
&s_8d65dd40bad40951,
&s_fcc949d2cbea7649,
};
static const uint16_t m_f35cc4560bbf6ec2[] = {0, 1, 2, 3, 6, 4, 5};
static const uint16_t m_f35cc4560bbf6ec2[] = {0, 6, 1, 2, 3, 4, 5};
static const uint16_t i_f35cc4560bbf6ec2[] = {0, 1, 2, 3, 4, 5, 6};
const ::capnp::_::RawSchema s_f35cc4560bbf6ec2 = {
0xf35cc4560bbf6ec2, b_f35cc4560bbf6ec2.words, 132, d_f35cc4560bbf6ec2, m_f35cc4560bbf6ec2,
2, 7, i_f35cc4560bbf6ec2, nullptr, nullptr, { &s_f35cc4560bbf6ec2, nullptr, nullptr, 0, 0, nullptr }, false
0xf35cc4560bbf6ec2, b_f35cc4560bbf6ec2.words, 139, d_f35cc4560bbf6ec2, m_f35cc4560bbf6ec2,
1, 7, i_f35cc4560bbf6ec2, nullptr, nullptr, { &s_f35cc4560bbf6ec2, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<36> b_8d65dd40bad40951 = {
@@ -836,71 +842,6 @@ const ::capnp::_::RawSchema s_8d65dd40bad40951 = {
0, 1, i_8d65dd40bad40951, nullptr, nullptr, { &s_8d65dd40bad40951, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<49> b_fcc949d2cbea7649 = {
{ 0, 0, 0, 0, 5, 0, 6, 0,
73, 118, 234, 203, 210, 73, 201, 252,
32, 0, 0, 0, 1, 0, 1, 0,
194, 110, 191, 11, 86, 196, 92, 243,
2, 0, 7, 0, 1, 0, 2, 0,
1, 0, 0, 0, 0, 0, 0, 0,
21, 0, 0, 0, 114, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
33, 0, 0, 0, 119, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
99, 117, 115, 116, 111, 109, 46, 99,
97, 112, 110, 112, 58, 70, 114, 111,
103, 80, 105, 108, 111, 116, 67, 97,
114, 80, 97, 114, 97, 109, 115, 46,
108, 97, 116, 101, 114, 97, 108, 84,
117, 110, 105, 110, 103, 0, 0, 0,
8, 0, 0, 0, 3, 0, 4, 0,
0, 0, 255, 255, 1, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
41, 0, 0, 0, 34, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
36, 0, 0, 0, 3, 0, 1, 0,
48, 0, 0, 0, 2, 0, 1, 0,
1, 0, 254, 255, 1, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
45, 0, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
40, 0, 0, 0, 3, 0, 1, 0,
52, 0, 0, 0, 2, 0, 1, 0,
112, 105, 100, 0, 0, 0, 0, 0,
16, 0, 0, 0, 0, 0, 0, 0,
46, 76, 209, 203, 63, 114, 34, 150,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
116, 111, 114, 113, 117, 101, 0, 0,
16, 0, 0, 0, 0, 0, 0, 0,
29, 204, 78, 128, 14, 110, 54, 128,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, }
};
::capnp::word const* const bp_fcc949d2cbea7649 = b_fcc949d2cbea7649.words;
#if !CAPNP_LITE
static const ::capnp::_::RawSchema* const d_fcc949d2cbea7649[] = {
&s_80366e0e804ecc1d,
&s_9622723fcbd14c2e,
&s_f35cc4560bbf6ec2,
};
static const uint16_t m_fcc949d2cbea7649[] = {0, 1};
static const uint16_t i_fcc949d2cbea7649[] = {0, 1};
const ::capnp::_::RawSchema s_fcc949d2cbea7649 = {
0xfcc949d2cbea7649, b_fcc949d2cbea7649.words, 49, d_fcc949d2cbea7649, m_fcc949d2cbea7649,
3, 2, i_fcc949d2cbea7649, nullptr, nullptr, { &s_fcc949d2cbea7649, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<268> b_da96579883444c35 = {
{ 0, 0, 0, 0, 5, 0, 6, 0,
53, 76, 68, 131, 152, 87, 150, 218,
@@ -1739,7 +1680,7 @@ const ::capnp::_::RawSchema s_f416ec09499d9d19 = {
0, 3, i_f416ec09499d9d19, nullptr, nullptr, { &s_f416ec09499d9d19, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<597> b_a1680744031fdb2d = {
static const ::capnp::_::AlignedData<613> b_a1680744031fdb2d = {
{ 0, 0, 0, 0, 5, 0, 6, 0,
45, 219, 31, 3, 68, 7, 104, 161,
13, 0, 0, 0, 1, 0, 13, 0,
@@ -1749,7 +1690,7 @@ static const ::capnp::_::AlignedData<597> b_a1680744031fdb2d = {
21, 0, 0, 0, 218, 0, 0, 0,
33, 0, 0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
29, 0, 0, 0, 175, 7, 0, 0,
29, 0, 0, 0, 231, 7, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
99, 117, 115, 116, 111, 109, 46, 99,
@@ -1757,252 +1698,259 @@ static const ::capnp::_::AlignedData<597> b_a1680744031fdb2d = {
103, 80, 105, 108, 111, 116, 80, 108,
97, 110, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0,
140, 0, 0, 0, 3, 0, 4, 0,
144, 0, 0, 0, 3, 0, 4, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
197, 3, 0, 0, 138, 0, 0, 0,
225, 3, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
200, 3, 0, 0, 3, 0, 1, 0,
212, 3, 0, 0, 2, 0, 1, 0,
228, 3, 0, 0, 3, 0, 1, 0,
240, 3, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
209, 3, 0, 0, 178, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
212, 3, 0, 0, 3, 0, 1, 0,
224, 3, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 64, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
221, 3, 0, 0, 162, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
224, 3, 0, 0, 3, 0, 1, 0,
236, 3, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
233, 3, 0, 0, 74, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
232, 3, 0, 0, 3, 0, 1, 0,
244, 3, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 65, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
241, 3, 0, 0, 98, 0, 0, 0,
237, 3, 0, 0, 178, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
240, 3, 0, 0, 3, 0, 1, 0,
252, 3, 0, 0, 2, 0, 1, 0,
5, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0,
2, 0, 0, 0, 64, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
249, 3, 0, 0, 90, 0, 0, 0,
249, 3, 0, 0, 162, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
248, 3, 0, 0, 3, 0, 1, 0,
4, 4, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
252, 3, 0, 0, 3, 0, 1, 0,
8, 4, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 4, 0, 0, 178, 0, 0, 0,
5, 4, 0, 0, 74, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
4, 4, 0, 0, 3, 0, 1, 0,
16, 4, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 65, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
13, 4, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
12, 4, 0, 0, 3, 0, 1, 0,
24, 4, 0, 0, 2, 0, 1, 0,
5, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
21, 4, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
20, 4, 0, 0, 3, 0, 1, 0,
32, 4, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
29, 4, 0, 0, 178, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
32, 4, 0, 0, 3, 0, 1, 0,
44, 4, 0, 0, 2, 0, 1, 0,
7, 0, 0, 0, 66, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
13, 4, 0, 0, 138, 0, 0, 0,
41, 4, 0, 0, 130, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
16, 4, 0, 0, 3, 0, 1, 0,
28, 4, 0, 0, 2, 0, 1, 0,
40, 4, 0, 0, 3, 0, 1, 0,
52, 4, 0, 0, 2, 0, 1, 0,
8, 0, 0, 0, 67, 0, 0, 0,
0, 0, 1, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
25, 4, 0, 0, 98, 0, 0, 0,
49, 4, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
24, 4, 0, 0, 3, 0, 1, 0,
36, 4, 0, 0, 2, 0, 1, 0,
9, 0, 0, 0, 5, 0, 0, 0,
52, 4, 0, 0, 3, 0, 1, 0,
64, 4, 0, 0, 2, 0, 1, 0,
9, 0, 0, 0, 68, 0, 0, 0,
0, 0, 1, 0, 9, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
33, 4, 0, 0, 146, 0, 0, 0,
61, 4, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
36, 4, 0, 0, 3, 0, 1, 0,
48, 4, 0, 0, 2, 0, 1, 0,
10, 0, 0, 0, 0, 0, 0, 0,
60, 4, 0, 0, 3, 0, 1, 0,
72, 4, 0, 0, 2, 0, 1, 0,
10, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
45, 4, 0, 0, 130, 0, 0, 0,
69, 4, 0, 0, 146, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
44, 4, 0, 0, 3, 0, 1, 0,
72, 4, 0, 0, 2, 0, 1, 0,
11, 0, 0, 0, 8, 0, 0, 0,
72, 4, 0, 0, 3, 0, 1, 0,
84, 4, 0, 0, 2, 0, 1, 0,
11, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 11, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
69, 4, 0, 0, 202, 0, 0, 0,
81, 4, 0, 0, 130, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
76, 4, 0, 0, 3, 0, 1, 0,
88, 4, 0, 0, 2, 0, 1, 0,
12, 0, 0, 0, 68, 0, 0, 0,
80, 4, 0, 0, 3, 0, 1, 0,
108, 4, 0, 0, 2, 0, 1, 0,
12, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 12, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
85, 4, 0, 0, 106, 0, 0, 0,
105, 4, 0, 0, 202, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
84, 4, 0, 0, 3, 0, 1, 0,
96, 4, 0, 0, 2, 0, 1, 0,
13, 0, 0, 0, 9, 0, 0, 0,
112, 4, 0, 0, 3, 0, 1, 0,
124, 4, 0, 0, 2, 0, 1, 0,
13, 0, 0, 0, 69, 0, 0, 0,
0, 0, 1, 0, 13, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
93, 4, 0, 0, 114, 0, 0, 0,
121, 4, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
92, 4, 0, 0, 3, 0, 1, 0,
104, 4, 0, 0, 2, 0, 1, 0,
14, 0, 0, 0, 10, 0, 0, 0,
120, 4, 0, 0, 3, 0, 1, 0,
132, 4, 0, 0, 2, 0, 1, 0,
14, 0, 0, 0, 9, 0, 0, 0,
0, 0, 1, 0, 14, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
101, 4, 0, 0, 122, 0, 0, 0,
129, 4, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
100, 4, 0, 0, 3, 0, 1, 0,
112, 4, 0, 0, 2, 0, 1, 0,
15, 0, 0, 0, 11, 0, 0, 0,
128, 4, 0, 0, 3, 0, 1, 0,
140, 4, 0, 0, 2, 0, 1, 0,
15, 0, 0, 0, 10, 0, 0, 0,
0, 0, 1, 0, 15, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
109, 4, 0, 0, 130, 0, 0, 0,
137, 4, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
108, 4, 0, 0, 3, 0, 1, 0,
120, 4, 0, 0, 2, 0, 1, 0,
16, 0, 0, 0, 12, 0, 0, 0,
136, 4, 0, 0, 3, 0, 1, 0,
148, 4, 0, 0, 2, 0, 1, 0,
16, 0, 0, 0, 11, 0, 0, 0,
0, 0, 1, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
117, 4, 0, 0, 130, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
116, 4, 0, 0, 3, 0, 1, 0,
128, 4, 0, 0, 2, 0, 1, 0,
17, 0, 0, 0, 69, 0, 0, 0,
0, 0, 1, 0, 17, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
125, 4, 0, 0, 74, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
124, 4, 0, 0, 3, 0, 1, 0,
136, 4, 0, 0, 2, 0, 1, 0,
18, 0, 0, 0, 13, 0, 0, 0,
0, 0, 1, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
133, 4, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
132, 4, 0, 0, 3, 0, 1, 0,
144, 4, 0, 0, 2, 0, 1, 0,
19, 0, 0, 0, 14, 0, 0, 0,
0, 0, 1, 0, 19, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
141, 4, 0, 0, 138, 0, 0, 0,
145, 4, 0, 0, 130, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
144, 4, 0, 0, 3, 0, 1, 0,
156, 4, 0, 0, 2, 0, 1, 0,
20, 0, 0, 0, 15, 0, 0, 0,
0, 0, 1, 0, 20, 0, 0, 0,
17, 0, 0, 0, 12, 0, 0, 0,
0, 0, 1, 0, 17, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
153, 4, 0, 0, 162, 0, 0, 0,
153, 4, 0, 0, 130, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
156, 4, 0, 0, 3, 0, 1, 0,
168, 4, 0, 0, 2, 0, 1, 0,
21, 0, 0, 0, 16, 0, 0, 0,
0, 0, 1, 0, 21, 0, 0, 0,
152, 4, 0, 0, 3, 0, 1, 0,
164, 4, 0, 0, 2, 0, 1, 0,
18, 0, 0, 0, 70, 0, 0, 0,
0, 0, 1, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
165, 4, 0, 0, 146, 0, 0, 0,
161, 4, 0, 0, 74, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
160, 4, 0, 0, 3, 0, 1, 0,
172, 4, 0, 0, 2, 0, 1, 0,
19, 0, 0, 0, 13, 0, 0, 0,
0, 0, 1, 0, 19, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
169, 4, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
168, 4, 0, 0, 3, 0, 1, 0,
180, 4, 0, 0, 2, 0, 1, 0,
22, 0, 0, 0, 17, 0, 0, 0,
0, 0, 1, 0, 22, 0, 0, 0,
20, 0, 0, 0, 14, 0, 0, 0,
0, 0, 1, 0, 20, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
177, 4, 0, 0, 154, 0, 0, 0,
177, 4, 0, 0, 138, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
180, 4, 0, 0, 3, 0, 1, 0,
192, 4, 0, 0, 2, 0, 1, 0,
23, 0, 0, 0, 18, 0, 0, 0,
21, 0, 0, 0, 15, 0, 0, 0,
0, 0, 1, 0, 21, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
189, 4, 0, 0, 162, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
192, 4, 0, 0, 3, 0, 1, 0,
204, 4, 0, 0, 2, 0, 1, 0,
22, 0, 0, 0, 16, 0, 0, 0,
0, 0, 1, 0, 22, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
201, 4, 0, 0, 146, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
204, 4, 0, 0, 3, 0, 1, 0,
216, 4, 0, 0, 2, 0, 1, 0,
23, 0, 0, 0, 17, 0, 0, 0,
0, 0, 1, 0, 23, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
189, 4, 0, 0, 114, 0, 0, 0,
213, 4, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
188, 4, 0, 0, 3, 0, 1, 0,
200, 4, 0, 0, 2, 0, 1, 0,
24, 0, 0, 0, 19, 0, 0, 0,
216, 4, 0, 0, 3, 0, 1, 0,
228, 4, 0, 0, 2, 0, 1, 0,
24, 0, 0, 0, 18, 0, 0, 0,
0, 0, 1, 0, 24, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
197, 4, 0, 0, 162, 0, 0, 0,
225, 4, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
200, 4, 0, 0, 3, 0, 1, 0,
212, 4, 0, 0, 2, 0, 1, 0,
25, 0, 0, 0, 1, 0, 0, 0,
224, 4, 0, 0, 3, 0, 1, 0,
236, 4, 0, 0, 2, 0, 1, 0,
25, 0, 0, 0, 19, 0, 0, 0,
0, 0, 1, 0, 25, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
209, 4, 0, 0, 162, 0, 0, 0,
233, 4, 0, 0, 162, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
212, 4, 0, 0, 3, 0, 1, 0,
224, 4, 0, 0, 2, 0, 1, 0,
26, 0, 0, 0, 20, 0, 0, 0,
236, 4, 0, 0, 3, 0, 1, 0,
248, 4, 0, 0, 2, 0, 1, 0,
26, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
221, 4, 0, 0, 82, 0, 0, 0,
245, 4, 0, 0, 162, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
220, 4, 0, 0, 3, 0, 1, 0,
232, 4, 0, 0, 2, 0, 1, 0,
27, 0, 0, 0, 21, 0, 0, 0,
248, 4, 0, 0, 3, 0, 1, 0,
4, 5, 0, 0, 2, 0, 1, 0,
27, 0, 0, 0, 20, 0, 0, 0,
0, 0, 1, 0, 27, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
229, 4, 0, 0, 122, 0, 0, 0,
1, 5, 0, 0, 82, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
228, 4, 0, 0, 3, 0, 1, 0,
240, 4, 0, 0, 2, 0, 1, 0,
28, 0, 0, 0, 70, 0, 0, 0,
0, 5, 0, 0, 3, 0, 1, 0,
12, 5, 0, 0, 2, 0, 1, 0,
28, 0, 0, 0, 21, 0, 0, 0,
0, 0, 1, 0, 28, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
237, 4, 0, 0, 146, 0, 0, 0,
9, 5, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
240, 4, 0, 0, 3, 0, 1, 0,
252, 4, 0, 0, 2, 0, 1, 0,
29, 0, 0, 0, 22, 0, 0, 0,
8, 5, 0, 0, 3, 0, 1, 0,
20, 5, 0, 0, 2, 0, 1, 0,
29, 0, 0, 0, 71, 0, 0, 0,
0, 0, 1, 0, 29, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
249, 4, 0, 0, 66, 0, 0, 0,
17, 5, 0, 0, 146, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
244, 4, 0, 0, 3, 0, 1, 0,
0, 5, 0, 0, 2, 0, 1, 0,
30, 0, 0, 0, 71, 0, 0, 0,
20, 5, 0, 0, 3, 0, 1, 0,
32, 5, 0, 0, 2, 0, 1, 0,
30, 0, 0, 0, 22, 0, 0, 0,
0, 0, 1, 0, 30, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
253, 4, 0, 0, 106, 0, 0, 0,
29, 5, 0, 0, 66, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
252, 4, 0, 0, 3, 0, 1, 0,
8, 5, 0, 0, 2, 0, 1, 0,
24, 5, 0, 0, 3, 0, 1, 0,
36, 5, 0, 0, 2, 0, 1, 0,
31, 0, 0, 0, 72, 0, 0, 0,
0, 0, 1, 0, 31, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
5, 5, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
4, 5, 0, 0, 3, 0, 1, 0,
16, 5, 0, 0, 2, 0, 1, 0,
32, 0, 0, 0, 73, 0, 0, 0,
0, 0, 1, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
13, 5, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
12, 5, 0, 0, 3, 0, 1, 0,
24, 5, 0, 0, 2, 0, 1, 0,
33, 0, 0, 0, 23, 0, 0, 0,
0, 0, 1, 0, 33, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
21, 5, 0, 0, 202, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
28, 5, 0, 0, 3, 0, 1, 0,
40, 5, 0, 0, 2, 0, 1, 0,
34, 0, 0, 0, 24, 0, 0, 0,
0, 0, 1, 0, 34, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
37, 5, 0, 0, 66, 0, 0, 0,
33, 5, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
32, 5, 0, 0, 3, 0, 1, 0,
44, 5, 0, 0, 2, 0, 1, 0,
32, 0, 0, 0, 73, 0, 0, 0,
0, 0, 1, 0, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
41, 5, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
40, 5, 0, 0, 3, 0, 1, 0,
52, 5, 0, 0, 2, 0, 1, 0,
33, 0, 0, 0, 74, 0, 0, 0,
0, 0, 1, 0, 33, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
49, 5, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
48, 5, 0, 0, 3, 0, 1, 0,
60, 5, 0, 0, 2, 0, 1, 0,
34, 0, 0, 0, 23, 0, 0, 0,
0, 0, 1, 0, 34, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
57, 5, 0, 0, 202, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
64, 5, 0, 0, 3, 0, 1, 0,
76, 5, 0, 0, 2, 0, 1, 0,
35, 0, 0, 0, 24, 0, 0, 0,
0, 0, 1, 0, 35, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
73, 5, 0, 0, 66, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
68, 5, 0, 0, 3, 0, 1, 0,
80, 5, 0, 0, 2, 0, 1, 0,
97, 99, 99, 101, 108, 101, 114, 97,
116, 105, 111, 110, 74, 101, 114, 107,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -2070,6 +2018,15 @@ static const ::capnp::_::AlignedData<597> b_a1680744031fdb2d = {
5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
100, 105, 115, 97, 98, 108, 101, 84,
104, 114, 111, 116, 116, 108, 101, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
101, 120, 112, 101, 114, 105, 109, 101,
110, 116, 97, 108, 77, 111, 100, 101,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -2343,11 +2300,11 @@ static const ::capnp::_::AlignedData<597> b_a1680744031fdb2d = {
static const ::capnp::_::RawSchema* const d_a1680744031fdb2d[] = {
&s_81c2f05a394cf4af,
};
static const uint16_t m_a1680744031fdb2d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34};
static const uint16_t i_a1680744031fdb2d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34};
static const uint16_t m_a1680744031fdb2d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35};
static const uint16_t i_a1680744031fdb2d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35};
const ::capnp::_::RawSchema s_a1680744031fdb2d = {
0xa1680744031fdb2d, b_a1680744031fdb2d.words, 597, d_a1680744031fdb2d, m_a1680744031fdb2d,
1, 35, i_a1680744031fdb2d, nullptr, nullptr, { &s_a1680744031fdb2d, nullptr, nullptr, 0, 0, nullptr }, false
0xa1680744031fdb2d, b_a1680744031fdb2d.words, 613, d_a1680744031fdb2d, m_a1680744031fdb2d,
1, 36, i_a1680744031fdb2d, nullptr, nullptr, { &s_a1680744031fdb2d, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<55> b_cb9fd56c7057593a = {
@@ -2761,18 +2718,6 @@ constexpr ::capnp::_::RawSchema const* FrogPilotCarParams::SafetyConfig::_capnpP
#endif // !CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
#endif // !CAPNP_LITE
// FrogPilotCarParams::LateralTuning
#if CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
constexpr uint16_t FrogPilotCarParams::LateralTuning::_capnpPrivate::dataWordSize;
constexpr uint16_t FrogPilotCarParams::LateralTuning::_capnpPrivate::pointerCount;
#endif // !CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
#if !CAPNP_LITE
#if CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
constexpr ::capnp::Kind FrogPilotCarParams::LateralTuning::_capnpPrivate::kind;
constexpr ::capnp::_::RawSchema const* FrogPilotCarParams::LateralTuning::_capnpPrivate::schema;
#endif // !CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
#endif // !CAPNP_LITE
// FrogPilotCarState
#if CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
constexpr uint16_t FrogPilotCarState::_capnpPrivate::dataWordSize;
+68 -287
View File
@@ -84,7 +84,6 @@ enum class EventName_aedffd8f31e7b55d: uint16_t {
CAPNP_DECLARE_ENUM(EventName, aedffd8f31e7b55d);
CAPNP_DECLARE_SCHEMA(f35cc4560bbf6ec2);
CAPNP_DECLARE_SCHEMA(8d65dd40bad40951);
CAPNP_DECLARE_SCHEMA(fcc949d2cbea7649);
CAPNP_DECLARE_SCHEMA(da96579883444c35);
CAPNP_DECLARE_SCHEMA(ccb4d6b0dc102d40);
CAPNP_DECLARE_SCHEMA(8033e8e60d6a0edb);
@@ -185,10 +184,9 @@ struct FrogPilotCarParams {
class Builder;
class Pipeline;
struct SafetyConfig;
struct LateralTuning;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(f35cc4560bbf6ec2, 1, 2)
CAPNP_DECLARE_STRUCT_HEADER(f35cc4560bbf6ec2, 1, 1)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
@@ -210,25 +208,6 @@ struct FrogPilotCarParams::SafetyConfig {
};
};
struct FrogPilotCarParams::LateralTuning {
LateralTuning() = delete;
class Reader;
class Builder;
class Pipeline;
enum Which: uint16_t {
PID,
TORQUE,
};
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(fcc949d2cbea7649, 1, 2)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
};
};
struct FrogPilotCarState {
FrogPilotCarState() = delete;
@@ -690,7 +669,7 @@ public:
inline bool hasSafetyConfigs() const;
inline ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>::Reader getSafetyConfigs() const;
inline typename LateralTuning::Reader getLateralTuning() const;
inline bool getCanUseSASCM() const;
private:
::capnp::_::StructReader _reader;
@@ -742,8 +721,8 @@ public:
inline void adoptSafetyConfigs(::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>>&& value);
inline ::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>> disownSafetyConfigs();
inline typename LateralTuning::Builder getLateralTuning();
inline typename LateralTuning::Builder initLateralTuning();
inline bool getCanUseSASCM();
inline void setCanUseSASCM(bool value);
private:
::capnp::_::StructBuilder _builder;
@@ -763,7 +742,6 @@ public:
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
inline typename LateralTuning::Pipeline getLateralTuning();
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
@@ -848,103 +826,6 @@ private:
};
#endif // !CAPNP_LITE
class FrogPilotCarParams::LateralTuning::Reader {
public:
typedef LateralTuning Reads;
Reader() = default;
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
inline ::capnp::MessageSize totalSize() const {
return _reader.totalSize().asPublic();
}
#if !CAPNP_LITE
inline ::kj::StringTree toString() const {
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
}
#endif // !CAPNP_LITE
inline Which which() const;
inline bool isPid() const;
inline bool hasPid() const;
inline ::cereal::CarParams::LateralPIDTuning::Reader getPid() const;
inline bool isTorque() const;
inline bool hasTorque() const;
inline ::cereal::CarParams::LateralTorqueTuning::Reader getTorque() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
template <typename, ::capnp::Kind>
friend struct ::capnp::List;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
};
class FrogPilotCarParams::LateralTuning::Builder {
public:
typedef LateralTuning Builds;
Builder() = delete; // Deleted to discourage incorrect usage.
// You can explicitly initialize to nullptr instead.
inline Builder(decltype(nullptr)) {}
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
inline operator Reader() const { return Reader(_builder.asReader()); }
inline Reader asReader() const { return *this; }
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
#if !CAPNP_LITE
inline ::kj::StringTree toString() const { return asReader().toString(); }
#endif // !CAPNP_LITE
inline Which which();
inline bool isPid();
inline bool hasPid();
inline ::cereal::CarParams::LateralPIDTuning::Builder getPid();
inline void setPid( ::cereal::CarParams::LateralPIDTuning::Reader value);
inline ::cereal::CarParams::LateralPIDTuning::Builder initPid();
inline void adoptPid(::capnp::Orphan< ::cereal::CarParams::LateralPIDTuning>&& value);
inline ::capnp::Orphan< ::cereal::CarParams::LateralPIDTuning> disownPid();
inline bool isTorque();
inline bool hasTorque();
inline ::cereal::CarParams::LateralTorqueTuning::Builder getTorque();
inline void setTorque( ::cereal::CarParams::LateralTorqueTuning::Reader value);
inline ::cereal::CarParams::LateralTorqueTuning::Builder initTorque();
inline void adoptTorque(::capnp::Orphan< ::cereal::CarParams::LateralTorqueTuning>&& value);
inline ::capnp::Orphan< ::cereal::CarParams::LateralTorqueTuning> disownTorque();
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
template <typename, ::capnp::Kind>
friend struct ::capnp::_::PointerHelpers;
};
#if !CAPNP_LITE
class FrogPilotCarParams::LateralTuning::Pipeline {
public:
typedef LateralTuning Pipelines;
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
: _typeless(kj::mv(typeless)) {}
private:
::capnp::AnyPointer::Pipeline _typeless;
friend class ::capnp::PipelineHook;
template <typename, ::capnp::Kind>
friend struct ::capnp::ToDynamic_;
};
#endif // !CAPNP_LITE
class FrogPilotCarState::Reader {
public:
typedef FrogPilotCarState Reads;
@@ -1557,6 +1438,8 @@ public:
inline ::int64_t getDesiredFollowDistance() const;
inline bool getDisableThrottle() const;
inline bool getExperimentalMode() const;
inline bool getForcingStop() const;
@@ -1664,6 +1547,9 @@ public:
inline ::int64_t getDesiredFollowDistance();
inline void setDesiredFollowDistance( ::int64_t value);
inline bool getDisableThrottle();
inline void setDisableThrottle(bool value);
inline bool getExperimentalMode();
inline void setExperimentalMode(bool value);
@@ -2339,22 +2225,20 @@ inline ::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfi
::capnp::bounded<0>() * ::capnp::POINTERS));
}
inline typename FrogPilotCarParams::LateralTuning::Reader FrogPilotCarParams::Reader::getLateralTuning() const {
return typename FrogPilotCarParams::LateralTuning::Reader(_reader);
inline bool FrogPilotCarParams::Reader::getCanUseSASCM() const {
return _reader.getDataField<bool>(
::capnp::bounded<4>() * ::capnp::ELEMENTS);
}
inline typename FrogPilotCarParams::LateralTuning::Builder FrogPilotCarParams::Builder::getLateralTuning() {
return typename FrogPilotCarParams::LateralTuning::Builder(_builder);
inline bool FrogPilotCarParams::Builder::getCanUseSASCM() {
return _builder.getDataField<bool>(
::capnp::bounded<4>() * ::capnp::ELEMENTS);
}
#if !CAPNP_LITE
inline typename FrogPilotCarParams::LateralTuning::Pipeline FrogPilotCarParams::Pipeline::getLateralTuning() {
return typename FrogPilotCarParams::LateralTuning::Pipeline(_typeless.noop());
}
#endif // !CAPNP_LITE
inline typename FrogPilotCarParams::LateralTuning::Builder FrogPilotCarParams::Builder::initLateralTuning() {
_builder.setDataField< ::uint16_t>(::capnp::bounded<1>() * ::capnp::ELEMENTS, 0);
_builder.getPointerField(::capnp::bounded<1>() * ::capnp::POINTERS).clear();
return typename FrogPilotCarParams::LateralTuning::Builder(_builder);
inline void FrogPilotCarParams::Builder::setCanUseSASCM(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<4>() * ::capnp::ELEMENTS, value);
}
inline ::uint16_t FrogPilotCarParams::SafetyConfig::Reader::getSafetyParam() const {
return _reader.getDataField< ::uint16_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
@@ -2369,123 +2253,6 @@ inline void FrogPilotCarParams::SafetyConfig::Builder::setSafetyParam( ::uint16_
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
}
inline ::cereal::FrogPilotCarParams::LateralTuning::Which FrogPilotCarParams::LateralTuning::Reader::which() const {
return _reader.getDataField<Which>(
::capnp::bounded<1>() * ::capnp::ELEMENTS);
}
inline ::cereal::FrogPilotCarParams::LateralTuning::Which FrogPilotCarParams::LateralTuning::Builder::which() {
return _builder.getDataField<Which>(
::capnp::bounded<1>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotCarParams::LateralTuning::Reader::isPid() const {
return which() == FrogPilotCarParams::LateralTuning::PID;
}
inline bool FrogPilotCarParams::LateralTuning::Builder::isPid() {
return which() == FrogPilotCarParams::LateralTuning::PID;
}
inline bool FrogPilotCarParams::LateralTuning::Reader::hasPid() const {
if (which() != FrogPilotCarParams::LateralTuning::PID) return false;
return !_reader.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
}
inline bool FrogPilotCarParams::LateralTuning::Builder::hasPid() {
if (which() != FrogPilotCarParams::LateralTuning::PID) return false;
return !_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
}
inline ::cereal::CarParams::LateralPIDTuning::Reader FrogPilotCarParams::LateralTuning::Reader::getPid() const {
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::PID),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::get(_reader.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline ::cereal::CarParams::LateralPIDTuning::Builder FrogPilotCarParams::LateralTuning::Builder::getPid() {
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::PID),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::get(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline void FrogPilotCarParams::LateralTuning::Builder::setPid( ::cereal::CarParams::LateralPIDTuning::Reader value) {
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::PID);
::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::set(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS), value);
}
inline ::cereal::CarParams::LateralPIDTuning::Builder FrogPilotCarParams::LateralTuning::Builder::initPid() {
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::PID);
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::init(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline void FrogPilotCarParams::LateralTuning::Builder::adoptPid(
::capnp::Orphan< ::cereal::CarParams::LateralPIDTuning>&& value) {
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::PID);
::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::adopt(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS), kj::mv(value));
}
inline ::capnp::Orphan< ::cereal::CarParams::LateralPIDTuning> FrogPilotCarParams::LateralTuning::Builder::disownPid() {
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::PID),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::disown(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline bool FrogPilotCarParams::LateralTuning::Reader::isTorque() const {
return which() == FrogPilotCarParams::LateralTuning::TORQUE;
}
inline bool FrogPilotCarParams::LateralTuning::Builder::isTorque() {
return which() == FrogPilotCarParams::LateralTuning::TORQUE;
}
inline bool FrogPilotCarParams::LateralTuning::Reader::hasTorque() const {
if (which() != FrogPilotCarParams::LateralTuning::TORQUE) return false;
return !_reader.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
}
inline bool FrogPilotCarParams::LateralTuning::Builder::hasTorque() {
if (which() != FrogPilotCarParams::LateralTuning::TORQUE) return false;
return !_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
}
inline ::cereal::CarParams::LateralTorqueTuning::Reader FrogPilotCarParams::LateralTuning::Reader::getTorque() const {
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::TORQUE),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::get(_reader.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline ::cereal::CarParams::LateralTorqueTuning::Builder FrogPilotCarParams::LateralTuning::Builder::getTorque() {
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::TORQUE),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::get(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline void FrogPilotCarParams::LateralTuning::Builder::setTorque( ::cereal::CarParams::LateralTorqueTuning::Reader value) {
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::TORQUE);
::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::set(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS), value);
}
inline ::cereal::CarParams::LateralTorqueTuning::Builder FrogPilotCarParams::LateralTuning::Builder::initTorque() {
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::TORQUE);
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::init(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline void FrogPilotCarParams::LateralTuning::Builder::adoptTorque(
::capnp::Orphan< ::cereal::CarParams::LateralTorqueTuning>&& value) {
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::TORQUE);
::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::adopt(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS), kj::mv(value));
}
inline ::capnp::Orphan< ::cereal::CarParams::LateralTorqueTuning> FrogPilotCarParams::LateralTuning::Builder::disownTorque() {
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::TORQUE),
"Must check which() before get()ing a union member.");
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::disown(_builder.getPointerField(
::capnp::bounded<1>() * ::capnp::POINTERS));
}
inline bool FrogPilotCarState::Reader::getAccelPressed() const {
return _reader.getDataField<bool>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
@@ -3036,34 +2803,48 @@ inline void FrogPilotPlan::Builder::setDesiredFollowDistance( ::int64_t value) {
::capnp::bounded<3>() * ::capnp::ELEMENTS, value);
}
inline bool FrogPilotPlan::Reader::getExperimentalMode() const {
inline bool FrogPilotPlan::Reader::getDisableThrottle() const {
return _reader.getDataField<bool>(
::capnp::bounded<66>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotPlan::Builder::getExperimentalMode() {
inline bool FrogPilotPlan::Builder::getDisableThrottle() {
return _builder.getDataField<bool>(
::capnp::bounded<66>() * ::capnp::ELEMENTS);
}
inline void FrogPilotPlan::Builder::setExperimentalMode(bool value) {
inline void FrogPilotPlan::Builder::setDisableThrottle(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<66>() * ::capnp::ELEMENTS, value);
}
inline bool FrogPilotPlan::Reader::getForcingStop() const {
inline bool FrogPilotPlan::Reader::getExperimentalMode() const {
return _reader.getDataField<bool>(
::capnp::bounded<67>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotPlan::Builder::getForcingStop() {
inline bool FrogPilotPlan::Builder::getExperimentalMode() {
return _builder.getDataField<bool>(
::capnp::bounded<67>() * ::capnp::ELEMENTS);
}
inline void FrogPilotPlan::Builder::setForcingStop(bool value) {
inline void FrogPilotPlan::Builder::setExperimentalMode(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<67>() * ::capnp::ELEMENTS, value);
}
inline bool FrogPilotPlan::Reader::getForcingStop() const {
return _reader.getDataField<bool>(
::capnp::bounded<68>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotPlan::Builder::getForcingStop() {
return _builder.getDataField<bool>(
::capnp::bounded<68>() * ::capnp::ELEMENTS);
}
inline void FrogPilotPlan::Builder::setForcingStop(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<68>() * ::capnp::ELEMENTS, value);
}
inline float FrogPilotPlan::Reader::getForcingStopLength() const {
return _reader.getDataField<float>(
::capnp::bounded<5>() * ::capnp::ELEMENTS);
@@ -3128,16 +2909,16 @@ inline void FrogPilotPlan::Builder::setIncreasedStoppedDistance(float value) {
inline bool FrogPilotPlan::Reader::getLateralCheck() const {
return _reader.getDataField<bool>(
::capnp::bounded<68>() * ::capnp::ELEMENTS);
::capnp::bounded<69>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotPlan::Builder::getLateralCheck() {
return _builder.getDataField<bool>(
::capnp::bounded<68>() * ::capnp::ELEMENTS);
::capnp::bounded<69>() * ::capnp::ELEMENTS);
}
inline void FrogPilotPlan::Builder::setLateralCheck(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<68>() * ::capnp::ELEMENTS, value);
::capnp::bounded<69>() * ::capnp::ELEMENTS, value);
}
inline float FrogPilotPlan::Reader::getLaneWidthLeft() const {
@@ -3198,16 +2979,16 @@ inline void FrogPilotPlan::Builder::setMinAcceleration(float value) {
inline bool FrogPilotPlan::Reader::getRedLight() const {
return _reader.getDataField<bool>(
::capnp::bounded<69>() * ::capnp::ELEMENTS);
::capnp::bounded<70>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotPlan::Builder::getRedLight() {
return _builder.getDataField<bool>(
::capnp::bounded<69>() * ::capnp::ELEMENTS);
::capnp::bounded<70>() * ::capnp::ELEMENTS);
}
inline void FrogPilotPlan::Builder::setRedLight(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<69>() * ::capnp::ELEMENTS, value);
::capnp::bounded<70>() * ::capnp::ELEMENTS, value);
}
inline float FrogPilotPlan::Reader::getRoadCurvature() const {
@@ -3372,16 +3153,16 @@ inline void FrogPilotPlan::Builder::setSpeedJerkStock(float value) {
inline bool FrogPilotPlan::Reader::getSpeedLimitChanged() const {
return _reader.getDataField<bool>(
::capnp::bounded<70>() * ::capnp::ELEMENTS);
::capnp::bounded<71>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotPlan::Builder::getSpeedLimitChanged() {
return _builder.getDataField<bool>(
::capnp::bounded<70>() * ::capnp::ELEMENTS);
::capnp::bounded<71>() * ::capnp::ELEMENTS);
}
inline void FrogPilotPlan::Builder::setSpeedLimitChanged(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<70>() * ::capnp::ELEMENTS, value);
::capnp::bounded<71>() * ::capnp::ELEMENTS, value);
}
inline float FrogPilotPlan::Reader::getTFollow() const {
@@ -3400,46 +3181,46 @@ inline void FrogPilotPlan::Builder::setTFollow(float value) {
inline bool FrogPilotPlan::Reader::getThemeUpdated() const {
return _reader.getDataField<bool>(
::capnp::bounded<71>() * ::capnp::ELEMENTS);
::capnp::bounded<72>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotPlan::Builder::getThemeUpdated() {
return _builder.getDataField<bool>(
::capnp::bounded<71>() * ::capnp::ELEMENTS);
::capnp::bounded<72>() * ::capnp::ELEMENTS);
}
inline void FrogPilotPlan::Builder::setThemeUpdated(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<71>() * ::capnp::ELEMENTS, value);
}
inline bool FrogPilotPlan::Reader::getTogglesUpdated() const {
return _reader.getDataField<bool>(
::capnp::bounded<72>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotPlan::Builder::getTogglesUpdated() {
return _builder.getDataField<bool>(
::capnp::bounded<72>() * ::capnp::ELEMENTS);
}
inline void FrogPilotPlan::Builder::setTogglesUpdated(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<72>() * ::capnp::ELEMENTS, value);
}
inline bool FrogPilotPlan::Reader::getTrackingLead() const {
inline bool FrogPilotPlan::Reader::getTogglesUpdated() const {
return _reader.getDataField<bool>(
::capnp::bounded<73>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotPlan::Builder::getTrackingLead() {
inline bool FrogPilotPlan::Builder::getTogglesUpdated() {
return _builder.getDataField<bool>(
::capnp::bounded<73>() * ::capnp::ELEMENTS);
}
inline void FrogPilotPlan::Builder::setTrackingLead(bool value) {
inline void FrogPilotPlan::Builder::setTogglesUpdated(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<73>() * ::capnp::ELEMENTS, value);
}
inline bool FrogPilotPlan::Reader::getTrackingLead() const {
return _reader.getDataField<bool>(
::capnp::bounded<74>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotPlan::Builder::getTrackingLead() {
return _builder.getDataField<bool>(
::capnp::bounded<74>() * ::capnp::ELEMENTS);
}
inline void FrogPilotPlan::Builder::setTrackingLead(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<74>() * ::capnp::ELEMENTS, value);
}
inline float FrogPilotPlan::Reader::getUnconfirmedSlcSpeedLimit() const {
return _reader.getDataField<float>(
::capnp::bounded<23>() * ::capnp::ELEMENTS);
+103 -71
View File
@@ -9336,17 +9336,17 @@ const ::capnp::_::RawSchema s_f28c5dc9e09375e3 = {
0, 10, i_f28c5dc9e09375e3, nullptr, nullptr, { &s_f28c5dc9e09375e3, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<191> b_e774a050cbf689a4 = {
static const ::capnp::_::AlignedData<223> b_e774a050cbf689a4 = {
{ 0, 0, 0, 0, 5, 0, 6, 0,
164, 137, 246, 203, 80, 160, 116, 231,
24, 0, 0, 0, 1, 0, 5, 0,
24, 0, 0, 0, 1, 0, 6, 0,
241, 171, 1, 54, 197, 105, 255, 151,
0, 0, 7, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
21, 0, 0, 0, 90, 1, 0, 0,
41, 0, 0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
37, 0, 0, 0, 111, 2, 0, 0,
37, 0, 0, 0, 223, 2, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
108, 111, 103, 46, 99, 97, 112, 110,
@@ -9356,84 +9356,98 @@ static const ::capnp::_::AlignedData<191> b_e774a050cbf689a4 = {
111, 114, 113, 117, 101, 83, 116, 97,
116, 101, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0,
44, 0, 0, 0, 3, 0, 4, 0,
52, 0, 0, 0, 3, 0, 4, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
37, 1, 0, 0, 58, 0, 0, 0,
93, 1, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
32, 1, 0, 0, 3, 0, 1, 0,
44, 1, 0, 0, 2, 0, 1, 0,
88, 1, 0, 0, 3, 0, 1, 0,
100, 1, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
41, 1, 0, 0, 50, 0, 0, 0,
97, 1, 0, 0, 50, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
36, 1, 0, 0, 3, 0, 1, 0,
48, 1, 0, 0, 2, 0, 1, 0,
92, 1, 0, 0, 3, 0, 1, 0,
104, 1, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
45, 1, 0, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
40, 1, 0, 0, 3, 0, 1, 0,
52, 1, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
49, 1, 0, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
44, 1, 0, 0, 3, 0, 1, 0,
56, 1, 0, 0, 2, 0, 1, 0,
5, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
53, 1, 0, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
48, 1, 0, 0, 3, 0, 1, 0,
60, 1, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
57, 1, 0, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
52, 1, 0, 0, 3, 0, 1, 0,
64, 1, 0, 0, 2, 0, 1, 0,
7, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
61, 1, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
56, 1, 0, 0, 3, 0, 1, 0,
68, 1, 0, 0, 2, 0, 1, 0,
8, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
65, 1, 0, 0, 82, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
64, 1, 0, 0, 3, 0, 1, 0,
76, 1, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
73, 1, 0, 0, 82, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
72, 1, 0, 0, 3, 0, 1, 0,
84, 1, 0, 0, 2, 0, 1, 0,
9, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 9, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
81, 1, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
84, 1, 0, 0, 3, 0, 1, 0,
96, 1, 0, 0, 2, 0, 1, 0,
10, 0, 0, 0, 9, 0, 0, 0,
0, 0, 1, 0, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
93, 1, 0, 0, 162, 0, 0, 0,
101, 1, 0, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
96, 1, 0, 0, 3, 0, 1, 0,
108, 1, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
105, 1, 0, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
100, 1, 0, 0, 3, 0, 1, 0,
112, 1, 0, 0, 2, 0, 1, 0,
5, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
109, 1, 0, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
104, 1, 0, 0, 3, 0, 1, 0,
116, 1, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
113, 1, 0, 0, 18, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
108, 1, 0, 0, 3, 0, 1, 0,
120, 1, 0, 0, 2, 0, 1, 0,
7, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
117, 1, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
112, 1, 0, 0, 3, 0, 1, 0,
124, 1, 0, 0, 2, 0, 1, 0,
8, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
121, 1, 0, 0, 82, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
120, 1, 0, 0, 3, 0, 1, 0,
132, 1, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
129, 1, 0, 0, 82, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
128, 1, 0, 0, 3, 0, 1, 0,
140, 1, 0, 0, 2, 0, 1, 0,
9, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 9, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
137, 1, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
140, 1, 0, 0, 3, 0, 1, 0,
152, 1, 0, 0, 2, 0, 1, 0,
10, 0, 0, 0, 9, 0, 0, 0,
0, 0, 1, 0, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
149, 1, 0, 0, 162, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
152, 1, 0, 0, 3, 0, 1, 0,
164, 1, 0, 0, 2, 0, 1, 0,
11, 0, 0, 0, 10, 0, 0, 0,
0, 0, 1, 0, 11, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
161, 1, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
164, 1, 0, 0, 3, 0, 1, 0,
176, 1, 0, 0, 2, 0, 1, 0,
12, 0, 0, 0, 11, 0, 0, 0,
0, 0, 1, 0, 12, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
173, 1, 0, 0, 66, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
168, 1, 0, 0, 3, 0, 1, 0,
180, 1, 0, 0, 2, 0, 1, 0,
97, 99, 116, 105, 118, 101, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -9526,16 +9540,34 @@ static const ::capnp::_::AlignedData<191> b_e774a050cbf689a4 = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
100, 101, 115, 105, 114, 101, 100, 76,
97, 116, 101, 114, 97, 108, 74, 101,
114, 107, 0, 0, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
118, 101, 114, 115, 105, 111, 110, 0,
4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, }
};
::capnp::word const* const bp_e774a050cbf689a4 = b_e774a050cbf689a4.words;
#if !CAPNP_LITE
static const uint16_t m_e774a050cbf689a4[] = {0, 9, 4, 10, 1, 8, 5, 3, 6, 2, 7};
static const uint16_t i_e774a050cbf689a4[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
static const uint16_t m_e774a050cbf689a4[] = {0, 9, 4, 10, 11, 1, 8, 5, 3, 6, 2, 7, 12};
static const uint16_t i_e774a050cbf689a4[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
const ::capnp::_::RawSchema s_e774a050cbf689a4 = {
0xe774a050cbf689a4, b_e774a050cbf689a4.words, 191, nullptr, m_e774a050cbf689a4,
0, 11, i_e774a050cbf689a4, nullptr, nullptr, { &s_e774a050cbf689a4, nullptr, nullptr, 0, 0, nullptr }, false
0xe774a050cbf689a4, b_e774a050cbf689a4.words, 223, nullptr, m_e774a050cbf689a4,
0, 13, i_e774a050cbf689a4, nullptr, nullptr, { &s_e774a050cbf689a4, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<130> b_9024e2d790c82ade = {
+39 -1
View File
@@ -1076,7 +1076,7 @@ struct ControlsState::LateralTorqueState {
class Pipeline;
struct _capnpPrivate {
CAPNP_DECLARE_STRUCT_HEADER(e774a050cbf689a4, 5, 0)
CAPNP_DECLARE_STRUCT_HEADER(e774a050cbf689a4, 6, 0)
#if !CAPNP_LITE
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
#endif // !CAPNP_LITE
@@ -7531,6 +7531,10 @@ public:
inline float getDesiredLateralAccel() const;
inline float getDesiredLateralJerk() const;
inline ::int32_t getVersion() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
@@ -7592,6 +7596,12 @@ public:
inline float getDesiredLateralAccel();
inline void setDesiredLateralAccel(float value);
inline float getDesiredLateralJerk();
inline void setDesiredLateralJerk(float value);
inline ::int32_t getVersion();
inline void setVersion( ::int32_t value);
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
@@ -30064,6 +30074,34 @@ inline void ControlsState::LateralTorqueState::Builder::setDesiredLateralAccel(f
::capnp::bounded<9>() * ::capnp::ELEMENTS, value);
}
inline float ControlsState::LateralTorqueState::Reader::getDesiredLateralJerk() const {
return _reader.getDataField<float>(
::capnp::bounded<10>() * ::capnp::ELEMENTS);
}
inline float ControlsState::LateralTorqueState::Builder::getDesiredLateralJerk() {
return _builder.getDataField<float>(
::capnp::bounded<10>() * ::capnp::ELEMENTS);
}
inline void ControlsState::LateralTorqueState::Builder::setDesiredLateralJerk(float value) {
_builder.setDataField<float>(
::capnp::bounded<10>() * ::capnp::ELEMENTS, value);
}
inline ::int32_t ControlsState::LateralTorqueState::Reader::getVersion() const {
return _reader.getDataField< ::int32_t>(
::capnp::bounded<11>() * ::capnp::ELEMENTS);
}
inline ::int32_t ControlsState::LateralTorqueState::Builder::getVersion() {
return _builder.getDataField< ::int32_t>(
::capnp::bounded<11>() * ::capnp::ELEMENTS);
}
inline void ControlsState::LateralTorqueState::Builder::setVersion( ::int32_t value) {
_builder.setDataField< ::int32_t>(
::capnp::bounded<11>() * ::capnp::ELEMENTS, value);
}
inline bool ControlsState::LateralLQRState::Reader::getActive() const {
return _reader.getDataField<bool>(
::capnp::bounded<0>() * ::capnp::ELEMENTS);
Binary file not shown.
+2
View File
@@ -795,6 +795,8 @@ struct ControlsState @0x97ff69c53601abf1 {
saturated @7 :Bool;
actualLateralAccel @9 :Float32;
desiredLateralAccel @10 :Float32;
desiredLateralJerk @11 :Float32;
version @12 :Int32;
}
struct LateralLQRState {
+22
View File
@@ -224,6 +224,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"AdvancedLateralTune", PERSISTENT},
{"AdvancedLongitudinalTune", PERSISTENT},
{"AggressiveFollow", PERSISTENT},
{"AggressiveFollowHigh", PERSISTENT},
{"AggressiveJerkAcceleration", PERSISTENT},
{"AggressiveJerkDanger", PERSISTENT},
{"AggressiveJerkDeceleration", PERSISTENT},
@@ -240,11 +241,13 @@ std::unordered_map<std::string, uint32_t> keys = {
{"AutomaticallyDownloadModels", PERSISTENT},
{"AutomaticUpdates", PERSISTENT},
{"AvailableModelNames", PERSISTENT},
{"AvailableModelSeries", PERSISTENT},
{"AvailableModels", PERSISTENT},
{"BigMap", PERSISTENT},
{"BlacklistedModels", PERSISTENT},
{"BlindSpotMetrics", PERSISTENT},
{"BlindSpotPath", PERSISTENT},
{"BootLogo", PERSISTENT},
{"BorderMetrics", PERSISTENT},
{"CalibratedLateralAcceleration", PERSISTENT},
{"CalibrationProgress", PERSISTENT},
@@ -270,6 +273,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"CEStatus", CLEAR_ON_OFFROAD_TRANSITION},
{"CEStoppedLead", PERSISTENT},
{"ClusterOffset", PERSISTENT},
{"BootLogoToDownload", CLEAR_ON_MANAGER_START},
{"ColorToDownload", CLEAR_ON_MANAGER_START},
{"Compass", PERSISTENT},
{"ConditionalExperimental", PERSISTENT},
@@ -308,6 +312,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DisengageVolume", PERSISTENT},
{"DoToggleReset", PERSISTENT},
{"DoToggleResetStock", PERSISTENT},
{"DownloadableBootLogos", PERSISTENT},
{"DownloadableColors", PERSISTENT},
{"DownloadableDistanceIcons", PERSISTENT},
{"DownloadableIcons", PERSISTENT},
@@ -320,6 +325,8 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DynamicPedalsOnUI", PERSISTENT},
{"EngageVolume", PERSISTENT},
{"ExperimentalGMTune", PERSISTENT},
{"EVTuning", PERSISTENT},
{"TruckTuning", PERSISTENT},
{"Fahrenheit", PERSISTENT},
{"FavoriteDestinations", PERSISTENT | DONT_LOG},
{"FlashPanda", CLEAR_ON_MANAGER_START},
@@ -345,6 +352,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"FrogsGoMoosTweak", PERSISTENT},
{"FullMap", PERSISTENT},
{"GasRegenCmd", PERSISTENT},
{"GMPedalLongitudinal", PERSISTENT},
{"GoatScream", PERSISTENT},
{"GreenLightAlert", PERSISTENT},
{"HideAlerts", PERSISTENT},
@@ -398,12 +406,17 @@ std::unordered_map<std::string, uint32_t> keys = {
{"MinimumBackupSize", PERSISTENT},
{"MinimumLaneChangeSpeed", PERSISTENT},
{"Model", PERSISTENT},
{"ModelVersion", PERSISTENT},
{"ModelDownloadProgress", CLEAR_ON_MANAGER_START},
{"ModelDrivesAndScores", PERSISTENT},
{"ModelRandomizer", PERSISTENT},
{"ModelToDownload", CLEAR_ON_MANAGER_START},
{"ModelUI", PERSISTENT},
{"ModelVersions", PERSISTENT},
{"ModelReleasedDates", PERSISTENT},
{"CommunityFavorites", PERSISTENT},
{"UserFavorites", PERSISTENT},
{"SortModelsByDate", PERSISTENT},
{"NavigationUI", PERSISTENT},
{"NextMapSpeedLimit", CLEAR_ON_MANAGER_START},
{"NewLongAPI", PERSISTENT},
@@ -449,6 +462,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"RandomThemes", PERSISTENT},
{"RefuseVolume", PERSISTENT},
{"RelaxedFollow", PERSISTENT},
{"RelaxedFollowHigh", PERSISTENT},
{"RelaxedJerkAcceleration", PERSISTENT},
{"RelaxedJerkDanger", PERSISTENT},
{"RelaxedJerkDeceleration", PERSISTENT},
@@ -459,6 +473,8 @@ std::unordered_map<std::string, uint32_t> keys = {
{"RoadEdgesWidth", PERSISTENT},
{"RoadName", CLEAR_ON_MANAGER_START},
{"RoadNameUI", PERSISTENT},
{"RedPanda", PERSISTENT},
{"RemoteStartBootsComma", PERSISTENT},
{"RotatingWheel", PERSISTENT},
{"ScreenBrightness", PERSISTENT},
{"ScreenBrightnessOnroad", PERSISTENT},
@@ -508,6 +524,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"SpeedLimitsFiltered", PERSISTENT | DONT_LOG},
{"SpeedLimitSources", PERSISTENT},
{"StandardFollow", PERSISTENT},
{"StandardFollowHigh", PERSISTENT},
{"StandardJerkAcceleration", PERSISTENT},
{"StandardJerkDanger", PERSISTENT},
{"StandardJerkDeceleration", PERSISTENT},
@@ -524,6 +541,8 @@ std::unordered_map<std::string, uint32_t> keys = {
{"SteerDelayStock", PERSISTENT},
{"SteerFriction", PERSISTENT},
{"SteerFrictionStock", PERSISTENT},
{"SteerOffset", PERSISTENT},
{"SteerOffsetStock", PERSISTENT},
{"SteerLatAccel", PERSISTENT},
{"SteerLatAccelStock", PERSISTENT},
{"SteerKP", PERSISTENT},
@@ -538,6 +557,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"StoppedTimer", PERSISTENT},
{"TacoTune", PERSISTENT},
{"TacoTuneHacks", PERSISTENT},
{"TrailerLoad", PERSISTENT},
{"TestAlert", CLEAR_ON_MANAGER_START},
{"TetheringEnabled", PERSISTENT},
{"ThemeDownloadProgress", CLEAR_ON_MANAGER_START},
@@ -576,6 +596,8 @@ std::unordered_map<std::string, uint32_t> keys = {
{"WarningSoftVolume", PERSISTENT},
{"WheelIcon", PERSISTENT},
{"WheelSpeed", PERSISTENT},
{"StopDistance", PERSISTENT},
{"RecoveryPower", PERSISTENT},
{"WheelToDownload", CLEAR_ON_MANAGER_START},
};
Binary file not shown.
+12 -1
View File
@@ -6,6 +6,7 @@ from collections import deque
from setproctitle import getproctitle
from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware import PC
@@ -34,7 +35,17 @@ def set_realtime_priority(level: int) -> None:
def set_core_affinity(cores: list[int]) -> None:
if not PC:
os.sched_setaffinity(0, cores)
for attempt in range(3): # Retry up to 3 times
try:
os.sched_setaffinity(0, cores)
return
except OSError as e:
if e.errno == 22: # EINVAL
time.sleep(0.1) # Brief delay before retry
else:
raise # Re-raise other errors
# If all retries fail, log and continue without affinity
cloudlog.error(f"Failed to set core affinity after retries: {cores}")
def config_realtime_process(cores: int | list[int], priority: int) -> None:
+52 -40
View File
@@ -6,14 +6,13 @@ from datetime import datetime
from pathlib import Path
from openpilot.frogpilot.common.frogpilot_utilities import delete_file, is_url_pingable
from openpilot.frogpilot.common.frogpilot_variables import RESOURCES_REPO, params_memory
GITHUB_URL = f"https://raw.githubusercontent.com/{RESOURCES_REPO}"
GITLAB_URL = f"https://gitlab.com/{RESOURCES_REPO}/-/raw"
GITHUB_URL = "https://raw.githubusercontent.com/firestar5683/StarPilot-Resources"
GITLAB_URL = "https://gitlab.com/firestar5683/FrogPilot-Resources/-/raw"
def check_github_rate_limit(session):
def check_github_rate_limit():
try:
response = session.get("https://api.github.com/rate_limit", timeout=10)
response = requests.get("https://api.github.com/rate_limit")
response.raise_for_status()
rate_limit_info = response.json()
@@ -26,91 +25,104 @@ def check_github_rate_limit(session):
print("GitHub rate limit reached")
print(f"GitHub Rate Limit Resets At (UTC): {reset_time}")
return False
except requests.exceptions.RequestException as exception:
print(f"Error checking GitHub rate limit: {exception}")
except requests.exceptions.RequestException as error:
print(f"Error checking GitHub rate limit: {error}")
return False
def download_file(cancel_param, destination, progress_param, url, download_param, session, offset_bytes=0, total_bytes=0):
def download_file(cancel_param, destination, progress_param, url, download_param, params_memory, allow_unknown_size=False, suppress_errors=False):
try:
destination.parent.mkdir(parents=True, exist_ok=True)
total_size = get_remote_file_size(url, session)
if total_size == 0:
total_size = get_remote_file_size(url, suppress_errors=suppress_errors or allow_unknown_size)
if total_size == 0 and not allow_unknown_size:
if not url.endswith(".gif"):
handle_error(None, "Download invalid...", "Download invalid...", download_param, progress_param)
if suppress_errors:
return
handle_error(None, "Download invalid...", "Download invalid...", download_param, progress_param, params_memory)
return
with session.get(url, stream=True, timeout=10) as response:
with requests.get(url, stream=True, timeout=10) as response:
response.raise_for_status()
with tempfile.NamedTemporaryFile(delete=False, dir=destination.parent) as temp_file:
with tempfile.NamedTemporaryFile(dir=destination.parent, delete=False) as temp_file:
temp_file_path = Path(temp_file.name)
downloaded_size = 0
for chunk in response.iter_content(chunk_size=16384):
if params_memory.get_bool(cancel_param):
temp_file_path.unlink(missing_ok=True)
handle_error(None, "Download cancelled...", "Download cancelled...", download_param, progress_param)
handle_error(None, "Download cancelled...", "Download cancelled...", download_param, progress_param, params_memory)
return
if chunk:
temp_file.write(chunk)
downloaded_size += len(chunk)
if total_bytes:
overall_progress = (offset_bytes + downloaded_size) / total_bytes * 100
else:
overall_progress = downloaded_size / total_size * 100
if overall_progress != 100:
params_memory.put(progress_param, f"{overall_progress:.0f}%")
else:
if total_size > 0:
progress = (downloaded_size / total_size) * 100
if progress != 100:
params_memory.put(progress_param, f"{progress:.0f}%")
else:
params_memory.put(progress_param, "Verifying authenticity...")
elif downloaded_size > 0:
params_memory.put(progress_param, "Verifying authenticity...")
temp_file_path.rename(destination)
except Exception as exception:
handle_request_error(exception, destination, download_param, progress_param)
except Exception as error:
if suppress_errors:
return
handle_request_error(error, destination, download_param, progress_param, params_memory)
def get_remote_file_size(url, session):
def get_remote_file_size(url, suppress_errors=False):
try:
response = session.head(url, headers={"Accept-Encoding": "identity"}, timeout=10)
response = requests.head(url, headers={"Accept-Encoding": "identity"}, timeout=10)
response.raise_for_status()
return int(response.headers.get("Content-Length", 0))
except Exception as exception:
handle_request_error(exception, None, None, None)
except Exception as error:
if not suppress_errors:
handle_request_error(error, None, None, None, None)
return 0
def get_repository_url(session):
def get_repository_url():
if is_url_pingable("https://github.com"):
if check_github_rate_limit(session):
if check_github_rate_limit():
return GITHUB_URL
if is_url_pingable("https://gitlab.com"):
return GITLAB_URL
return None
def handle_error(destination, error_message, error, download_param, progress_param):
def handle_error(destination, error_message, error, download_param, progress_param, params_memory):
if destination:
delete_file(destination)
if progress_param and "404" not in error_message:
if params_memory and progress_param and "404" not in error_message:
print(f"Error occurred: {error}")
params_memory.put(progress_param, error_message)
params_memory.remove(download_param)
def handle_request_error(error, destination, download_param, progress_param):
def handle_request_error(error, destination, download_param, progress_param, params_memory):
error_map = {
requests.exceptions.ConnectionError: "Connection dropped",
requests.exceptions.HTTPError: lambda error: f"Server error ({error.response.status_code})" if error and getattr(error, "response", None) else "Server error",
requests.exceptions.RequestException: "Network request error. Check connection",
requests.exceptions.Timeout: "Download timed out",
requests.ConnectionError: "Connection dropped",
requests.HTTPError: lambda error: f"Server error ({error.response.status_code})" if error.response else "Server error",
requests.RequestException: "Network request error. Check connection",
requests.Timeout: "Download timed out"
}
error_message = error_map.get(type(error), "Unexpected error")
handle_error(destination, f"Failed: {error_message}", error, download_param, progress_param)
handle_error(destination, f"Failed: {error_message}", error, download_param, progress_param, params_memory)
def verify_download(file_path, url, session):
remote_file_size = get_remote_file_size(url, session)
def verify_download(file_path, url, allow_unknown_size=False):
remote_file_size = get_remote_file_size(url, suppress_errors=allow_unknown_size)
if remote_file_size == 0 and allow_unknown_size:
if not file_path.is_file():
print(f"File not found: {file_path}")
return False
if file_path.stat().st_size == 0:
print(f"File is empty: {file_path}")
return False
return True
if remote_file_size == 0:
print(f"Error fetching remote size for {file_path}")
+362 -462
View File
@@ -5,59 +5,240 @@ import requests
import shutil
import time
import urllib.parse
import urllib.request
from pathlib import Path
from urllib.parse import quote_plus
from openpilot.common.basedir import BASEDIR
from openpilot.frogpilot.assets.download_functions import GITLAB_URL, download_file, get_remote_file_size, get_repository_url, handle_error, handle_request_error, verify_download
from openpilot.frogpilot.common.frogpilot_utilities import delete_file, extract_tar, load_json_file, update_json_file
from openpilot.frogpilot.common.frogpilot_variables import DEFAULT_MODEL, MODELS_PATH, RESOURCES_REPO, TINYGRAD_FILES, params, params_default, params_memory, update_frogpilot_toggles
from openpilot.frogpilot.assets.download_functions import GITLAB_URL, download_file, get_repository_url, handle_error, handle_request_error, verify_download
from openpilot.frogpilot.common.frogpilot_utilities import delete_file
from openpilot.frogpilot.common.frogpilot_variables import DEFAULT_MODEL, MODELS_PATH, params, params_default, params_memory
VERSION = "v16"
VERSION_PATH = MODELS_PATH / "model_version"
VERSION = "v20"
CANCEL_DOWNLOAD_PARAM = "CancelModelDownload"
DOWNLOAD_PROGRESS_PARAM = "ModelDownloadProgress"
MODEL_DOWNLOAD_PARAM = "ModelToDownload"
MODEL_DOWNLOAD_ALL_PARAM = "DownloadAllModels"
UPDATE_TINYGRAD_PARAM = "UpdateTinygrad"
DEFAULT_TINYGRAD_SIZE = 87746736
TAR_FILE_NAME = f"Tinygrad_{VERSION}.tar.gz"
TINYGRAD_MODELD_PATH = Path(BASEDIR) / "frogpilot/tinygrad_modeld"
TINYGRAD_REPO_PATH = Path(BASEDIR) / "tinygrad_repo"
class ModelManager:
def __init__(self, boot_run=False):
def __init__(self):
self.available_models = (params.get("AvailableModels", encoding="utf-8") or "").split(",")
self.model_versions = (params.get("ModelVersions", encoding="utf-8") or "").split(",")
self.model_series = (params.get("AvailableModelSeries", encoding="utf-8") or "").split(",")
self.downloading_model = False
self.available_models = (params.get("AvailableModels", encoding="utf-8") or "").split(",")
self.available_model_names = (params.get("AvailableModelNames", encoding="utf-8") or "").split(",")
self.model_versions = (params.get("ModelVersions", encoding="utf-8") or "").split(",")
@staticmethod
def fetch_models(url):
try:
with urllib.request.urlopen(url, timeout=10) as response:
return json.loads(response.read().decode("utf-8"))["models"]
except Exception as error:
handle_request_error(error, None, None, None, None)
return []
self.model_sizes_path = MODELS_PATH / "model_sizes.json"
self.tinygrad_sizes_path = MODELS_PATH / "tinygrad_sizes.json"
@staticmethod
def fetch_all_model_sizes(repo_url):
project_path = "firestar5683/StarPilot-Resources"
branch = "Models"
self.model_sizes = load_json_file(self.model_sizes_path)
self.tinygrad_sizes = load_json_file(self.tinygrad_sizes_path)
if "github" in repo_url:
api_url = f"https://api.github.com/repos/{project_path}/contents?ref={branch}"
elif "gitlab" in repo_url:
api_url = f"https://gitlab.com/api/v4/projects/{urllib.parse.quote_plus(project_path)}/repository/tree?ref={branch}"
else:
return {}
self.session = requests.Session()
self.session.headers.update({"Accept-Language": "en"})
self.session.headers.update({"User-Agent": "frogpilot-model-downloader/1.0 (https://github.com/FrogAi/FrogPilot)"})
try:
response = requests.get(api_url)
response.raise_for_status()
model_files = [file for file in response.json() if "." in file["name"]]
if boot_run:
self.copy_default_model()
self.validate_models()
if "gitlab" in repo_url:
model_sizes = {}
for file in model_files:
file_path = file["path"]
metadata_url = f"https://gitlab.com/api/v4/projects/{urllib.parse.quote_plus(project_path)}/repository/files/{urllib.parse.quote_plus(file_path)}/raw?ref={branch}"
metadata_response = requests.head(metadata_url)
metadata_response.raise_for_status()
model_sizes[file["name"].rsplit(".", 1)[0]] = int(metadata_response.headers.get("content-length", 0))
return model_sizes
else:
return {file["name"].rsplit(".", 1)[0]: file["size"] for file in model_files if "size" in file}
except Exception as error:
handle_request_error(f"Failed to fetch model sizes from {'GitHub' if 'github' in repo_url else 'GitLab'}: {error}", None, None, None, None)
return {}
def handle_verification_failure(self, model, model_path, file_extension):
print(f"Verification failed for model {model}. Retrying from GitLab...")
model_url = f"{GITLAB_URL}/Models/{model}.{file_extension}"
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, model_url, MODEL_DOWNLOAD_PARAM, params_memory)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_model = False
return
if verify_download(model_path, model_url):
print(f"Model {model} downloaded and verified successfully!")
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(MODEL_DOWNLOAD_PARAM)
self.downloading_model = False
else:
handle_error(model_path, "Verification failed...", "GitLab verification failed", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_model = False
def download_model(self, model_to_download):
self.downloading_model = True
repo_url = get_repository_url()
if not repo_url:
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_model = False
return
try:
model_index = self.available_models.index(model_to_download)
model_version = self.model_versions[model_index]
except Exception:
handle_error(None, f"Unknown model version for {model_to_download}! Download aborted.", "Model download failed", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_model = False
return
if model_version in ("v8", "v9", "v10", "v11", "v12"):
# Download all PKL and metadata files for multi-file tinygrad models (v8 and v9)
filenames = [
f"{model_to_download}_driving_policy_tinygrad.pkl",
f"{model_to_download}_driving_vision_tinygrad.pkl",
f"{model_to_download}_driving_policy_metadata.pkl",
f"{model_to_download}_driving_vision_metadata.pkl",
]
if model_version == "v12":
filenames += [
f"{model_to_download}_driving_off_policy_tinygrad.pkl",
f"{model_to_download}_driving_off_policy_metadata.pkl",
]
for filename in filenames:
model_path = MODELS_PATH / filename
model_url = f"{repo_url}/Models/{filename}"
print(f"Downloading model file: {filename}")
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, model_url, MODEL_DOWNLOAD_PARAM, params_memory)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_model = False
return
if verify_download(model_path, model_url):
print(f"File {filename} downloaded and verified successfully!")
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloaded {filename}!")
else:
self.handle_verification_failure(filename[:-4], model_path, "pkl")
self.downloading_model = False
return
# After all files are downloaded and verified
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(MODEL_DOWNLOAD_PARAM)
elif model_version == "v7":
# Download both PKL and metadata for OG tinygrad models
v7_filenames = [
f"{model_to_download}.pkl",
f"{model_to_download}_metadata.pkl"
]
for filename in v7_filenames:
model_path = MODELS_PATH / filename
model_url = f"{repo_url}/Models/{filename}"
print(f"Downloading v7 model file: {filename}")
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, model_url, MODEL_DOWNLOAD_PARAM, params_memory)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_model = False
return
if verify_download(model_path, model_url):
print(f"File {filename} downloaded and verified successfully!")
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloaded {filename}!")
else:
self.handle_verification_failure(filename.rsplit('.',1)[0], model_path, "pkl")
self.downloading_model = False
return
# Once both files are fetched
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(MODEL_DOWNLOAD_PARAM)
else:
# Classic model: download only the .thneed file
file_extension = "thneed"
model_path = MODELS_PATH / f"{model_to_download}.{file_extension}"
model_url = f"{repo_url}/Models/{model_to_download}.{file_extension}"
print(f"Downloading classic model: {model_to_download}")
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, model_url, MODEL_DOWNLOAD_PARAM, params_memory)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_model = False
return
if verify_download(model_path, model_url):
print(f"Model {model_to_download} downloaded and verified successfully!")
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(MODEL_DOWNLOAD_PARAM)
else:
self.handle_verification_failure(model_to_download, model_path, file_extension)
self.downloading_model = False
return
self.downloading_model = False
@staticmethod
def copy_default_model():
default_model_path = MODELS_PATH / f"{DEFAULT_MODEL}.thneed"
source_path = Path(__file__).parents[2] / "selfdrive/modeld/models/supercombo.thneed"
if source_path.is_file() and not default_model_path.is_file():
shutil.copyfile(source_path, default_model_path)
print(f"Copied the default model from {source_path} to {default_model_path}")
def check_models(self, boot_run, repo_url):
downloaded_models = [
model for model in MODELS_PATH.iterdir()
if (MODELS_PATH / f"{model}.thneed").is_file() or all((MODELS_PATH / f"{model}_{filename}").is_file() for filename, _ in TINYGRAD_FILES)
]
for model_file in downloaded_models:
if not any(model in model_file.name for model in set(self.available_models)):
available_models = set(self.available_models) - {DEFAULT_MODEL}
downloaded_models = set()
for model in available_models:
try:
model_index = self.available_models.index(model)
model_version = self.model_versions[model_index]
except Exception:
model_version = None
if model_version in ("v8", "v9", "v10", "v11", "v12"):
v8_v9_files = [
f"{model}_driving_policy_tinygrad.pkl",
f"{model}_driving_vision_tinygrad.pkl",
f"{model}_driving_policy_metadata.pkl",
f"{model}_driving_vision_metadata.pkl",
]
if model_version == "v12":
v8_v9_files += [
f"{model}_driving_off_policy_tinygrad.pkl",
f"{model}_driving_off_policy_metadata.pkl",
]
if all((MODELS_PATH / f).is_file() for f in v8_v9_files):
downloaded_models.add(model)
elif model_version == "v7":
filename = f"{model}.pkl"
if (MODELS_PATH / filename).is_file():
downloaded_models.add(model)
else:
filename = f"{model}.thneed"
if (MODELS_PATH / filename).is_file():
downloaded_models.add(model)
outdated_models = downloaded_models - available_models
for model in outdated_models:
for model_file in MODELS_PATH.glob(f"{model}*"):
print(f"Removing outdated model: {model_file}")
delete_file(model_file)
@@ -65,466 +246,185 @@ class ModelManager:
if tmp_file.is_file():
delete_file(tmp_file)
if params.get("Model", encoding="utf-8").removesuffix("_default") not in self.available_models:
if params.get("Model", encoding="utf-8") not in self.available_models:
params.put("Model", params_default.get("Model", encoding="utf-8"))
if not (not boot_run and params.get_bool("AutomaticallyDownloadModels")):
automatically_download_models = params.get_bool("AutomaticallyDownloadModels")
if not automatically_download_models:
return
model_sizes = self.fetch_all_model_sizes(repo_url)
if not model_sizes:
print("No model size data available. Skipping model checks...")
return
print("No model size data available. Continuing downloads based on file existence")
# do not return; proceed to download missing files
need_to_update_models = False
for model in self.available_models:
if self.is_tinygrad_model(model):
model_file = MODELS_PATH / f"{model}.thneed"
if not model_file.is_file():
need_to_update_models = True
continue
needs_download = False
expected_size = model_sizes.get(model_file.name)
local_size = self.model_sizes.get(model_file.name)
# Enhanced model file validation per model version
for model in available_models:
model_version = None
try:
model_index = self.available_models.index(model)
model_version = self.model_versions[model_index]
except Exception:
model_version = None
if expected_size > 0 and local_size != expected_size:
print(f"Model {model} is outdated. Deleting {model_file}...")
delete_file(model_file)
need_to_update_models = True
else:
model_missing = False
model_outdated = False
for filename, _ in TINYGRAD_FILES:
expected_file = MODELS_PATH / f"{model}_{filename}"
if not expected_file.is_file():
model_missing = True
need_to_update_models = True
if model_version in ("v8", "v9", "v10", "v11", "v12"):
v8_v9_files = [
f"{model}_driving_policy_tinygrad.pkl",
f"{model}_driving_vision_tinygrad.pkl",
f"{model}_driving_policy_metadata.pkl",
f"{model}_driving_vision_metadata.pkl",
]
if model_version == "v12":
v8_v9_files += [
f"{model}_driving_off_policy_tinygrad.pkl",
f"{model}_driving_off_policy_metadata.pkl",
]
for filename in v8_v9_files:
path = MODELS_PATH / filename
expected_size = model_sizes.get(filename.rsplit(".", 1)[0])
if not path.is_file() or expected_size is None or path.stat().st_size != expected_size:
needs_download = True
break
for filename, _ in TINYGRAD_FILES:
model_file = f"{model}_{filename}"
expected_size = model_sizes.get(model_file)
local_size = self.model_sizes.get(model_file)
if expected_size > 0 and local_size != expected_size:
model_outdated = True
need_to_update_models = True
break
if model_missing or model_outdated:
print(f"Model {model} is either missing required files or outdated. Deleting...")
for filename, _ in TINYGRAD_FILES:
delete_file(MODELS_PATH / f"{model}_{filename}")
if need_to_update_models:
params_memory.put_bool(MODEL_DOWNLOAD_ALL_PARAM, True)
def check_tinygrad(self, repo_url):
tinygrad_url = f"{repo_url}/Tinygrad/{TAR_FILE_NAME}"
expected_size = get_remote_file_size(tinygrad_url, self.session)
local_size = int(self.tinygrad_sizes.get(TAR_FILE_NAME, 0))
if expected_size > 0 and local_size != expected_size:
print(f"Tinygrad version {VERSION} is outdated, expected_size: {expected_size}, local_size: {local_size}, flagging for update...")
params.put_bool("TinygradUpdateAvailable", True)
def copy_default_model(self):
classic_default_model_path = MODELS_PATH / "wd-40.thneed"
source_path = Path(__file__).parents[1] / "classic_modeld/models/supercombo.thneed"
if source_path.is_file() and (not classic_default_model_path.is_file() or source_path.stat().st_size != classic_default_model_path.stat().st_size):
shutil.copyfile(source_path, classic_default_model_path)
print(f"Copied the classic default model from {source_path} to {classic_default_model_path}")
self.update_model_size(classic_default_model_path)
default_model_path = MODELS_PATH / "national-public-radio.thneed"
source_path = Path(__file__).parents[2] / "selfdrive/modeld/models/supercombo.thneed"
if source_path.is_file() and (not default_model_path.is_file() or source_path.stat().st_size != default_model_path.stat().st_size):
shutil.copyfile(source_path, default_model_path)
print(f"Copied the default model from {source_path} to {default_model_path}")
self.update_model_size(default_model_path)
for filename, description in TINYGRAD_FILES:
source = TINYGRAD_MODELD_PATH / "models" / filename
target = MODELS_PATH / f"{DEFAULT_MODEL}_{filename}"
if source.is_file() and (not target.is_file() or source.stat().st_size != target.stat().st_size):
shutil.copyfile(source, target)
print(f"Copied the tinygrad {description} from {source} to {target}")
def download_all_models(self):
repo_url = get_repository_url(self.session)
if not repo_url:
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
return
self.fetch_models(f"{repo_url}/Versions/model_names_{VERSION}.json", repo_url)
for model in self.available_models:
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM)
return
if self.is_tinygrad_model(model):
already_downloaded = (MODELS_PATH / f"{model}.thneed").is_file()
elif model_version == "v7":
filename = f"{model}.pkl"
path = MODELS_PATH / filename
expected_size = model_sizes.get(model)
if not path.is_file() or expected_size is None or path.stat().st_size != expected_size:
needs_download = True
else:
already_downloaded = all((MODELS_PATH / f"{model}_{filename}").is_file() for filename, _ in TINYGRAD_FILES)
filename = f"{model}.thneed"
path = MODELS_PATH / filename
expected_size = model_sizes.get(model)
if not path.is_file() or expected_size is None or path.stat().st_size != expected_size:
needs_download = True
if already_downloaded:
continue
if needs_download:
self.download_all_models()
print(f"Model {model} is not downloaded. Preparing to download...")
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloading \"{self.available_model_names[self.available_models.index(model)]}\"...")
self.download_model(model)
def update_model_params(self, model_info, repo_url):
self.available_models = [model["id"] for model in model_info]
self.model_versions = [model["version"] for model in model_info]
self.model_series = [model.get("series", "Dom Forgot To Label Me") for model in model_info]
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "All models downloaded!")
params_memory.remove(MODEL_DOWNLOAD_ALL_PARAM)
params.put("AvailableModels", ",".join(self.available_models))
params.put("AvailableModelNames", ",".join([model["name"] for model in model_info]))
params.put("AvailableModelSeries", ",".join(self.model_series))
params.put("CommunityFavorites", ",".join([model["id"] for model in model_info if model.get("community_favorite", False)]))
params.put("ModelReleasedDates", ",".join([model.get("released", "2023-01-01") for model in model_info]))
params.put("ModelVersions", ",".join(self.model_versions))
params.put("CommunityFavorites", ",".join([model["id"] for model in model_info if model.get("community_favorite", False)]))
params.put("AvailableModelSeries", ",".join(self.model_series))
print("Models list updated successfully")
def download_model(self, model_to_download):
self.downloading_model = True
# --- Generate per-model version JSON for offline UI ---
try:
versions_file = MODELS_PATH / ".model_versions.json"
version_map = {model_id: version for model_id, version in zip(self.available_models, self.model_versions)}
with open(versions_file, "w") as vf:
json.dump(version_map, vf)
except Exception as e:
print(f"Failed to write .model_versions.json: {e}")
# --- end JSON generation ---
repo_url = get_repository_url(self.session)
if not repo_url:
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
self.downloading_model = False
return
# Immediately sync the active ModelVersion param
try:
current = params.get("Model", encoding="utf-8")
if current in version_map:
params.put("ModelVersion", version_map[current])
print(f"Successfully synced ModelVersion to {version_map[current]} for model {current}")
else:
print(f"Warning: Model {current} not found in version map")
except Exception as e:
print(f"Failed to sync ModelVersion for {current}: {e}")
if self.is_tinygrad_model(model_to_download):
model_path = MODELS_PATH / f"{model_to_download}.thneed"
model_url = f"{repo_url}/Models/{model_to_download}.thneed"
# Also ensure ModelVersion is set for the default model if not already set
try:
if not params.get("ModelVersion", encoding="utf-8"):
default_model = params.get("Model", encoding="utf-8") or DEFAULT_MODEL
if default_model in version_map:
params.put("ModelVersion", version_map[default_model])
print(f"Set default ModelVersion to {version_map[default_model]} for model {default_model}")
except Exception as e:
print(f"Failed to set default ModelVersion: {e}")
print(f"Downloading model: {model_to_download}")
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, model_url, MODEL_DOWNLOAD_PARAM, self.session)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
delete_file(model_path)
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
self.downloading_model = False
return
if verify_download(model_path, model_url, self.session):
print(f"Model {model_to_download} downloaded and verified successfully!")
self.update_model_size(model_path)
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(MODEL_DOWNLOAD_PARAM)
self.downloading_model = False
return
print(f"Verification failed for model {model_to_download}. Retrying from GitLab...")
fallback_url = f"{GITLAB_URL}/Models/{model_to_download}.thneed"
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, fallback_url, MODEL_DOWNLOAD_PARAM, self.session)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
delete_file(model_path)
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
self.downloading_model = False
return
if verify_download(model_path, fallback_url, self.session):
print(f"Model {model_to_download} downloaded and verified successfully from GitLab!")
self.update_model_size(model_path)
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(MODEL_DOWNLOAD_PARAM)
self.downloading_model = False
else:
handle_error(model_path, "Verification failed...", "GitLab verification failed", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
self.downloading_model = False
else:
all_model_sizes = self.fetch_all_model_sizes(repo_url) or {}
tinygrad_filenames = [f"{model_to_download}_{file_key}" for file_key, _ in TINYGRAD_FILES]
file_sizes = []
file_sources = []
missing = [name for name in tinygrad_filenames if int(all_model_sizes.get(name, 0)) <= 0]
if missing:
handle_error(None, "Missing size metadata...", f"Sizes not found for: {', '.join(missing)}...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
self.downloading_model = False
return
for filename in tinygrad_filenames:
primary_url = f"{repo_url}/Models/compiled/{filename}"
file_size = int(all_model_sizes.get(filename, 0))
file_sizes.append(file_size)
file_sources.append((primary_url, None))
downloaded_offset_bytes = 0
known_file_sizes = [size for size in file_sizes if size > 0]
total_model_bytes = sum(known_file_sizes) if len(known_file_sizes) == len(file_sizes) else 0
for (file_key, description), part_bytes, (primary_url, fallback_url) in zip(TINYGRAD_FILES, file_sizes, file_sources):
filename = f"{model_to_download}_{file_key}"
model_path = MODELS_PATH / filename
print(f"Downloading {description} for model: {model_to_download}")
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, primary_url, MODEL_DOWNLOAD_PARAM, self.session, offset_bytes=downloaded_offset_bytes, total_bytes=total_model_bytes)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
delete_file(model_path)
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
self.downloading_model = False
return
if verify_download(model_path, primary_url, self.session):
print(f"{description.capitalize()} for {model_to_download} downloaded and verified successfully!")
if total_model_bytes:
downloaded_offset_bytes += part_bytes
continue
print(f"Verification failed for {filename}. Retrying from GitLab...")
fallback_url = f"{GITLAB_URL}/Models/compiled/{filename}"
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, fallback_url, MODEL_DOWNLOAD_PARAM, self.session, offset_bytes=downloaded_offset_bytes, total_bytes=total_model_bytes)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
delete_file(model_path)
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
self.downloading_model = False
return
if verify_download(model_path, fallback_url, self.session):
print(f"{description.capitalize()} for {model_to_download} downloaded and verified successfully from GitLab!")
if total_model_bytes:
downloaded_offset_bytes += part_bytes
else:
handle_error(model_path, "Verification failed...", f"GitLab verification failed for {filename}", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
self.downloading_model = False
return
print(f"Updating model sizes for {model_to_download}...")
for filename, _ in TINYGRAD_FILES:
file_path = MODELS_PATH / f"{model_to_download}_{filename}"
self.update_model_size(file_path)
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(MODEL_DOWNLOAD_PARAM)
self.downloading_model = False
def fetch_all_model_sizes(self, repo_url):
is_github = "github" in repo_url
is_gitlab = "gitlab" in repo_url
repo_encoded = quote_plus(RESOURCES_REPO)
model_sizes = {}
try:
def fetch_dir_sizes(api_url):
sizes = {}
print(f"Fetching model metadata: {api_url}")
response = self.session.get(api_url, timeout=10)
response.raise_for_status()
content = response.json()
model_files = [file for file in content if "." in file["name"]]
if is_github:
for file in model_files:
sizes[file["name"]] = file.get("size", 0)
else:
for file in model_files:
file_path = quote_plus(file["path"])
metadata_url = f"https://gitlab.com/api/v4/projects/{repo_encoded}/repository/files/{file_path}/raw?ref=Models"
head_response = self.session.head(metadata_url, timeout=10)
if head_response.ok:
sizes[file["name"]] = int(head_response.headers.get("content-length", 0))
return sizes
if is_github:
top_api_url = f"https://api.github.com/repos/{RESOURCES_REPO}/contents?ref=Models"
version_api_url = f"https://api.github.com/repos/{RESOURCES_REPO}/contents/compiled?ref=Models"
elif is_gitlab:
top_api_url = f"https://gitlab.com/api/v4/projects/{repo_encoded}/repository/tree?ref=Models"
version_api_url = f"https://gitlab.com/api/v4/projects/{repo_encoded}/repository/tree?path=compiled&ref=Models"
else:
print(f"Unsupported repository URL: {repo_url}")
return model_sizes
model_sizes.update(fetch_dir_sizes(top_api_url))
model_sizes.update(fetch_dir_sizes(version_api_url))
return model_sizes
except requests.exceptions.RequestException as e:
handle_request_error(f"Failed to fetch model sizes from {'GitHub' if is_github else 'GitLab'}: {e}", None, None, None)
return {}
def fetch_models(self, url, repo_url, boot_run=False):
try:
response = self.session.get(url, timeout=10)
response.raise_for_status()
model_info = response.json().get("models", [])
if model_info:
self.update_model_params(model_info)
self.check_models(boot_run, repo_url)
self.check_tinygrad(repo_url)
except Exception as exception:
handle_request_error(exception, None, None, None)
return []
def is_tinygrad_model(self, model):
return self.model_versions[self.available_models.index(model)] in {"v1", "v2", "v3", "v4", "v5", "v6"}
def update_model_params(self, model_info):
self.available_models = [model["id"] for model in model_info]
self.available_model_names = [model["name"] for model in model_info]
self.model_versions = [model["version"] for model in model_info]
params.put("AvailableModels", ",".join(self.available_models))
params.put("AvailableModelNames", ",".join(self.available_model_names))
params.put("ModelVersions", ",".join(self.model_versions))
print("Models list updated successfully!")
def update_models(self, boot_run):
def update_models(self, boot_run=False):
if self.downloading_model:
return
repo_url = get_repository_url(self.session)
repo_url = get_repository_url()
if repo_url is None:
print("GitHub and GitLab are offline...")
return
self.fetch_models(f"{repo_url}/Versions/model_names_{VERSION}.json", repo_url, boot_run)
model_info = self.fetch_models(f"{repo_url}/Versions/model_names_{VERSION}.json")
if model_info:
self.update_model_params(model_info, repo_url)
self.check_models(boot_run, repo_url)
def update_model_size(self, file_path):
self.model_sizes[file_path.name] = file_path.stat().st_size
update_json_file(self.model_sizes_path, self.model_sizes)
print(f"Updated size for {file_path.name} in {self.model_sizes_path.name}")
# Ensure ModelVersion is set immediately after updating model params
if boot_run:
try:
current = params.get("Model", encoding="utf-8")
if current and current in [model["id"] for model in model_info]:
model_index = [model["id"] for model in model_info].index(current)
version = model_info[model_index]["version"]
params.put("ModelVersion", version)
print(f"Boot sync: Set ModelVersion to {version} for model {current}")
except Exception as e:
print(f"Boot sync failed: {e}")
def update_tinygrad_size(self, file_path):
self.tinygrad_sizes[TAR_FILE_NAME] = file_path.stat().st_size
update_json_file(self.tinygrad_sizes_path, self.tinygrad_sizes)
print(f"Updated size for {TAR_FILE_NAME} in {self.tinygrad_sizes_path.name}")
def update_tinygrad(self):
repo_url = get_repository_url(self.session)
def download_all_models(self):
repo_url = get_repository_url()
if not repo_url:
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", None, None)
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
return
primary_url = f"{repo_url}/Tinygrad/{TAR_FILE_NAME}"
fallback_url = f"https://gitlab.com/{RESOURCES_REPO}/-/raw/Tinygrad/{TAR_FILE_NAME}"
model_info = self.fetch_models(f"{repo_url}/Versions/model_names_{VERSION}.json")
if model_info:
available_models = [model["id"] for model in model_info]
available_model_names = [re.sub(r"[🗺️👀📡]", "", model["name"]).strip() for model in model_info]
model_versions = [model["version"] for model in model_info]
model_series = [model.get("series", "Dom Forgot To Label Me") for model in model_info]
tinygrad_tar_path = Path("/data/tmp/tinygrad.tar.gz")
try:
print(f"Attempting to download tinygrad from {primary_url}...")
download_file(CANCEL_DOWNLOAD_PARAM, tinygrad_tar_path, DOWNLOAD_PROGRESS_PARAM, primary_url, UPDATE_TINYGRAD_PARAM, self.session)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
delete_file(tinygrad_tar_path)
handle_error(None, "Tinygrad update cancelled...", "Tinygrad update cancelled...", UPDATE_TINYGRAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
params_memory.remove("CancelModelDownload")
return
if not verify_download(tinygrad_tar_path, primary_url, self.session):
print(f"Verification failed for {primary_url}. Retrying from GitLab...")
download_file(CANCEL_DOWNLOAD_PARAM, tinygrad_tar_path, DOWNLOAD_PROGRESS_PARAM, fallback_url, UPDATE_TINYGRAD_PARAM, self.session)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
delete_file(tinygrad_tar_path)
handle_error(None, "Tinygrad update cancelled...", "Tinygrad update cancelled...", UPDATE_TINYGRAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
params_memory.remove("CancelModelDownload")
return
if not verify_download(tinygrad_tar_path, fallback_url, self.session):
handle_error(tinygrad_tar_path, "Verification Failed", "Tinygrad verification failed", UPDATE_TINYGRAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
return
print("Tinygrad downloaded successfully! Proceeding with installation...")
self.update_tinygrad_size(tinygrad_tar_path)
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Installing...")
print("Deleting old tinygrad directories...")
delete_file(TINYGRAD_MODELD_PATH)
print(f"Removed {TINYGRAD_MODELD_PATH}")
delete_file(TINYGRAD_REPO_PATH)
print(f"Removed {TINYGRAD_REPO_PATH}")
extract_tar(tinygrad_tar_path, Path(BASEDIR))
print("Tinygrad update completed successfully!")
params.put_bool("TinygradUpdateAvailable", False)
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Updated!")
params_memory.remove(UPDATE_TINYGRAD_PARAM)
self.update_tinygrad_models(repo_url)
except Exception as exception:
handle_error(tinygrad_tar_path, "Update Failed", f"An unexpected error occurred: {exception}", UPDATE_TINYGRAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
def update_tinygrad_models(self, repo_url=None):
print("Updating old Tinygrad models...")
installed_tinygrad_models = set()
for filename, _ in TINYGRAD_FILES:
suffix = f"_{filename}"
for file_path in MODELS_PATH.glob(f"*{suffix}"):
model_name = file_path.name.rsplit(suffix, 1)[0]
if model_name in set(self.available_models):
installed_tinygrad_models.add(model_name)
delete_file(file_path)
self.copy_default_model()
update_frogpilot_toggles()
if repo_url is None:
return
current_model = params.get("Model", encoding="utf-8").removesuffix("_default")
models_to_redownload = [current_model]
models_to_redownload += [model for model in sorted(installed_tinygrad_models) if model != current_model]
if DEFAULT_MODEL in models_to_redownload:
models_to_redownload.remove(DEFAULT_MODEL)
if models_to_redownload:
print(f"Redownloading the following models: {', '.join(models_to_redownload)}")
self.fetch_models(f"{repo_url}/Versions/model_names_{VERSION}.json", repo_url, boot_run=True)
for model in models_to_redownload:
for model, model_name, model_version in zip(available_models, available_model_names, model_versions):
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM)
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
return
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloading \"{self.available_model_names[self.available_models.index(model)]}\"...")
self.download_model(model)
if model_version in ("v8", "v9", "v10", "v11", "v12"):
required_files = [
f"{model}_driving_policy_tinygrad.pkl",
f"{model}_driving_vision_tinygrad.pkl",
f"{model}_driving_policy_metadata.pkl",
f"{model}_driving_vision_metadata.pkl",
]
if model_version == "v12":
required_files += [
f"{model}_driving_off_policy_tinygrad.pkl",
f"{model}_driving_off_policy_metadata.pkl",
]
missing = [f for f in required_files if not (MODELS_PATH / f).is_file()]
if missing:
print(f"Tinygrad model {model} is missing files. Preparing to download...")
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloading \"{model_name}\"...")
self.download_model(model)
elif model_version == "v7":
# OG tinygrad: only need PKL file
model_file = MODELS_PATH / f"{model}.pkl"
if not model_file.is_file():
print(f"PKL model {model} is missing. Preparing to download...")
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloading \"{model_name}\"...")
self.download_model(model)
else:
# Classic: only need .thneed
model_file = MODELS_PATH / f"{model}.thneed"
if not model_file.is_file():
print(f"Classic model {model} is missing. Preparing to download...")
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloading \"{model_name}\"...")
self.download_model(model)
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "All models downloaded!")
else:
print("No previously installed tinygrad models to redownload")
update_frogpilot_toggles()
def validate_models(self):
current = params.get("Model", encoding="utf-8")
default = params_default.get("Model", encoding="utf-8")
if current.endswith("_default") and current != default:
print(f"Model '{current}' does not match default '{default}', resetting...")
params.put("Model", default)
if VERSION_PATH.is_file():
version_name = VERSION_PATH.read_text().strip()
if version_name != VERSION or int(self.tinygrad_sizes.get(TAR_FILE_NAME, 0)) == 0:
self.update_tinygrad_models()
self.tinygrad_sizes[TAR_FILE_NAME] = DEFAULT_TINYGRAD_SIZE
update_json_file(self.tinygrad_sizes_path, self.tinygrad_sizes)
print(f"Updated size for {TAR_FILE_NAME} in {self.tinygrad_sizes_path.name}")
params.remove("TinygradUpdateAvailable")
VERSION_PATH.write_text(VERSION)
print(f"Updated {VERSION_PATH} to {VERSION}")
handle_error(None, "Unable to fetch models...", "Model list unavailable", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
@@ -0,0 +1 @@
{"input_std":[[9.281861],[1.8477924],[0.7977224],[0.047467366],[1.7969038],[1.8168812],[1.8351218],[1.785757],[1.7335743],[1.6658221],[1.5893887],[0.047346078],[0.0473731],[0.047383286],[0.047291175],[0.047300573],[0.04712479],[0.046799928]],"model_test_loss":0.027355343103408813,"input_size":18,"current_date_and_time":"2023-08-05_05-11-44","input_mean":[[21.655252],[-0.07694559],[-0.006081294],[-0.007598456],[-0.07309746],[-0.075890236],[-0.07804942],[-0.076106496],[-0.07184982],[-0.06528668],[-0.060404416],[-0.0077262702],[-0.0077046235],[-0.0076850485],[-0.007687444],[-0.007708868],[-0.0077959923],[-0.008017422]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.35776415],[-0.126288],[0.007988413],[0.03850029],[-0.056796823],[0.0072075897],[0.17376427]],"dense_1_W":[[0.010538558,6.7293744,-0.007391298,-1.035045,-0.47446147,1.0940393,0.08225446,-0.5795131,0.573115,1.82923,-0.16999252,0.8818023,0.03560901,-0.9470653,-1.042151,1.1532398,1.9009485,-1.2265956],[0.009010456,-1.3618345,0.039026346,0.22943486,-0.6669799,-0.768271,-0.16828564,-0.13406865,0.19760236,-0.18953702,-0.019137766,0.1468623,0.26367718,0.4732375,0.73679143,0.62450147,0.30198866,-0.93340015],[-0.044318866,2.2138615,9.620889,-0.66480356,-0.06849887,-0.038568456,-0.30580127,-0.088089585,0.31187677,0.5968372,-1.1462088,0.13234706,0.29166338,-0.69978577,-0.01790655,-0.097928315,-0.56950736,0.7560642],[1.3758256,0.8700429,-0.24295154,0.20832618,-0.7735097,1.0379355,-0.60753095,-0.5457418,-1.0892137,-0.6708325,0.24982774,0.09251328,0.08596103,-0.58034134,-0.3046114,-0.19754426,0.006461508,0.5989185],[0.06314328,-2.492993,-0.28200585,0.49902886,0.8513198,-0.5704965,1.0960953,-0.08426956,-0.76074624,0.10669376,0.02775254,-0.42194095,-0.34657434,-0.10863868,0.17019278,0.0963825,-0.10169116,0.21243806],[0.007984091,-1.9276509,-0.0013926353,0.04057924,1.4438432,1.3440262,-2.506722,1.700801,0.72410774,0.13471895,-0.18753268,0.7303607,0.018133093,-0.71643597,0.07050904,-0.30161944,0.085108586,0.061202843],[0.9413167,-0.9954305,-0.19510143,-0.4711845,-0.12398665,-0.45175523,1.0616771,0.28550953,-0.55543137,-0.21576759,-0.2364377,-0.012782618,-0.050565187,0.257694,-0.20975965,0.00022543047,-0.08760746,0.4983852]],"activation":"σ"},{"dense_2_W":[[-0.71804935,0.017023304,-0.099854074,-0.3262651,-0.402829,-0.12073083,0.13537839],[-0.44002575,-0.1071093,-0.58886194,-0.17319413,-0.21134079,0.23135148,0.0006880979],[-0.55711204,-0.8693639,-0.6443761,-0.7382846,0.18980889,-0.6082511,-0.63103676],[0.11338112,-0.5327033,0.2856197,0.32239884,-0.72682667,0.5302305,-0.45094746],[-0.35197002,-0.14440043,-0.025249843,-0.48697743,0.3340018,-0.25992322,0.0050561456],[0.34757507,0.15670523,-0.14263277,0.50704384,-0.020171141,0.6408679,-0.3074123],[0.40258837,-0.59363264,0.35948923,-0.060853776,-0.0072541097,0.89743376,-0.49789146],[-0.63476,0.29580453,0.1689314,-0.5061521,0.24901053,-0.23218995,0.57968193],[-0.66173035,0.50861925,-0.5845035,-0.6602214,0.8341883,-0.31437424,0.8046359],[0.038493533,0.15464582,-0.04848341,-0.57820857,-0.25891733,-0.47527292,0.21441916],[-0.15464531,-0.07454202,-0.8215851,-0.12614948,-0.5924209,0.00017916794,0.24154592],[0.6091424,-0.112086505,0.1144111,-0.31035227,-0.9237534,0.041003596,-0.3542808],[0.2989792,-0.23780549,0.116059326,-0.6056522,0.5499526,-0.9001413,0.5200723]],"activation":"σ","dense_2_b":[[-0.2638964],[-0.24607328],[-0.15493082],[-0.0071187904],[-0.23515384],[-0.09098133],[-0.034066215],[0.03609036],[-0.03842933],[-0.042302527],[-0.2439947],[-0.16342077],[-0.01030317]]},{"dense_3_W":[[0.45771673,-0.2084603,0.3570187,0.35835707,0.13684571,-0.582958,0.29889587,0.43543494,0.11841301,-0.26185623,-0.4988437,0.5752996,-0.28057483],[-0.19594137,0.050280698,-0.29057446,-0.2829161,-0.16987291,-0.21278952,-0.59946907,0.21295123,0.7040468,0.53549695,-0.52553934,-0.19560973,0.6233473],[-0.19091003,0.08669418,-0.5792192,0.57799137,-0.36263424,0.6037143,0.27898273,-0.30951327,-0.3572644,0.46720102,-0.5403428,0.32415462,-0.60570025]],"activation":"identity","dense_3_b":[[-0.0047377176],[0.023170695],[-0.021546118]]},{"dense_4_W":[[-0.12453941,-1.006034,0.96776205]],"dense_4_b":[[-0.02351544]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 912 KiB

After

Width:  |  Height:  |  Size: 503 KiB

+145 -55
View File
@@ -37,6 +37,7 @@ HOLIDAY_SLUGS = {
}
THEME_COMPONENT_PARAMS = {
"boot_logos": "BootLogoToDownload",
"colors": "ColorToDownload",
"distance_icons": "DistanceIconToDownload",
"icons": "IconToDownload",
@@ -93,16 +94,29 @@ class ThemeManager:
steering_wheel_save_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(steering_wheel_image_path, steering_wheel_save_path)
default_boot_logo_path = Path(__file__).parent / "other_images/frogpilot_boot_logo.png"
boot_logo_save_path = THEME_SAVE_PATH / "bootlogos/starpilot.png"
boot_logo_save_path.parent.mkdir(parents=True, exist_ok=True)
if not boot_logo_save_path.exists():
shutil.copy2(default_boot_logo_path, boot_logo_save_path)
def download_theme(self, theme_component, theme_name, asset_param, frogpilot_toggles):
self.downloading_theme = True
allow_unknown_size = theme_component in {"boot_logos", "steering_wheels"}
name_candidates = [theme_name]
repo_url = get_repository_url(self.session)
repo_url = get_repository_url()
if not repo_url:
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", asset_param, DOWNLOAD_PROGRESS_PARAM)
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_theme = False
return
if theme_component == "distance_icons":
if theme_component == "boot_logos":
download_link = f"{repo_url}/Themes/bootlogo"
download_path = THEME_SAVE_PATH / "bootlogos" / theme_name
extensions = [".png", ".jpg", ".jpeg"]
name_candidates = list(dict.fromkeys([theme_name, theme_name.replace("_", "-"), theme_name.replace("-", "_")]))
elif theme_component == "distance_icons":
download_link = f"{repo_url}/Distance-Icons/{theme_name}"
download_path = THEME_SAVE_PATH / "theme_packs" / theme_name / theme_component
extensions = [".zip"]
@@ -117,39 +131,42 @@ class ThemeManager:
for extension in extensions:
theme_path = download_path.with_suffix(extension)
theme_url = download_link + extension
theme_urls = [f"{download_link}/{candidate}{extension}" for candidate in name_candidates]
if theme_component != "boot_logos":
theme_urls = [download_link + extension]
delete_file(theme_path)
print(f"Downloading theme from GitHub: {theme_name}")
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, self.session)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
for theme_url in theme_urls:
delete_file(theme_path)
handle_error(None, "Download cancelled...", "Download cancelled...", asset_param, DOWNLOAD_PROGRESS_PARAM)
self.downloading_theme = False
return
print(f"Downloading theme from GitHub: {theme_name}")
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, params_memory, allow_unknown_size=allow_unknown_size, suppress_errors=allow_unknown_size)
if verify_download(theme_path, theme_url, self.session):
print(f"Theme {theme_name} downloaded and verified successfully from GitHub!")
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
delete_file(theme_path)
handle_error(None, "Download cancelled...", "Download cancelled...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
if extension == ".zip":
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Unpacking theme...")
extract_zip(theme_path, download_path)
self.downloading_theme = False
return
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(asset_param)
if verify_download(theme_path, theme_url, allow_unknown_size=allow_unknown_size):
print(f"Theme {theme_name} downloaded and verified successfully from GitHub!")
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
self.downloading_theme = False
if extension == ".zip":
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Unpacking theme...")
extract_zip(theme_path, download_path)
self.update_themes(frogpilot_toggles)
return
elif self.handle_verification_failure(extension, theme_component, theme_name, asset_param, theme_path, download_path, frogpilot_toggles):
return
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(asset_param)
handle_error(download_path, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM)
self.downloading_theme = False
self.update_themes(frogpilot_toggles)
return
elif self.handle_verification_failure(extension, theme_component, theme_name, asset_param, theme_path, download_path, frogpilot_toggles):
return
handle_error(download_path, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_theme = False
def fetch_assets(self, repo_url, frogpilot_toggles):
@@ -158,7 +175,7 @@ class ThemeManager:
repo_encoded = quote_plus(RESOURCES_REPO)
assets = {"themes": {}, "wheels": []}
assets = {"boot_logos": [], "themes": {}, "wheels": []}
try:
def list_files(branch):
if is_github:
@@ -234,6 +251,20 @@ class ThemeManager:
theme_name, sub_path = item["path"].split("/", 1)
theme_path = sub_path.lower()
if theme_name.lower() == "bootlogo":
if Path(sub_path).suffix.lower() not in (".png", ".jpg", ".jpeg"):
continue
assets["boot_logos"].append(sub_path)
logo_name = Path(sub_path).stem
local_files = list((THEME_SAVE_PATH / "bootlogos").glob(f"{logo_name}.*"))
if local_files and expected_size > 0:
local_size = self.theme_sizes.get("boot_logos", {}).get(logo_name)
if local_size != expected_size:
print(f"boot logo {logo_name} is outdated, redownloading...")
self.download_theme("boot_logos", logo_name, THEME_COMPONENT_PARAMS["boot_logos"], frogpilot_toggles)
continue
for key in ("colors", "icons", "signals", "sounds"):
if key in theme_path:
assets["themes"].setdefault(theme_name, set()).add(key)
@@ -246,13 +277,14 @@ class ThemeManager:
self.download_theme(key, theme_name, THEME_COMPONENT_PARAMS[key], frogpilot_toggles)
break
assets["boot_logos"].sort()
assets["themes"] = {key: sorted(list(value)) for key, value in assets["themes"].items()}
assets["wheels"].sort()
return assets
except requests.exceptions.RequestException as error:
print(f"Request failed: {error}")
handle_request_error(f"Failed to fetch theme sizes from {'GitHub' if is_github else 'GitLab'}: {error}", None, None, None)
handle_request_error(f"Failed to fetch theme sizes from {'GitHub' if is_github else 'GitLab'}: {error}", None, None, None, None)
return {}
@staticmethod
@@ -265,7 +297,7 @@ class ThemeManager:
parts = base.replace("_", "-").split("-")
capitalized_parts = [part.capitalize() for part in parts if part]
if len(capitalized_parts) > 1 and component != "steering_wheels":
if len(capitalized_parts) > 1 and component not in {"boot_logos", "steering_wheels"}:
display = f"{capitalized_parts[0]} ({' '.join(capitalized_parts[1:])})"
else:
display = " ".join(capitalized_parts)
@@ -321,37 +353,44 @@ class ThemeManager:
}
def handle_verification_failure(self, extension, theme_component, theme_name, asset_param, theme_path, download_path, frogpilot_toggles):
if theme_component == "distance_icons":
allow_unknown_size = theme_component in {"boot_logos", "steering_wheels"}
if theme_component == "boot_logos":
download_link = f"{GITLAB_URL}/Themes/bootlogo"
name_candidates = list(dict.fromkeys([theme_name, theme_name.replace("_", "-"), theme_name.replace("-", "_")]))
elif theme_component == "distance_icons":
download_link = f"{GITLAB_URL}/Distance-Icons/{theme_name}"
name_candidates = [theme_name]
elif theme_component == "steering_wheels":
download_link = f"{GITLAB_URL}/Steering-Wheels/{theme_name}"
name_candidates = [theme_name]
else:
download_link = f"{GITLAB_URL}/Themes/{theme_name}/{theme_component}"
name_candidates = [theme_name]
delete_file(theme_path)
for candidate in name_candidates:
delete_file(theme_path)
theme_url = download_link + extension
print(f"Downloading theme from GitLab: {theme_name}")
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, self.session)
theme_url = f"{download_link}/{candidate}{extension}" if theme_component == "boot_logos" else download_link + extension
print(f"Downloading theme from GitLab: {theme_name}")
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, params_memory, allow_unknown_size=allow_unknown_size, suppress_errors=allow_unknown_size)
if verify_download(theme_path, theme_url, self.session):
print(f"Theme {theme_name} downloaded and verified successfully from GitLab!")
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
if verify_download(theme_path, theme_url, allow_unknown_size=allow_unknown_size):
print(f"Theme {theme_name} downloaded and verified successfully from GitLab!")
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
if extension == ".zip":
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Unpacking theme...")
extract_zip(theme_path, download_path)
if extension == ".zip":
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Unpacking theme...")
extract_zip(theme_path, download_path)
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(asset_param)
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
params_memory.remove(asset_param)
self.downloading_theme = False
self.downloading_theme = False
self.update_themes(frogpilot_toggles)
return True
self.update_themes(frogpilot_toggles)
return True
handle_error(None, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM)
self.downloading_theme = False
# Let the caller continue trying alternate extensions before surfacing failure.
return False
@staticmethod
@@ -415,6 +454,8 @@ class ThemeManager:
return random.choice(candidates) if candidates else "stock"
def update_active_theme(self, time_validated, frogpilot_toggles, boot_run=False, randomize_theme=False):
boot_logo = getattr(frogpilot_toggles, "boot_logo", "stock")
if time_validated and frogpilot_toggles.holiday_themes:
self.holiday_theme = self.update_holiday()
else:
@@ -422,6 +463,7 @@ class ThemeManager:
if self.holiday_theme != "stock":
asset_mappings = {
"boot_logo": ("boot_logo", boot_logo),
"color_scheme": ("colors", self.holiday_theme),
"distance_icons": ("distance_icons", self.holiday_theme),
"icon_pack": ("icons", self.holiday_theme),
@@ -434,6 +476,7 @@ class ThemeManager:
selected_theme = self.randomize_theme_asset(available_themes)
asset_mappings = {
"boot_logo": ("boot_logo", boot_logo),
"color_scheme": ("colors", selected_theme.replace("-animated", "")),
"distance_icons": ("distance_icons", self.randomize_distance_icons(available_themes, selected_theme.replace("-animated", ""))),
"icon_pack": ("icons", selected_theme),
@@ -444,6 +487,7 @@ class ThemeManager:
elif not frogpilot_toggles.random_themes:
asset_mappings = {
"boot_logo": ("boot_logo", boot_logo),
"color_scheme": ("colors", frogpilot_toggles.color_scheme),
"distance_icons": ("distance_icons", frogpilot_toggles.distance_icons),
"icon_pack": ("icons", frogpilot_toggles.icon_pack),
@@ -458,7 +502,9 @@ class ThemeManager:
for asset, (asset_type, current_value) in asset_mappings.items():
print(f"Updating {asset}: {asset_type} with value {current_value}")
if asset_type == "wheel_image":
if asset_type == "boot_logo":
self.update_boot_logo(current_value)
elif asset_type == "wheel_image":
self.update_wheel_image(current_value, boot_run=boot_run)
else:
self.update_theme_asset(asset_type, current_value, boot_run=boot_run)
@@ -499,9 +545,16 @@ class ThemeManager:
save_location.symlink_to(asset_location, target_is_directory=True)
print(f"Linked {save_location} to {asset_location}")
def update_theme_params(self, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels):
def update_theme_params(self, downloadable_boot_logos, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels):
def update_param(key, assets, subfolder):
if subfolder == "steering_wheels":
if subfolder == "boot_logos":
themes_path = THEME_SAVE_PATH / "bootlogos"
existing_assets = {item.stem.lower() for item in themes_path.glob("*") if item.is_file()}
pending_assets = [asset for asset in assets if asset.lower() not in existing_assets]
params.put(key, ",".join(sorted(set(pending_assets))))
print(f"{key} updated successfully")
return
elif subfolder == "steering_wheels":
themes_path = THEME_SAVE_PATH / subfolder
existing_assets = {self.format_name(item.name, "steering_wheels") for item in themes_path.glob("*") if item.is_file()}
else:
@@ -511,6 +564,7 @@ class ThemeManager:
params.put(key, ",".join(sorted(set(assets) - existing_assets)))
print(f"{key} updated successfully")
update_param("DownloadableBootLogos", downloadable_boot_logos, "boot_logos")
update_param("DownloadableColors", downloadable_colors, "colors")
update_param("DownloadableDistanceIcons", downloadable_distance_icons, "distance_icons")
update_param("DownloadableIcons", downloadable_icons, "icons")
@@ -529,12 +583,20 @@ class ThemeManager:
theme_name = self.format_name(theme_dir.name, "theme_packs")
downloaded_themes[theme_name] = sorted(components)
(THEME_SAVE_PATH / "bootlogos").mkdir(parents=True, exist_ok=True)
downloaded_boot_logos = []
for boot_logo_file in (THEME_SAVE_PATH / "bootlogos").iterdir():
if boot_logo_file.is_file():
downloaded_boot_logos.append(self.format_name(boot_logo_file.name, "boot_logos"))
downloaded_wheels = []
for wheel_file in (THEME_SAVE_PATH / "steering_wheels").iterdir():
if wheel_file.is_file():
downloaded_wheels.append(self.format_name(wheel_file.name, "steering_wheels"))
params.put("ThemesDownloaded", json.dumps({
"boot_logos": sorted(downloaded_boot_logos),
"themes": {key: downloaded_themes[key] for key in sorted(downloaded_themes)},
"steering_wheels": sorted(downloaded_wheels)
}))
@@ -542,7 +604,9 @@ class ThemeManager:
print("ThemesDownloaded updated successfully")
def update_theme_size(self, theme_component, theme_name, file_size):
if theme_component == "steering_wheels":
if theme_component == "boot_logos":
key = "boot_logos"
elif theme_component == "steering_wheels":
key = "wheels"
else:
key = "themes"
@@ -550,7 +614,7 @@ class ThemeManager:
if key not in self.theme_sizes:
self.theme_sizes[key] = {}
if key == "wheels":
if key in {"boot_logos", "wheels"}:
self.theme_sizes[key][theme_name] = file_size
else:
if theme_name not in self.theme_sizes[key]:
@@ -563,7 +627,7 @@ class ThemeManager:
if self.downloading_theme:
return
repo_url = get_repository_url(self.session)
repo_url = get_repository_url()
if repo_url is None:
print("GitHub and GitLab are offline...")
return
@@ -572,6 +636,7 @@ class ThemeManager:
if not assets:
return
downloadable_boot_logos = []
downloadable_colors = []
downloadable_distance_icons = []
downloadable_icons = []
@@ -593,8 +658,10 @@ class ThemeManager:
if "sounds" in available_assets:
downloadable_sounds.append(theme_name)
downloadable_boot_logos = [Path(boot_logo).stem for boot_logo in assets["boot_logos"]]
downloadable_wheels = [self.format_name(wheel, "steering_wheels") for wheel in assets["wheels"]]
print(f"Downloadable Boot Logos: {downloadable_boot_logos}")
print(f"Downloadable Colors: {downloadable_colors}")
print(f"Downloadable Icons: {downloadable_icons}")
print(f"Downloadable Signals: {downloadable_signals}")
@@ -605,7 +672,21 @@ class ThemeManager:
if boot_run:
self.validate_themes(downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels, frogpilot_toggles)
self.update_theme_params(downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels)
self.update_theme_params(downloadable_boot_logos, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels)
@staticmethod
def update_boot_logo(image):
boot_logo_location = Path(__file__).parent / "other_images/frogpilot_boot_logo.png"
image_name = image.replace(" ", "_").lower()
source_file = next((file for file in (THEME_SAVE_PATH / "bootlogos").glob("*") if file.is_file() and file.stem.lower() == image_name), boot_logo_location)
# Avoid SameFileError when the selected/fallback source is already the active boot logo.
if source_file.resolve() == boot_logo_location.resolve():
print(f"Boot logo unchanged: {boot_logo_location}")
return
shutil.copy2(source_file, boot_logo_location)
print(f"Copied {source_file} to {boot_logo_location}")
def update_wheel_image(self, image, boot_run=False, random_event=False):
wheel_save_location = ACTIVE_THEME_PATH / "steering_wheel"
@@ -641,6 +722,15 @@ class ThemeManager:
def validate_themes(self, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels, frogpilot_toggles):
downloaded_data = json.loads(params.get("ThemesDownloaded") or "{}")
boot_logos_path = THEME_SAVE_PATH / "bootlogos"
for display_name in downloaded_data.get("boot_logos", []):
file_stem = display_name.replace(" ", "_").lower()
matching_files = list(boot_logos_path.glob(f"{file_stem}.*"))
if not matching_files:
print(f"Missing boot logo '{display_name}'. Downloading...")
self.download_theme("boot_logos", file_stem, THEME_COMPONENT_PARAMS["boot_logos"], frogpilot_toggles)
self.update_active_theme(True, frogpilot_toggles)
for display_name, components in downloaded_data.get("themes", {}).items():
raw_name = display_name.lower().replace(" ", "_").replace("(", "").replace(")", "")
theme_folder_name = raw_name.replace("_animated", "-animated")
+1 -1
View File
@@ -150,7 +150,7 @@ def frogpilot_boot_functions(build_metadata, params_cache):
params_cache.clear_all()
FrogPilotVariables().update(holiday_theme="stock", started=False)
ModelManager(boot_run=True)
ModelManager()
ThemeManager(boot_run=True).update_active_theme(time_validated=system_time_valid(), frogpilot_toggles=get_frogpilot_toggles(), boot_run=True)
if VIDEO_CACHE_PATH.exists():
+16 -5
View File
@@ -2,6 +2,7 @@
import json
import math
import numpy as np
import os
import requests
import shutil
import subprocess
@@ -22,7 +23,7 @@ from opendbc.can.parser import CANParser
from openpilot.common.realtime import DT_DMON, DT_HW
from openpilot.selfdrive.car.toyota.carcontroller import LOCK_CMD
from openpilot.system.hardware import HARDWARE
from panda import Panda
from panda import Panda, FW_PATH
from openpilot.frogpilot.common.frogpilot_variables import EARTH_RADIUS, KONIK_PATH, MAPD_PATH, MAPS_PATH, params, params_cache, params_memory
@@ -151,11 +152,21 @@ def extract_zip(zip_file, extract_path):
print(f"Extraction completed: {zip_file} has been removed")
def flash_panda():
remote_start = params.get_bool("RemoteStartBootsComma")
for serial in Panda.list():
try:
panda = Panda(serial)
flash_fn = None
if remote_start:
app_fn = panda.get_mcu_type().config.app_fn
remote_fn = "panda_h7_remote.bin.signed" if app_fn == "panda_h7.bin.signed" else "panda_remote.bin.signed"
candidate = os.path.join(FW_PATH, remote_fn)
if os.path.isfile(candidate):
flash_fn = candidate
else:
print(f"Remote-start panda firmware missing: {candidate}. Falling back to default firmware.")
panda.reset(enter_bootstub=True)
panda.flash()
panda.flash(fn=flash_fn)
panda.close()
except Exception as exception:
print(f"Error flashing Panda {serial}: {exception}")
@@ -296,12 +307,12 @@ def update_openpilot():
if params.get("UpdaterState", encoding="utf-8") != "idle":
return
while params.get_bool("IsOnroad") or params_memory.get_bool("UpdateSpeedLimits") or running_threads.get("lock_doors", threading.Thread()).is_alive():
time.sleep(60)
if not update_available():
return
while params.get_bool("IsOnroad") or params_memory.get_bool("UpdateSpeedLimits") or running_threads.get("lock_doors", threading.Thread()).is_alive():
time.sleep(60)
while True:
if not update_available():
break
+186 -101
View File
@@ -13,7 +13,8 @@ from openpilot.common.conversions import Conversions as CV
from openpilot.common.params import Params
from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.selfdrive.car.car_helpers import interfaces
from openpilot.selfdrive.car.gm.values import GMFlags
from openpilot.selfdrive.car.gm.values import EV_CAR as GM_EV_CAR, GMFlags
from openpilot.selfdrive.car.hyundai.values import EV_CAR as HYUNDAI_EV_CAR
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.mock.interface import CarInterface
from openpilot.selfdrive.car.mock.values import CAR as MOCK
@@ -32,6 +33,7 @@ params_memory = Params("/dev/shm/params")
GearShifter = car.CarState.GearShifter
SafetyModel = car.CarParams.SafetyModel
TransmissionType = car.CarParams.TransmissionType
CITY_SPEED_LIMIT = 25 # 55mph is typically the minimum speed for highways
CRUISING_SPEED = 5 # Roughly the speed cars go when not touching the gas while in drive
@@ -40,11 +42,16 @@ EARTH_RADIUS = 6378137 # Radius of the Earth in meters
MAX_T_FOLLOW = 3.0 # Maximum allowed following duration. Larger values risk losing track of the lead but may be increased as models improve
MINIMUM_LATERAL_ACCELERATION = 1.3 # m/s^2, typical minimum lateral acceleration when taking curves
PLANNER_TIME = ModelConstants.T_IDXS[-1] # Length of time the model projects out for
THRESHOLD = 0.63 # Requires the condition to be true for ~1 second
def scale_threshold(v_ego):#0 40 60 80 100 0 40 60 80 100
# More aggressive with hysteresis and lead probability: faster activation at higher speeds
return np.interp(v_ego, [0, 17.9, 26.8, 35.8, 44.7], [0.58, 0.60, 0.62, 0.75, 0.9])
NON_DRIVING_GEARS = [GearShifter.neutral, GearShifter.park, GearShifter.reverse, GearShifter.unknown]
RESOURCES_REPO = "FrogAi/FrogPilot-Resources"
RESOURCES_REPO = "firestar5683/StarPilot-Resources"
ACTIVE_THEME_PATH = Path(__file__).parents[1] / "assets/active_theme"
METADATAS_PATH = Path(__file__).parents[1] / "assets/model_metadata"
@@ -66,12 +73,11 @@ KONIK_PATH = Path("/cache/use_konik")
MAPD_PATH = Path("/data/media/0/osm/mapd")
MAPS_PATH = Path("/data/media/0/osm/offline")
NNFF_MODELS_PATH = Path(BASEDIR) / "frogpilot/assets/nnff_models"
DEFAULT_MODEL = "firehose"
DEFAULT_MODEL = "bd2"
DEFAULT_MODEL_NAME = "Firehose (Default) 👀📡"
DEFAULT_MODEL_VERSION = "v9"
DEFAULT_MODEL_VERSION = "v11"
BUTTON_FUNCTIONS = {
"NOTHING": 0,
@@ -90,12 +96,15 @@ EXCLUDED_KEYS = {
}
TINYGRAD_FILES = [
("driving_off_policy_metadata.pkl", "off-policy metadata"),
("driving_off_policy_tinygrad.pkl", "off-policy model"),
("driving_policy_metadata.pkl", "policy metadata"),
("driving_policy_tinygrad.pkl", "policy model"),
("driving_vision_metadata.pkl", "vision metadata"),
("driving_vision_tinygrad.pkl", "vision model"),
]
@cache
def get_nnff_model_files():
model_dir = Path(NNFF_MODELS_PATH)
@@ -117,13 +126,16 @@ def update_frogpilot_toggles():
frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("AccelerationPath", "1", 2, "0"),
("AccelerationProfile", "2", 0, "0"),
("AdjacentLeadsUI", "1", 3, "0"),
("AdjacentLeadsUI", "0", 3, "0"),
("AdjacentPath", "0", 3, "0"),
("AdjacentPathMetrics", "0", 3, "0"),
("AdvancedCustomUI", "0", 2, "0"),
("AdvancedLateralTune", "0", 3, "0"),
("AdvancedLateralTune", "1", 2, "0"),
("AdvancedLongitudinalTune", "0", 3, "0"),
("EVTuning", "", 3, "0"),
("TruckTuning", "0", 3, "0"),
("AggressiveFollow", "1.25", 2, "1.25"),
("AggressiveFollowHigh", "1.25", 2, "1.25"),
("AggressiveJerkAcceleration", "50", 3, "50"),
("AggressiveJerkDanger", "100", 3, "100"),
("AggressiveJerkDeceleration", "50", 3, "50"),
@@ -133,19 +145,23 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("AlertVolumeControl", "0", 2, "0"),
("AlwaysOnDM", "0", 0, "0"),
("AlwaysOnLateral", "1", 0, "0"),
("AlwaysOnLateralLKAS", "1", 2, "0"),
("AlwaysOnLateralMain", "1", 2, "0"),
("AlwaysOnLateralLKAS", "1", 0, "0"),
("AlwaysOnLateralMain", "1", 0, "0"),
("AMapKey1", "", 0, ""),
("AMapKey2", "", 0, ""),
("AutomaticallyDownloadModels", "1", 1, "0"),
("AutomaticUpdates", "1", 0, "1"),
("AvailableModelNames", "", 1, ""),
("AvailableModelSeries", "", 1, ""),
("AvailableModels", "", 1, ""),
("CommunityFavorites", "", 1, ""),
("UserFavorites", "", 0, ""),
("BigMap", "0", 2, "0"),
("BlacklistedModels", "", 2, ""),
("BlindSpotMetrics", "1", 3, "0"),
("BlindSpotPath", "1", 1, "0"),
("BorderMetrics", "0", 3, "0"),
("BootLogo", "starpilot", 0, "stock"),
("BorderMetrics", "1", 3, "0"),
("CalibratedLateralAcceleration", str(DEFAULT_LATERAL_ACCELERATION), 2, str(DEFAULT_LATERAL_ACCELERATION)),
("CalibrationProgress", "0", 3, "0"),
("CameraView", "3", 2, "0"),
@@ -157,16 +173,16 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("CECurvesLead", "0", 1, "0"),
("CELead", "0", 1, "0"),
("CEModelStopTime", str(PLANNER_TIME - 2), 2, "0"),
("CENavigation", "1", 2, "0"),
("CENavigationIntersections", "0", 2, "0"),
("CENavigation", "0", 2, "0"),
("CENavigationIntersections", "1", 2, "0"),
("CENavigationLead", "1", 2, "0"),
("CENavigationTurns", "1", 2, "0"),
("CESignalSpeed", "55", 2, "0"),
("CESignalLaneDetection", "1", 2, "0"),
("CESlowerLead", "0", 1, "0"),
("CESlowerLead", "1", 1, "0"),
("CESpeed", "0", 1, "0"),
("CESpeedLead", "0", 1, "0"),
("CEStoppedLead", "0", 1, "0"),
("CEStoppedLead", "1", 1, "0"),
("ClusterOffset", "1.015", 2, "1.015"),
("Compass", "0", 1, "0"),
("ConditionalExperimental", "1", 1, "0"),
@@ -184,7 +200,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("CustomUI", "1", 1, "0"),
("DecelerationProfile", "1", 2, "0"),
("DeveloperMetrics", "1", 3, "0"),
("DeveloperSidebar", "1", 3, "0"),
("DeveloperSidebar", "0", 3, "0"),
("DeveloperSidebarMetric1", "1", 3, "0"),
("DeveloperSidebarMetric2", "2", 3, "0"),
("DeveloperSidebarMetric3", "3", 3, "0"),
@@ -193,7 +209,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("DeveloperSidebarMetric6", "6", 3, "0"),
("DeveloperSidebarMetric7", "7", 3, "0"),
("DeveloperWidgets", "1", 3, "0"),
("DeveloperUI", "0", 3, "0"),
("DeveloperUI", "1", 3, "0"),
("DeviceManagement", "1", 1, "0"),
("DeviceShutdown", "9", 1, "33"),
("DisableOnroadUploads", "0", 2, "0"),
@@ -203,17 +219,17 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("DistanceButtonControl", "1", 2, "0"),
("DriverCamera", "0", 1, "0"),
("DynamicPathWidth", "0", 2, "0"),
("DynamicPedalsOnUI", "1", 1, "0"),
("DynamicPedalsOnUI", "1", 2, "0"),
("EngageVolume", "101", 2, "101"),
("ExperimentalGMTune", "0", 2, "0"),
("ExperimentalLongitudinalEnabled", "0", 0, "0"),
("ExperimentalModeConfirmed", "0", 0, "0"),
("Fahrenheit", "0", 3, "0"),
("FavoriteDestinations", "", 0, ""),
("ForceAutoTune", "0", 3, "0"),
("ForceAutoTuneOff", "0", 3, "0"),
("ForceAutoTune", "0", 2, "0"),
("ForceAutoTuneOff", "1", 2, "0"),
("ForceFingerprint", "0", 2, "0"),
("ForceMPHDashboard", "0", 3, "0"),
("ForceMPHDashboard", "0", 2, "0"),
("ForceStops", "0", 2, "0"),
("ForceTorqueController", "0", 3, "0"),
("FPSCounter", "1", 3, "0"),
@@ -222,6 +238,9 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("FrogsGoMoosTweak", "1", 2, "0"),
("FullMap", "0", 2, "0"),
("GasRegenCmd", "1", 2, "0"),
("GMPedalLongitudinal", "1", 2, "1"),
("RedPanda", "0", 3, "0"),
("RemoteStartBootsComma", "0", 3, "0"),
("GithubSshKeys", "", 0, ""),
("GithubUsername", "", 0, ""),
("GoatScream", "0", 1, "0"),
@@ -235,10 +254,10 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("HideMaxSpeed", "0", 2, "0"),
("HideSpeed", "0", 2, "0"),
("HideSpeedLimit", "0", 2, "0"),
("HigherBitrate", "0", 2, "0"),
("HigherBitrate", "0", 3, "0"),
("HolidayThemes", "1", 0, "0"),
("HumanAcceleration", "1", 2, "0"),
("HumanFollowing", "1", 2, "0"),
("HumanAcceleration", "0", 2, "0"),
("HumanFollowing", "0", 2, "0"),
("IncreasedStoppedDistance", "0", 1, "0"),
("IncreaseThermalLimits", "0", 2, "0"),
("IsLdwEnabled", "0", 0, "0"),
@@ -246,13 +265,13 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("KonikDongleId", "", 0, ""),
("KonikMinutes", "0", 0, "0"),
("LaneChanges", "1", 0, "1"),
("LaneChangeTime", "1.0", 1, "0"),
("LaneDetectionWidth", "0", 1, "0"),
("LaneChangeTime", "2.0", 0, "0"),
("LaneDetectionWidth", "0", 2, "0"),
("LaneLinesWidth", "4", 2, "2"),
("LateralTune", "1", 1, "0"),
("LateralTune", "1", 2, "0"),
("LeadDepartingAlert", "0", 0, "0"),
("LeadDetectionThreshold", "35", 3, "50"),
("LeadInfo", "1", 3, "0"),
("LeadInfo", "1", 2, "0"),
("LiveDelay", "", 0, ""),
("LKASButtonControl", "5", 2, "0"),
("LockDoors", "1", 0, "0"),
@@ -261,33 +280,36 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("LongitudinalActuatorDelay", "", 3, ""),
("LongitudinalActuatorDelayStock", "", 3, ""),
("LongitudinalTune", "1", 0, "0"),
("TrailerLoad", "0", 2, "0"),
("LongPitch", "1", 2, "0"),
("LoudBlindspotAlert", "0", 0, "0"),
("LowVoltageShutdown", str(VBATT_PAUSE_CHARGING), 3, str(VBATT_PAUSE_CHARGING)),
("LowVoltageShutdown", str(VBATT_PAUSE_CHARGING), 2, str(VBATT_PAUSE_CHARGING)),
("MapAcceleration", "0", 1, "0"),
("MapboxPublicKey", "", 0, ""),
("MapboxSecretKey", "", 0, ""),
("MapDeceleration", "0", 1, "0"),
("MapGears", "0", 2, "0"),
("MapGears", "0", 1, "0"),
("MapsSelected", "", 0, ""),
("MapStyle", "1", 2, "0"),
("MaxDesiredAcceleration", "4.0", 2, "2.0"),
("MinimumLaneChangeSpeed", str(LANE_CHANGE_SPEED_MIN / CV.MPH_TO_MS), 2, str(LANE_CHANGE_SPEED_MIN / CV.MPH_TO_MS)),
("Model", DEFAULT_MODEL + "_default", 1, DEFAULT_MODEL + "_default"),
("Model", DEFAULT_MODEL, 1, DEFAULT_MODEL),
("ModelDrivesAndScores", "", 2, ""),
("ModelRandomizer", "0", 2, "0"),
("ModelReleasedDates", "", 1, ""),
("ModelUI", "1", 2, "0"),
("ModelVersions", "", 2, ""),
("SortModelsByDate", "0", 2, "0"),
("NavigationUI", "1", 1, "0"),
("NavSettingLeftSide", "0", 0, "0"),
("NavSettingTime24h", "0", 0, "0"),
("NewLongAPI", "1", 3, "1"),
("NNFF", "1", 2, "0"),
("NNFFLite", "1", 2, "0"),
("NewLongAPI", "0", 2, "1"),
("NNFF", "0", 2, "0"),
("NNFFLite", "0", 2, "0"),
("NoLogging", "0", 2, "0"),
("NoUploads", "0", 2, "0"),
("NudgelessLaneChange", "1", 0, "0"),
("NumericalTemp", "1", 3, "0"),
("NudgelessLaneChange", "0", 0, "0"),
("NumericalTemp", "1", 2, "0"),
("Offset1", "5", 0, "0"),
("Offset2", "5", 0, "0"),
("Offset3", "5", 0, "0"),
@@ -300,15 +322,15 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("openpilotMinutes", "0", 0, "0"),
("PathEdgeWidth", "20", 2, "0"),
("PathWidth", "6.1", 2, "5.9"),
("PauseAOLOnBrake", "0", 1, "0"),
("PauseLateralOnSignal", "0", 1, "0"),
("PauseLateralSpeed", "0", 1, "0"),
("PedalsOnUI", "0", 1, "0"),
("PauseAOLOnBrake", "0", 2, "0"),
("PauseLateralOnSignal", "0", 2, "0"),
("PauseLateralSpeed", "0", 2, "0"),
("PedalsOnUI", "0", 2, "0"),
("PersonalizeOpenpilot", "1", 0, "0"),
("PreferredSchedule", "2", 0, "0"),
("PromptDistractedVolume", "101", 2, "101"),
("PromptVolume", "101", 2, "101"),
("QOLLateral", "1", 1, "0"),
("QOLLateral", "1", 2, "0"),
("QOLLongitudinal", "1", 1, "0"),
("QOLVisuals", "1", 0, "0"),
("RadarTracksUI", "0", 3, "0"),
@@ -318,19 +340,20 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("RecordFront", "0", 0, "0"),
("RefuseVolume", "101", 2, "101"),
("RelaxedFollow", "1.75", 2, "1.75"),
("RelaxedJerkAcceleration", "100", 3, "100"),
("RelaxedFollowHigh", "1.75", 2, "1.75"),
("RelaxedJerkAcceleration", "50", 3, "50"),
("RelaxedJerkDanger", "100", 3, "100"),
("RelaxedJerkDeceleration", "100", 3, "100"),
("RelaxedJerkSpeed", "100", 3, "100"),
("RelaxedJerkSpeedDecrease", "100", 3, "100"),
("RelaxedJerkDeceleration", "50", 3, "50"),
("RelaxedJerkSpeed", "50", 3, "50"),
("RelaxedJerkSpeedDecrease", "50", 3, "50"),
("RelaxedPersonalityProfile", "1", 2, "0"),
("ReverseCruise", "0", 1, "0"),
("ReverseCruise", "1", 1, "0"),
("RoadEdgesWidth", "2", 2, "2"),
("RoadNameUI", "1", 1, "0"),
("RoadNameUI", "1", 2, "0"),
("RotatingWheel", "1", 1, "0"),
("ScreenBrightness", "101", 2, "101"),
("ScreenBrightnessOnroad", "101", 2, "101"),
("ScreenManagement", "1", 1, "0"),
("ScreenManagement", "1", 2, "0"),
("ScreenRecorder", "1", 2, "0"),
("ScreenTimeout", "30", 2, "30"),
("ScreenTimeoutOnroad", "30", 2, "10"),
@@ -348,13 +371,13 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("ShownToggleDescriptions", "", 0, ""),
("ShowSLCOffset", "1", 0, "0"),
("ShowSpeedLimits", "1", 1, "0"),
("ShowSteering", "0", 3, "0"),
("ShowStoppingPoint", "1", 3, "0"),
("ShowStoppingPointMetrics", "1", 3, "0"),
("ShowSteering", "1", 3, "0"),
("ShowStoppingPoint", "0", 2, "0"),
("ShowStoppingPointMetrics", "0", 2, "0"),
("ShowStorageLeft", "0", 3, "0"),
("ShowStorageUsed", "0", 3, "0"),
("Sidebar", "0", 0, "0"),
("SignalMetrics", "0", 3, "0"),
("SignalMetrics", "0", 2, "0"),
("SLCConfirmation", "0", 0, "0"),
("SLCConfirmationHigher", "0", 0, "0"),
("SLCConfirmationLower", "0", 0, "0"),
@@ -367,28 +390,31 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("SLCPriority2", "Map Data", 2, "Map Data"),
("SLCPriority3", "Dashboard", 2, "Dashboard"),
("SNGHack", "1", 2, "0"),
("SpeedLimitChangedAlert", "0", 0, "0"),
("SpeedLimitChangedAlert", "1", 0, "0"),
("SpeedLimitController", "1", 0, "0"),
("SpeedLimitFiller", "0", 0, "0"),
("SpeedLimitSources", "0", 3, "0"),
("SpeedLimitSources", "0", 2, "0"),
("SshEnabled", "0", 0, "0"),
("StartupMessageBottom", "Human-tested, frog-approved 🐸", 0, "Always keep hands on wheel and eyes on road"),
("StartupMessageTop", "Hop in and buckle up!", 0, "Be ready to take over at any time"),
("StandardFollow", "1.45", 2, "1.45"),
("StandardJerkAcceleration", "100", 3, "100"),
("StandardFollowHigh", "1.45", 2, "1.45"),
("StandardJerkAcceleration", "50", 3, "50"),
("StandardJerkDanger", "100", 3, "100"),
("StandardJerkDeceleration", "100", 3, "100"),
("StandardJerkSpeed", "100", 3, "100"),
("StandardJerkSpeedDecrease", "100", 3, "100"),
("StandardJerkDeceleration", "50", 3, "50"),
("StandardJerkSpeed", "50", 3, "50"),
("StandardJerkSpeedDecrease", "50", 3, "50"),
("StandardPersonalityProfile", "1", 2, "0"),
("StandbyMode", "0", 1, "0"),
("StandbyMode", "0", 2, "0"),
("StartAccel", "", 3, ""),
("StartAccelStock", "", 3, ""),
("StaticPedalsOnUI", "0", 1, "0"),
("StaticPedalsOnUI", "0", 2, "0"),
("SteerDelay", "", 3, ""),
("SteerDelayStock", "", 3, ""),
("SteerFriction", "", 3, ""),
("SteerFrictionStock", "", 3, ""),
("SteerOffset", "", 3, ""),
("SteerOffsetStock", "", 3, ""),
("SteerKP", "", 3, ""),
("SteerKPStock", "", 3, ""),
("SteerLatAccel", "", 3, ""),
@@ -403,8 +429,6 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("TacoTune", "0", 2, "0"),
("TacoTuneHacks", "0", 2, "0"),
("TetheringEnabled", "0", 0, "0"),
("ThemesDownloaded", "", 0, ""),
("TinygradUpdateAvailable", "0", 1, "0"),
("ToyotaDoors", "1", 0, "0"),
("TrafficFollow", "0.5", 2, "0.5"),
("TrafficJerkAcceleration", "50", 3, "50"),
@@ -413,8 +437,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("TrafficJerkSpeed", "50", 3, "50"),
("TrafficJerkSpeedDecrease", "50", 3, "50"),
("TrafficPersonalityProfile", "1", 2, "0"),
("TuningLevel", "0", 0, "0"),
("TuningLevelConfirmed", "0", 0, "0"),
("TuningLevel", "3", 0, "0"),
("TuningLevelConfirmed", "1", 0, "0"),
("TurnDesires", "0", 2, "0"),
("UnlimitedLength", "1", 2, "0"),
("UnlockDoors", "1", 0, "0"),
@@ -431,7 +455,9 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("WarningImmediateVolume", "101", 2, "101"),
("WarningSoftVolume", "101", 2, "101"),
("WheelIcon", "frog", 0, "stock"),
("WheelSpeed", "0", 2, "0")
("WheelSpeed", "0", 2, "0"),
("StopDistance", "6", 3, "6"),
("RecoveryPower", "1.0", 2, "1.0")
]
misc_tuning_levels: list[tuple[str, str | bytes, int, str]] = [
@@ -526,6 +552,10 @@ class FrogPilotVariables:
safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
CP.safetyConfigs = [safety_config]
is_torque_car = CP.lateralTuning.which() == "torque"
if not is_torque_car:
CarInterfaceBase.configure_torque_tune(MOCK.MOCK, CP.lateralTuning)
fpmsg_bytes = params.get("FrogPilotCarParams" if started else "FrogPilotCarParamsPersistent", block=started)
if fpmsg_bytes:
with custom.FrogPilotCarParams.from_bytes(fpmsg_bytes) as fpcp_reader:
@@ -534,40 +564,37 @@ class FrogPilotVariables:
CarInterface, _, _ = interfaces[MOCK.MOCK]
FPCP = CarInterface.get_frogpilot_params(MOCK.MOCK, gen_empty_fingerprint(), [], CP, toggle)
is_torque_car = FPCP.lateralTuning.which() == "torque"
if not is_torque_car:
CarInterfaceBase.configure_torque_tune(MOCK.MOCK, FPCP.lateralTuning)
toggle.always_on_lateral_set = bool(CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL)
toggle.car_make = CP.carName
toggle.car_model = CP.carFingerprint
toggle.disable_openpilot_long = params.get_bool("DisableOpenpilotLongitudinal") if tuning_level >= level["DisableOpenpilotLongitudinal"] else default.get_bool("DisableOpenpilotLongitudinal")
friction = FPCP.lateralTuning.torque.friction
has_auto_tune = toggle.car_make in {"hyundai", "toyota"} and FPCP.lateralTuning.which() == "torque"
friction = CP.lateralTuning.torque.friction
has_auto_tune = toggle.car_make in {"hyundai", "toyota"} and CP.lateralTuning.which() == "torque"
has_bsm = CP.enableBsm
toggle.has_cc_long = toggle.car_make == "gm" and bool(CP.flags & GMFlags.CC_LONG.value)
has_nnff = nnff_supported(toggle.car_model)
toggle.has_pedal = CP.enableGasInterceptor
has_radar = not CP.radarUnavailable
toggle.has_sdsu = toggle.car_make == "toyota" and bool(CP.flags & ToyotaFlags.SMART_DSU.value)
toggle.has_sascm = toggle.car_make == "gm" and bool(CP.flags & GMFlags.SASCM.value)
has_sng = CP.autoResumeSng
toggle.has_zss = toggle.car_make == "toyota" and bool(FPCP.fpFlags & ToyotaFrogPilotFlags.ZSS.value)
is_angle_car = CP.steerControlType == car.CarParams.SteerControlType.angle
latAccelFactor = FPCP.lateralTuning.torque.latAccelFactor
latAccelFactor = CP.lateralTuning.torque.latAccelFactor
longitudinalActuatorDelay = CP.longitudinalActuatorDelay
toggle.openpilot_longitudinal = CP.openpilotLongitudinalControl and not toggle.disable_openpilot_long
pcm_cruise = CP.pcmCruise
startAccel = CP.startAccel
stopAccel = CP.stopAccel
steerActuatorDelay = CP.steerActuatorDelay
steerKp = FPCP.lateralTuning.torque.kp
steerKp = CP.lateralTuning.torque.kp
steerRatio = CP.steerRatio
toggle.stoppingDecelRate = CP.stoppingDecelRate
taco_hacks_allowed = CP.safetyConfigs[0].safetyModel == SafetyModel.hyundaiCanfd
toggle.use_lkas_for_aol = not toggle.openpilot_longitudinal and CP.safetyConfigs[0].safetyModel == SafetyModel.hyundaiCanfd
toggle.vEgoStarting = CP.vEgoStarting
toggle.vEgoStopping = CP.vEgoStopping
msg_bytes = params.get("LiveTorqueParameters")
if msg_bytes:
with log.LiveTorqueParametersData.from_bytes(msg_bytes) as LTP:
@@ -594,14 +621,37 @@ class FrogPilotVariables:
toggle.steerActuatorDelay = np.clip(params.get_float("SteerDelay"), 0.01, 1.0) if advanced_lateral_tuning and tuning_level >= level["SteerDelay"] else steerActuatorDelay
toggle.use_custom_steerActuatorDelay = bool(round(toggle.steerActuatorDelay, 2) != round(steerActuatorDelay, 2))
toggle.friction = np.clip(params.get_float("SteerFriction"), 0, 0.5) if advanced_lateral_tuning and tuning_level >= level["SteerFriction"] else friction
toggle.steer_offset = np.clip(params.get_float("SteerOffset"), -0.2, 0.2) if advanced_lateral_tuning and tuning_level >= level["SteerOffset"] and toggle.car_make == "gm" else 0.0
toggle.use_custom_friction = bool(round(toggle.friction, 2) != round(friction, 2)) and is_torque_car and not toggle.force_auto_tune or toggle.force_auto_tune_off
toggle.steerKp = [[0], [np.clip(params.get_float("SteerKP"), steerKp * 0.5, steerKp * 1.5) if advanced_lateral_tuning and is_torque_car and tuning_level >= level["SteerKP"] else steerKp]]
toggle.latAccelFactor = np.clip(params.get_float("SteerLatAccel"), latAccelFactor * 0.75, latAccelFactor * 1.25) if advanced_lateral_tuning and tuning_level >= level["SteerLatAccel"] else latAccelFactor
toggle.latAccelFactor = np.clip(params.get_float("SteerLatAccel"), latAccelFactor * 0.5, latAccelFactor * 1.25) if advanced_lateral_tuning and tuning_level >= level["SteerLatAccel"] else latAccelFactor
toggle.use_custom_latAccelFactor = bool(round(toggle.latAccelFactor, 2) != round(latAccelFactor, 2)) and is_torque_car and not toggle.force_auto_tune or toggle.force_auto_tune_off
toggle.steerRatio = np.clip(params.get_float("SteerRatio"), steerRatio * 0.5, steerRatio * 1.5) if advanced_lateral_tuning and tuning_level >= level["SteerRatio"] else steerRatio
toggle.steerRatio = np.clip(params.get_float("SteerRatio"), steerRatio * 0.25, steerRatio * 1.5) if advanced_lateral_tuning and tuning_level >= level["SteerRatio"] else steerRatio
toggle.use_custom_steerRatio = bool(round(toggle.steerRatio, 2) != round(steerRatio, 2)) and not toggle.force_auto_tune or toggle.force_auto_tune_off
advanced_longitudinal_tuning = params.get_bool("AdvancedLongitudinalTune") if tuning_level >= level["AdvancedLongitudinalTune"] else default.get_bool("AdvancedLongitudinalTune")
gm_ev_vehicle = toggle.car_make == "gm" and CP.carFingerprint in GM_EV_CAR
gm_ev_vehicle &= not (toggle.car_model.startswith("CHEVROLET_VOLT") and not toggle.car_model.endswith("_CC"))
gm_ev_vehicle &= toggle.car_model != "CHEVROLET_MALIBU_HYBRID_CC"
ev_vehicle = gm_ev_vehicle or (toggle.car_make == "hyundai" and CP.carFingerprint in HYUNDAI_EV_CAR)
ev_vehicle |= CP.transmissionType == TransmissionType.direct
if params.get("EVTuning") == b"":
params.put_bool("EVTuning", ev_vehicle)
if params.get("TruckTuning") == b"":
params.put_bool("TruckTuning", False)
ev_tuning_param = params.get_bool("EVTuning")
truck_tuning_param = params.get_bool("TruckTuning")
# Enforce exclusivity between EV and Truck tuning.
if truck_tuning_param and ev_tuning_param:
ev_tuning_param = False
params.put_bool("EVTuning", False)
toggle.ev_tuning = ev_tuning_param if advanced_longitudinal_tuning and tuning_level >= level["EVTuning"] else ev_vehicle
toggle.truck_tuning = truck_tuning_param if advanced_longitudinal_tuning and tuning_level >= level["TruckTuning"] else False
toggle.longitudinalActuatorDelay = np.clip(params.get_float("LongitudinalActuatorDelay"), 0, 1) if advanced_longitudinal_tuning and tuning_level >= level["LongitudinalActuatorDelay"] else longitudinalActuatorDelay
toggle.startAccel = np.clip(params.get_float("StartAccel"), 0, 4) if advanced_longitudinal_tuning and tuning_level >= level["StartAccel"] else startAccel
toggle.stopAccel = np.clip(params.get_float("StopAccel"), -4, 0) if advanced_longitudinal_tuning and tuning_level >= level["StopAccel"] else stopAccel
@@ -609,6 +659,10 @@ class FrogPilotVariables:
toggle.vEgoStarting = np.clip(params.get_float("VEgoStarting"), 0.01, 1) if advanced_longitudinal_tuning and tuning_level >= level["VEgoStarting"] else toggle.vEgoStarting
toggle.vEgoStopping = np.clip(params.get_float("VEgoStopping"), 0.01, 1) if advanced_longitudinal_tuning and tuning_level >= level["VEgoStopping"] else toggle.vEgoStopping
toggle.stop_distance = params.get_float("StopDistance") if advanced_longitudinal_tuning and tuning_level >= level["StopDistance"] else 6.0
toggle.recovery_power = np.clip(params.get_float("RecoveryPower"), 0.5, 2.0) if advanced_longitudinal_tuning and tuning_level >= level["RecoveryPower"] else 1.0
toggle.alert_volume_controller = params.get_bool("AlertVolumeControl") if tuning_level >= level["AlertVolumeControl"] else default.get_bool("AlertVolumeControl")
toggle.disengage_volume = params.get_int("DisengageVolume") if toggle.alert_volume_controller and tuning_level >= level["DisengageVolume"] else default.get_int("DisengageVolume")
toggle.engage_volume = params.get_int("EngageVolume") if toggle.alert_volume_controller and tuning_level >= level["EngageVolume"] else default.get_int("EngageVolume")
@@ -624,7 +678,7 @@ class FrogPilotVariables:
toggle.always_on_lateral_main = toggle.always_on_lateral_set and not toggle.use_lkas_for_aol and (params.get_bool("AlwaysOnLateralMain") if tuning_level >= level["AlwaysOnLateralMain"] else default.get_bool("AlwaysOnLateralMain"))
toggle.always_on_lateral_pause_speed = params.get_int("PauseAOLOnBrake") if toggle.always_on_lateral_set and tuning_level >= level["PauseAOLOnBrake"] else default.get_int("PauseAOLOnBrake")
toggle.automatic_updates = (params.get_bool("AutomaticUpdates") if tuning_level >= level["AutomaticUpdates"] and (self.release_branch or self.vetting_branch) else default.get_bool("AutomaticUpdates")) and not BACKUP_PATH.is_file()
toggle.automatic_updates = (params.get_bool("AutomaticUpdates") if tuning_level >= level["AutomaticUpdates"] else default.get_bool("AutomaticUpdates")) and not BACKUP_PATH.is_file()
toggle.car_model = params.get("CarModel", encoding="utf-8") or toggle.car_model
@@ -664,28 +718,31 @@ class FrogPilotVariables:
toggle.aggressive_jerk_danger = np.clip(params.get_int("AggressiveJerkDanger") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkDanger"] else default.get_int("AggressiveJerkDanger") / 100
toggle.aggressive_jerk_speed = np.clip(params.get_int("AggressiveJerkSpeed") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkSpeed"] else default.get_int("AggressiveJerkSpeed") / 100
toggle.aggressive_jerk_speed_decrease = np.clip(params.get_int("AggressiveJerkSpeedDecrease") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkSpeedDecrease"] else default.get_int("AggressiveJerkSpeedDecrease") / 100
toggle.aggressive_follow = np.clip(params.get_float("AggressiveFollow"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollow"] else default.get_float("AggressiveFollow")
toggle.aggressive_follow = [np.clip(params.get_float("AggressiveFollow"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollow"] else default.get_float("AggressiveFollow"),
np.clip(params.get_float("AggressiveFollowHigh"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollowHigh"] else default.get_float("AggressiveFollowHigh")]
standard_profile = toggle.custom_personalities and (params.get_bool("StandardPersonalityProfile") if tuning_level >= level["StandardPersonalityProfile"] else default.get_bool("StandardPersonalityProfile"))
toggle.standard_jerk_acceleration = np.clip(params.get_int("StandardJerkAcceleration") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkAcceleration"] else default.get_int("StandardJerkAcceleration") / 100
toggle.standard_jerk_deceleration = np.clip(params.get_int("StandardJerkDeceleration") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkDeceleration"] else default.get_int("StandardJerkDeceleration") / 100
toggle.standard_jerk_danger = np.clip(params.get_int("StandardJerkDanger") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkDanger"] else default.get_int("StandardJerkDanger") / 100
toggle.standard_jerk_speed = np.clip(params.get_int("StandardJerkSpeed") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkSpeed"] else default.get_int("StandardJerkSpeed") / 100
toggle.standard_jerk_speed_decrease = np.clip(params.get_int("StandardJerkSpeedDecrease") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkSpeedDecrease"] else default.get_int("StandardJerkSpeedDecrease") / 100
toggle.standard_follow = np.clip(params.get_float("StandardFollow"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollow"] else default.get_float("StandardFollow")
toggle.standard_follow = [np.clip(params.get_float("StandardFollow"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollow"] else default.get_float("StandardFollow"),
np.clip(params.get_float("StandardFollowHigh"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollowHigh"] else default.get_float("StandardFollowHigh")]
relaxed_profile = toggle.custom_personalities and (params.get_bool("RelaxedPersonalityProfile") if tuning_level >= level["RelaxedPersonalityProfile"] else default.get_bool("RelaxedPersonalityProfile"))
toggle.relaxed_jerk_acceleration = np.clip(params.get_int("RelaxedJerkAcceleration") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkAcceleration"] else default.get_int("RelaxedJerkAcceleration") / 100
toggle.relaxed_jerk_deceleration = np.clip(params.get_int("RelaxedJerkDeceleration") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkDeceleration"] else default.get_int("RelaxedJerkDeceleration") / 100
toggle.relaxed_jerk_danger = np.clip(params.get_int("RelaxedJerkDanger") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkDanger"] else default.get_int("RelaxedJerkDanger") / 100
toggle.relaxed_jerk_speed = np.clip(params.get_int("RelaxedJerkSpeed") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkSpeed"] else default.get_int("RelaxedJerkSpeed") / 100
toggle.relaxed_jerk_speed_decrease = np.clip(params.get_int("RelaxedJerkSpeedDecrease") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkSpeedDecrease"] else default.get_int("RelaxedJerkSpeedDecrease") / 100
toggle.relaxed_follow = np.clip(params.get_float("RelaxedFollow"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollow"] else default.get_float("RelaxedFollow")
toggle.relaxed_follow = [np.clip(params.get_float("RelaxedFollow"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollow"] else default.get_float("RelaxedFollow"),
np.clip(params.get_float("RelaxedFollowHigh"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollowHigh"] else default.get_float("RelaxedFollowHigh")]
traffic_profile = toggle.custom_personalities and (params.get_bool("TrafficPersonalityProfile") if tuning_level >= level["TrafficPersonalityProfile"] else default.get_bool("TrafficPersonalityProfile"))
toggle.traffic_mode_jerk_acceleration = [np.clip(params.get_int("TrafficJerkAcceleration") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkAcceleration"] else default.get_int("TrafficJerkAcceleration") / 100, toggle.aggressive_jerk_acceleration]
toggle.traffic_mode_jerk_deceleration = [np.clip(params.get_int("TrafficJerkDeceleration") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkDeceleration"] else default.get_int("TrafficJerkDeceleration") / 100, toggle.aggressive_jerk_deceleration]
toggle.traffic_mode_jerk_danger = [np.clip(params.get_int("TrafficJerkDanger") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkDanger"] else default.get_int("TrafficJerkDanger") / 100, toggle.aggressive_jerk_danger]
toggle.traffic_mode_jerk_speed = [np.clip(params.get_int("TrafficJerkSpeed") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkSpeed"] else default.get_int("TrafficJerkSpeed") / 100, toggle.aggressive_jerk_speed]
toggle.traffic_mode_jerk_speed_decrease = [np.clip(params.get_int("TrafficJerkSpeedDecrease") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkSpeedDecrease"] else default.get_int("TrafficJerkSpeedDecrease") / 100, toggle.aggressive_jerk_speed_decrease]
toggle.traffic_mode_follow = [np.clip(params.get_float("TrafficFollow"), 0.5, MAX_T_FOLLOW) if traffic_profile and tuning_level >= level["TrafficFollow"] else default.get_float("TrafficFollow"), toggle.aggressive_follow]
toggle.traffic_mode_follow = [np.clip(params.get_float("TrafficFollow"), 0.5, MAX_T_FOLLOW) if traffic_profile and tuning_level >= level["TrafficFollow"] else default.get_float("TrafficFollow"), toggle.aggressive_follow[0]]
custom_ui = params.get_bool("CustomUI") if tuning_level >= level["CustomUI"] else default.get_bool("CustomUI")
toggle.acceleration_path = toggle.openpilot_longitudinal and (custom_ui and (params.get_bool("AccelerationPath") if tuning_level >= level["AccelerationPath"] else default.get_bool("AccelerationPath")) or toggle.debug_mode)
@@ -770,6 +827,9 @@ class FrogPilotVariables:
toggle.vEgoStarting = 0.15 if toggle.experimental_gm_tune else toggle.vEgoStarting
toggle.vEgoStopping = 0.15 if toggle.experimental_gm_tune else toggle.vEgoStopping
toggle.red_panda = toggle.car_make == "gm" and (params.get_bool("RedPanda") if tuning_level >= level["RedPanda"] else default.get_bool("RedPanda"))
toggle.remote_start_boots_comma = toggle.car_make == "gm" and (params.get_bool("RemoteStartBootsComma") if tuning_level >= level["RemoteStartBootsComma"] else default.get_bool("RemoteStartBootsComma"))
toggle.force_fingerprint = (params.get_bool("ForceFingerprint") if tuning_level >= level["ForceFingerprint"] else default.get_bool("ForceFingerprint")) and toggle.car_model is not None
toggle.frogsgomoo_tweak = toggle.openpilot_longitudinal and toggle.car_make == "toyota" and (params.get_bool("FrogsGoMoosTweak") if tuning_level >= level["FrogsGoMoosTweak"] else default.get_bool("FrogsGoMoosTweak"))
@@ -814,33 +874,49 @@ class FrogPilotVariables:
toggle.human_following = longitudinal_tuning and (params.get_bool("HumanFollowing") if tuning_level >= level["HumanFollowing"] else default.get_bool("HumanFollowing"))
toggle.lead_detection_probability = np.clip(params.get_int("LeadDetectionThreshold") / 100, 0.25, 0.50) if longitudinal_tuning and tuning_level >= level["LeadDetectionThreshold"] else default.get_int("LeadDetectionThreshold") / 100
toggle.max_desired_acceleration = np.clip(params.get_float("MaxDesiredAcceleration"), 0.1, 4.0) if longitudinal_tuning and tuning_level >= level["MaxDesiredAcceleration"] else default.get_float("MaxDesiredAcceleration")
toggle.trailer_load_kg = (np.clip(params.get_int("TrailerLoad"), 0, 15000) if longitudinal_tuning and tuning_level >= level["TrailerLoad"] else default.get_int("TrailerLoad")) * CV.LB_TO_KG
toggle.taco_tune = longitudinal_tuning and (params.get_bool("TacoTune") if tuning_level >= level["TacoTune"] else default.get_bool("TacoTune"))
toggle.available_models = (params.get("AvailableModels", encoding="utf-8") or "") + f",{DEFAULT_MODEL}"
toggle.available_model_names = (params.get("AvailableModelNames", encoding="utf-8") or "") + f",{DEFAULT_MODEL_NAME}"
downloaded_models = [model for model in toggle.available_models.split(",") if (MODELS_PATH / f"{model}.thneed").is_file() or all((MODELS_PATH / f"{model}_{filename}").is_file() for filename, _ in TINYGRAD_FILES)]
model_versions = (params.get("ModelVersions", encoding="utf-8") or "") + f",{DEFAULT_MODEL_VERSION}"
toggle.model_randomizer = params.get_bool("ModelRandomizer") if tuning_level >= level["ModelRandomizer"] else default.get_bool("ModelRandomizer")
if toggle.model_randomizer:
if not started:
blacklisted_models = (params.get("BlacklistedModels", encoding="utf-8") or "").split(",")
selectable_models = [model for model in downloaded_models if model not in blacklisted_models]
toggle.model = random.choice(selectable_models) if selectable_models else DEFAULT_MODEL
toggle.model_name = "Mystery Model 👻"
toggle.model_version = model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)]
else:
model = ((params.get("Model", encoding="utf-8") if tuning_level >= level["Model"] else default.get("Model", encoding="utf-8")) or DEFAULT_MODEL).removesuffix("_default")
if model in downloaded_models:
toggle.model = model
toggle.model_name = dict(zip(toggle.available_models.split(","), toggle.available_model_names.split(",")))[toggle.model]
toggle.model_version = dict(zip(toggle.available_models.split(","), model_versions.split(",")))[toggle.model]
toggle.available_models = params.get("AvailableModels", encoding="utf-8") or ""
toggle.available_model_names = params.get("AvailableModelNames", encoding="utf-8") or ""
toggle.available_model_series = params.get("AvailableModelSeries", encoding="utf-8") or ""
toggle.community_favorites = params.get("CommunityFavorites", encoding="utf-8") or ""
toggle.model_released_dates = params.get("ModelReleasedDates", encoding="utf-8") or ""
toggle.model_versions = params.get("ModelVersions", encoding="utf-8") or ""
toggle.sort_models_by_date = params.get_bool("SortModelsByDate") if tuning_level >= level["SortModelsByDate"] else default.get_bool("SortModelsByDate")
toggle.user_favorites = params.get("UserFavorites", encoding="utf-8") or ""
downloaded_models = [model for model in toggle.available_models.split(",") if any(MODELS_PATH.glob(f"{model}*"))]
toggle.model_randomizer = downloaded_models and (params.get_bool("ModelRandomizer") if tuning_level >= level["ModelRandomizer"] else default.get_bool("ModelRandomizer"))
if toggle.available_models and toggle.available_model_names and downloaded_models and toggle.model_versions:
if DEFAULT_MODEL not in toggle.available_models.split(","):
toggle.available_models += f",{DEFAULT_MODEL}"
toggle.available_model_names += f",{DEFAULT_MODEL_NAME}"
toggle.model_versions += f",{DEFAULT_MODEL_VERSION}"
downloaded_models += [DEFAULT_MODEL]
if toggle.model_randomizer:
if not started:
blacklisted_models = (params.get("BlacklistedModels", encoding="utf-8") or "").split(",")
selectable_models = [model for model in downloaded_models if model not in blacklisted_models]
toggle.model = random.choice(selectable_models) if selectable_models else default.get("Model", encoding="utf-8")
toggle.model_name = "Mystery Model 👻"
toggle.model_version = toggle.model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)]
else:
toggle.model = params.get("Model", encoding="utf-8") if tuning_level >= level["Model"] else default.get("Model", encoding="utf-8")
if toggle.model in downloaded_models:
toggle.model_name = toggle.available_model_names.split(",")[toggle.available_models.split(",").index(toggle.model)]
toggle.model_version = toggle.model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)]
else:
toggle.model = default.get("Model", encoding="utf-8")
toggle.model_name = toggle.available_model_names.split(",")[toggle.available_models.split(",").index(toggle.model)]
toggle.model_version = toggle.model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)]
else:
toggle.model = DEFAULT_MODEL
toggle.model_name = DEFAULT_MODEL_NAME
toggle.model_version = DEFAULT_MODEL_VERSION
toggle.classic_longitudinal = toggle.model_version in {"v1", "v2", "v3", "v4"}
toggle.classic_model = toggle.model_version in {"v1", "v2", "v3", "v4"}
toggle.classic_longitudinal = toggle.model_version in {"v1", "v2", "v3", "v4", "v5", "v6"}
toggle.tinygrad_model = not toggle.classic_model and toggle.model_version not in {"v5", "v6"}
toggle.tinygrad_model = toggle.model_version in {"v8", "v9", "v10", "v11", "v12"}
toggle.tomb_raider = toggle.model == "space-lab"
toggle.model_ui = params.get_bool("ModelUI") if tuning_level >= level["ModelUI"] else default.get_bool("ModelUI")
toggle.dynamic_path_width = toggle.model_ui and (params.get_bool("DynamicPathWidth") if tuning_level >= level["DynamicPathWidth"] else default.get_bool("DynamicPathWidth"))
@@ -863,6 +939,7 @@ class FrogPilotVariables:
toggle.old_long_api |= toggle.openpilot_longitudinal and toggle.car_make == "hyundai" and not (params.get_bool("NewLongAPI") if tuning_level >= level["NewLongAPI"] else default.get_bool("NewLongAPI"))
personalize_openpilot = params.get_bool("PersonalizeOpenpilot") if tuning_level >= level["PersonalizeOpenpilot"] else default.get_bool("PersonalizeOpenpilot")
toggle.boot_logo = params.get("BootLogo", encoding="utf-8") or "starpilot"
toggle.color_scheme = toggle.current_holiday_theme if toggle.current_holiday_theme != "stock" else params.get("CustomColors", encoding="utf-8") if personalize_openpilot else "stock"
toggle.distance_icons = toggle.current_holiday_theme if toggle.current_holiday_theme != "stock" else params.get("CustomDistanceIcons", encoding="utf-8") if personalize_openpilot else "stock"
toggle.icon_pack = toggle.current_holiday_theme if toggle.current_holiday_theme != "stock" else params.get("CustomIcons", encoding="utf-8") if personalize_openpilot else "stock"
@@ -906,7 +983,6 @@ class FrogPilotVariables:
toggle.screen_timeout = params.get_int("ScreenTimeout") if screen_management and tuning_level >= level["ScreenTimeout"] else default.get_int("ScreenTimeout")
toggle.screen_timeout_onroad = params.get_int("ScreenTimeoutOnroad") if screen_management and tuning_level >= level["ScreenTimeoutOnroad"] else default.get_int("ScreenTimeoutOnroad")
toggle.standby_mode = screen_management and (params.get_bool("StandbyMode") if tuning_level >= level["StandbyMode"] else default.get_bool("StandbyMode"))
toggle.sng_hack = toggle.openpilot_longitudinal and toggle.car_make == "toyota" and not toggle.has_pedal and not has_sng and (params.get_bool("SNGHack") if tuning_level >= level["SNGHack"] else default.get_bool("SNGHack"))
toggle.speed_limit_controller = toggle.openpilot_longitudinal and (params.get_bool("SpeedLimitController") if tuning_level >= level["SpeedLimitController"] else default.get_bool("SpeedLimitController"))
@@ -953,7 +1029,16 @@ class FrogPilotVariables:
toggle.lock_doors = toyota_doors and (params.get_bool("LockDoors") if tuning_level >= level["LockDoors"] else default.get_bool("LockDoors"))
toggle.unlock_doors = toyota_doors and (params.get_bool("UnlockDoors") if tuning_level >= level["UnlockDoors"] else default.get_bool("UnlockDoors"))
toggle.volt_sng = toggle.car_model == "CHEVROLET_VOLT" and (params.get_bool("VoltSNG") if tuning_level >= level["VoltSNG"] else default.get_bool("VoltSNG"))
volt_models = {
"CHEVROLET_VOLT",
"CHEVROLET_VOLT_2019",
"CHEVROLET_VOLT_ASCM",
"CHEVROLET_VOLT_CAMERA",
}
toggle.volt_sng = toggle.car_model in volt_models and (params.get_bool("VoltSNG") if tuning_level >= level["VoltSNG"] else default.get_bool("VoltSNG"))
toggle.gm_pedal_longitudinal = params.get_bool("GMPedalLongitudinal") if tuning_level >= level["GMPedalLongitudinal"] else default.get_bool("GMPedalLongitudinal")
params_memory.put("FrogPilotToggles", json.dumps(toggle.__dict__))
params_memory.remove("FrogPilotTogglesUpdated")
+9 -3
View File
@@ -9,7 +9,7 @@ from openpilot.common.conversions import Conversions as CV
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.realtime import DT_MDL
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import A_CHANGE_COST, DANGER_ZONE_COST, J_EGO_COST, STOP_DISTANCE
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import A_CHANGE_COST, COMFORT_BRAKE, DANGER_ZONE_COST, J_EGO_COST
from openpilot.frogpilot.common.frogpilot_utilities import calculate_lane_width, calculate_road_curvature
from openpilot.frogpilot.common.frogpilot_variables import CRUISING_SPEED, MINIMUM_LATERAL_ACCELERATION, PLANNER_TIME, THRESHOLD, params, params_memory
@@ -65,6 +65,8 @@ class FrogPilotPlanner:
self.cem.curve_detected = False
self.cem.stop_sign_and_light(v_ego, sm, PLANNER_TIME - 2)
self.driving_in_curve = abs(self.lateral_acceleration) >= MINIMUM_LATERAL_ACCELERATION
self.frogpilot_events.update(v_cruise, sm, frogpilot_toggles)
@@ -85,7 +87,7 @@ class FrogPilotPlanner:
params_memory.remove("LastGPSPosition")
self.lateral_acceleration = v_ego**2 * (sm["carState"].steeringAngleDeg - sm["liveParameters"].angleOffsetDeg) * CV.DEG_TO_RAD / (self.CP.steerRatio * self.CP.wheelbase)
self.lateral_acceleration = v_ego**2 * sm["controlsState"].curvature
check_lane_width = frogpilot_toggles.adjacent_paths or frogpilot_toggles.adjacent_path_metrics or frogpilot_toggles.blind_spot_path or frogpilot_toggles.lane_detection
if check_lane_width and v_ego >= frogpilot_toggles.minimum_lane_change_speed:
@@ -115,7 +117,9 @@ class FrogPilotPlanner:
def update_lead_status(self):
following_lead = self.lead_one.status
following_lead &= self.lead_one.dRel < self.model_length + STOP_DISTANCE
from frogpilot.common.frogpilot_variables import get_frogpilot_toggles
fp_toggles = get_frogpilot_toggles()
following_lead &= self.lead_one.dRel < self.model_length + fp_toggles.stop_distance
self.tracking_lead_filter.update(following_lead)
return self.tracking_lead_filter.x >= THRESHOLD
@@ -138,6 +142,8 @@ class FrogPilotPlanner:
frogpilotPlan.desiredFollowDistance = self.frogpilot_following.desired_follow_distance
frogpilotPlan.disableThrottle = self.frogpilot_following.disable_throttle
frogpilotPlan.experimentalMode = self.cem.experimental_mode or self.frogpilot_vcruise.slc.experimental_mode
frogpilotPlan.forcingStop = self.frogpilot_vcruise.forcing_stop
@@ -1,20 +1,54 @@
#!/usr/bin/env python3
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.realtime import DT_MDL
from openpilot.common.numpy_fast import interp
from openpilot.common.conversions import Conversions as CV
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED, THRESHOLD, params_memory
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED, THRESHOLD, params_memory, scale_threshold
class ConditionalExperimentalMode:
# ===== CONDITIONAL EXPERIMENTAL MODE SPEED-BASED TUNING =====
# Speed ranges: [0-35, 35-55, 55-70, 70+ mph]
# FILTER TIME CONSTANTS (Lower = More responsive, Higher = Smoother)
# [City, Urban Hwy, Rural Hwy, High Speed]
FILTER_TIME_CURVES = [0.9, 0.8, 0.6, 0.5] # Faster detection at highway speeds
FILTER_TIME_LEADS = [0.9, 0.8, 0.7, 0.5] # Less sensitive at 70+ mph for slow leads
FILTER_TIME_LIGHTS = [0.9, 0.8, 0.75, 0.55] # Less sensitive at 60+ mph for stoplights
# HIGHWAY LIGHT DETECTION MULTIPLIERS
# How much to increase model stop time at highway speeds
LIGHT_BOOSTS = [1.0, 1.2, 1.1, 1.0] # Keep conservative boost for highest speeds
LIGHT_SPEED_LOW = 50 * CV.MPH_TO_MS # 50 mph threshold
LIGHT_SPEED_HIGH = 60 * CV.MPH_TO_MS # 60 mph threshold
LIGHT_MAX_TIME = 9 # Balanced max time preserving city performance
# ===== END TUNING PARAMETERS =====
# Current active values
FILTER_TIME_CURVE = 0.8
FILTER_TIME_LEAD = 0.8
FILTER_TIME_LIGHT = 0.8
LIGHT_BOOST_LOW = 1.15
LIGHT_BOOST_HIGH = 1.2
@staticmethod
def get_speed_based_param(speed_mph, param_array):
"""Get parameter value based on current speed using smooth interpolation between breakpoints [0, 35, 55, 70]"""
return interp(speed_mph, [0, 35, 55, 70], param_array)
def __init__(self, FrogPilotPlanner):
self.frogpilot_planner = FrogPilotPlanner
self.curvature_filter = FirstOrderFilter(0, 1, DT_MDL)
self.slow_lead_filter = FirstOrderFilter(0, 1, DT_MDL)
self.stop_light_filter = FirstOrderFilter(0, 0.5, DT_MDL)
# Faster filters with hysteresis for better responsiveness
self.curvature_filter = FirstOrderFilter(0, self.FILTER_TIME_CURVE, DT_MDL)
self.slow_lead_filter = FirstOrderFilter(0, self.FILTER_TIME_LEAD, DT_MDL)
self.stop_light_filter = FirstOrderFilter(0, self.FILTER_TIME_LIGHT, DT_MDL)
self.curve_detected = False
self.experimental_mode = False
self.stop_light_detected = False
self.prev_experimental_mode = False # For hysteresis
def update(self, v_ego, sm, frogpilot_toggles):
if frogpilot_toggles.experimental_mode_via_press:
@@ -24,9 +58,26 @@ class ConditionalExperimentalMode:
if self.status_value not in {1, 2} and not sm["carState"].standstill:
self.update_conditions(v_ego, sm, frogpilot_toggles)
new_experimental_mode = self.check_conditions(v_ego, sm, frogpilot_toggles)
# Add hysteresis to prevent rapid toggling
if new_experimental_mode and not self.prev_experimental_mode:
# Require weaker conditions to turn on
hysteresis_factor = 0.9
elif not new_experimental_mode and self.prev_experimental_mode:
# Require stronger conditions to turn off
hysteresis_factor = 1.2
else:
hysteresis_factor = 1.0
# Apply hysteresis to key conditions
if hasattr(self, 'slow_lead_detected'):
self.slow_lead_detected = self.slow_lead_detected if hysteresis_factor == 1.0 else (self.slow_lead_filter.x >= scale_threshold(v_ego) * hysteresis_factor)
if hasattr(self, 'curve_detected'):
self.curve_detected = self.curve_detected if hysteresis_factor == 1.0 else (self.curvature_filter.x >= THRESHOLD * hysteresis_factor)
self.experimental_mode = self.check_conditions(v_ego, sm, frogpilot_toggles)
self.prev_experimental_mode = self.experimental_mode
params_memory.put_int("CEStatus", self.status_value if self.experimental_mode else 0)
else:
self.experimental_mode = self.status_value == 2 or sm["carState"].standstill and self.experimental_mode and self.frogpilot_planner.model_stopped
@@ -55,7 +106,7 @@ class ConditionalExperimentalMode:
self.status_value = 8
return True
if frogpilot_toggles.conditional_lead and self.slow_lead_detected:
if frogpilot_toggles.conditional_lead and self.slow_lead_detected and v_ego <= 35.31:
self.status_value = 9 if self.frogpilot_planner.lead_one.vLead < 1 else 10
return True
@@ -71,30 +122,70 @@ class ConditionalExperimentalMode:
def update_conditions(self, v_ego, sm, frogpilot_toggles):
self.curve_detection(v_ego, frogpilot_toggles)
self.slow_lead(frogpilot_toggles)
self.slow_lead(frogpilot_toggles, v_ego)
self.stop_sign_and_light(v_ego, sm, frogpilot_toggles.conditional_model_stop_time)
def curve_detection(self, v_ego, frogpilot_toggles):
self.curvature_filter.update(self.frogpilot_planner.road_curvature_detected or self.frogpilot_planner.driving_in_curve)
self.curve_detected = self.curvature_filter.x >= THRESHOLD and v_ego > CRUISING_SPEED
def slow_lead(self, frogpilot_toggles):
def slow_lead(self, frogpilot_toggles, v_ego):
if self.frogpilot_planner.tracking_lead:
slower_lead = frogpilot_toggles.conditional_slower_lead and self.frogpilot_planner.frogpilot_following.slower_lead
stopped_lead = frogpilot_toggles.conditional_stopped_lead and self.frogpilot_planner.lead_one.vLead < 1
lead_threshold = scale_threshold(v_ego)
# Adjust threshold based on lead probability for vision-only accuracy
lead_prob = getattr(self.frogpilot_planner.lead_one, 'modelProb', 1.0)
adjusted_threshold = lead_threshold * (1.0 + 0.2 * (1.0 - lead_prob)) # Higher threshold for lower confidence
self.slow_lead_filter.update(slower_lead or stopped_lead)
self.slow_lead_detected = self.slow_lead_filter.x >= THRESHOLD
self.slow_lead_detected = self.slow_lead_filter.x >= adjusted_threshold
else:
self.slow_lead_filter.x = 0
self.slow_lead_detected = False
def stop_sign_and_light(self, v_ego, sm, model_time):
if not sm["frogpilotCarState"].trafficModeEnabled:
model_stopping = self.frogpilot_planner.model_length < v_ego * model_time
speed_mph = v_ego * CV.MS_TO_MPH # Convert m/s to mph
# Interp for smooth scaling in 35-45 mph
bp = [0, 35, 45]
low_filter_time = 0.0 # No filtering under 35 mph
tuned_filter_time_curves = self.FILTER_TIME_CURVES[1] # At 35-55 mph
tuned_filter_time_leads = self.FILTER_TIME_LEADS[1]
tuned_filter_time_lights = self.FILTER_TIME_LIGHTS[1]
low_boost = 1.0
tuned_boost = self.LIGHT_BOOSTS[1]
low_cap_factor = 0.0 # No cap under 35 mph
tuned_cap_factor = 1.0
filter_time_curves = interp(speed_mph, bp, [low_filter_time, low_filter_time, tuned_filter_time_curves])
filter_time_leads = interp(speed_mph, bp, [low_filter_time, low_filter_time, tuned_filter_time_leads])
filter_time_lights = interp(speed_mph, bp, [low_filter_time, low_filter_time, tuned_filter_time_lights])
light_boost = interp(speed_mph, bp, [low_boost, low_boost, tuned_boost])
cap_factor = interp(speed_mph, bp, [low_cap_factor, low_cap_factor, tuned_cap_factor])
# Update filter times with interp
self.curvature_filter = FirstOrderFilter(self.curvature_filter.x, filter_time_curves, DT_MDL)
self.slow_lead_filter = FirstOrderFilter(self.slow_lead_filter.x, filter_time_leads, DT_MDL)
self.stop_light_filter = FirstOrderFilter(self.stop_light_filter.x, filter_time_lights, DT_MDL)
# Disable stoplight detection at very high speeds to prevent false positives
if speed_mph > 75: # Disable above 75 mph
self.stop_light_filter.x = 0
self.stop_light_detected = False
return
# Adjust model time with interp boost and gradual cap
adjusted_model_time = model_time * light_boost
if cap_factor > 0:
adjusted_model_time = min(adjusted_model_time, self.LIGHT_MAX_TIME * cap_factor + model_time * (1 - cap_factor)) # Gradual cap
model_stopping = self.frogpilot_planner.model_length < v_ego * adjusted_model_time
self.stop_light_filter.update(self.frogpilot_planner.model_stopped or model_stopping)
self.stop_light_detected = self.stop_light_filter.x >= THRESHOLD and not self.frogpilot_planner.tracking_lead
self.stop_light_detected = self.stop_light_filter.x >= THRESHOLD**2 and not self.frogpilot_planner.tracking_lead
else:
self.stop_light_filter.x = 0
self.stop_light_detected = False
@@ -1,33 +1,100 @@
#!/usr/bin/env python3
import numpy as np
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import CRUISE_MIN_ACCEL
from openpilot.selfdrive.controls.lib.longitudinal_planner import ACCEL_MIN, get_max_accel
def cubic_interp(x, xp, fp):
"""Cubic interpolation using NumPy's native operations for speed."""
# Boundary conditions
if x <= xp[0]:
return fp[0]
elif x >= xp[-1]:
return fp[-1]
# Find interval
i = np.searchsorted(xp, x) - 1
i = max(0, min(i, len(xp)-2)) # clamp the index
# Normalized position
t = (x - xp[i]) / float(xp[i+1] - xp[i])
# Hermite cubic formula
return fp[i]*(1 - 3*t**2 + 2*t**3) + fp[i+1]*(3*t**2 - 2*t**3)
def akima_interp(x, xp, fp):
"""Akima-inspired interpolation with reduced overshoot characteristics."""
if x <= xp[0]:
return fp[0]
elif x >= xp[-1]:
return fp[-1]
i = np.searchsorted(xp, x) - 1
i = max(0, min(i, len(xp)-2)) # clamp the index
t = (x - xp[i]) / float(xp[i+1] - xp[i])
# Quintic polynomial to reduce overshoot
t2 = t*t
t4 = t2*t2
t3 = t2*t
return (fp[i]*(1 - 10*t3 + 15*t4 - 6*t3*t2)
+ fp[i+1]*(10*t3 - 15*t4 + 6*t3*t2))
from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, get_max_accel
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT
A_CRUISE_MIN_ECO = CRUISE_MIN_ACCEL / 2
A_CRUISE_MIN_SPORT = CRUISE_MIN_ACCEL * 2
A_CRUISE_MIN_ECO = A_CRUISE_MIN / 2
A_CRUISE_MIN_SPORT = A_CRUISE_MIN * 2
# MPH = [0.0, 11, 22, 34, 45, 56, 89]
A_CRUISE_MAX_BP_CUSTOM = [0.0, 5., 10., 15., 20., 25., 40.]
A_CRUISE_MAX_VALS_ECO = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2]
A_CRUISE_MAX_VALS_SPORT = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6]
# MPH = [0.0, 11, 22, 34, 45, 56, 89]
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.12, 1.12, 1.45]
A_CRUISE_MAX_VALS_STANDARD_EV = [1.15, 1.15, 1.15, 1.15, 1.30, 1.30, 1.72]
A_CRUISE_MAX_VALS_SPORT_EV = [1.25, 1.25, 1.25, 1.25, 1.45, 1.5, 2.0]
A_CRUISE_MAX_VALS_SPORT_PLUS_EV = [1.35, 1.35, 1.35, 1.35, 1.60, 1.60, 2.10]
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]
A_CRUISE_MAX_VALS_ECO_TRUCK = [3.00, 1.05, 0.60, 0.50, 0.50, 0.45, 0.35]
A_CRUISE_MAX_VALS_STANDARD_TRUCK = [6.00, 1.10, 0.70, 0.60, 0.55, 0.45, 0.35]
A_CRUISE_MAX_VALS_SPORT_TRUCK = [6.00, 1.15, 0.75, 0.70, 0.60, 0.50, 0.40]
A_CRUISE_MAX_VALS_SPORT_PLUS_TRUCK = [6.00, 1.30, 0.90, 0.80, 0.70, 0.60, 0.45]
def get_max_accel_eco(v_ego):
return float(np.interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_ECO))
def get_max_accel_eco(v_ego, ev_tuning=True, truck_tuning=False):
if ev_tuning:
cruise_vals = A_CRUISE_MAX_VALS_ECO_EV
elif truck_tuning:
cruise_vals = A_CRUISE_MAX_VALS_ECO_TRUCK
else:
cruise_vals = A_CRUISE_MAX_VALS_ECO_GAS
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals))
def get_max_accel_sport(v_ego):
return float(np.interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT))
def get_max_accel_sport(v_ego, ev_tuning=True, truck_tuning=False):
if ev_tuning:
cruise_vals = A_CRUISE_MAX_VALS_SPORT_EV
elif truck_tuning:
cruise_vals = A_CRUISE_MAX_VALS_SPORT_TRUCK
else:
cruise_vals = A_CRUISE_MAX_VALS_SPORT_GAS
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals))
def get_max_accel_standard(v_ego, ev_tuning=True, truck_tuning=False):
if ev_tuning:
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_STANDARD_EV))
if truck_tuning:
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_STANDARD_TRUCK))
return get_max_accel(v_ego)
def get_max_accel_low_speeds(max_accel, v_cruise):
return float(np.interp(v_cruise, [0., CITY_SPEED_LIMIT / 2, CITY_SPEED_LIMIT], [max_accel / 4, max_accel / 2, max_accel]))
return float(akima_interp(v_cruise, [0., CITY_SPEED_LIMIT / 2, CITY_SPEED_LIMIT], [max_accel / 4, max_accel / 2, max_accel]))
def get_max_accel_ramp_off(max_accel, v_cruise, v_ego):
return float(np.interp(v_cruise - v_ego, [0., 1., 5.], [0., 0.5, max_accel]))
return float(akima_interp(v_cruise - v_ego, [0., 1., 5., 10.], [0., 0.5, 1.0, max_accel]))
def get_max_allowed_accel(v_ego):
return float(np.interp(v_ego, [0., 5., 20.], [4.0, 4.0, 2.0])) # ISO 15622:2018
def get_max_allowed_accel(v_ego, ev_tuning=True, truck_tuning=False):
if ev_tuning:
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT_PLUS_EV))
if truck_tuning:
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT_PLUS_TRUCK))
return float(akima_interp(v_ego, [0., 5., 20.], [4.0, 4.0, 2.0])) # ISO 15622:2018
class FrogPilotAcceleration:
def __init__(self, FrogPilotPlanner):
@@ -39,34 +106,34 @@ class FrogPilotAcceleration:
def update(self, v_ego, sm, frogpilot_toggles):
eco_gear = sm["frogpilotCarState"].ecoGear
sport_gear = sm["frogpilotCarState"].sportGear
ev_tuning = frogpilot_toggles.ev_tuning
truck_tuning = frogpilot_toggles.truck_tuning
if sm["frogpilotCarState"].trafficModeEnabled:
self.max_accel = get_max_accel(v_ego)
self.max_accel = get_max_accel_standard(v_ego, ev_tuning, truck_tuning)
elif frogpilot_toggles.map_acceleration and (eco_gear or sport_gear):
if eco_gear:
self.max_accel = get_max_accel_eco(v_ego)
self.max_accel = get_max_accel_eco(v_ego, ev_tuning, truck_tuning)
else:
if frogpilot_toggles.acceleration_profile == 2:
self.max_accel = get_max_accel_sport(v_ego)
self.max_accel = get_max_accel_sport(v_ego, ev_tuning, truck_tuning)
else:
self.max_accel = get_max_allowed_accel(v_ego)
self.max_accel = get_max_allowed_accel(v_ego, ev_tuning, truck_tuning)
else:
if frogpilot_toggles.acceleration_profile == 1:
self.max_accel = get_max_accel_eco(v_ego)
self.max_accel = get_max_accel_eco(v_ego, ev_tuning, truck_tuning)
elif frogpilot_toggles.acceleration_profile == 2:
self.max_accel = get_max_accel_sport(v_ego)
self.max_accel = get_max_accel_sport(v_ego, ev_tuning, truck_tuning)
elif frogpilot_toggles.acceleration_profile == 3:
self.max_accel = get_max_allowed_accel(v_ego)
self.max_accel = get_max_allowed_accel(v_ego, ev_tuning, truck_tuning)
else:
self.max_accel = get_max_accel(v_ego)
self.max_accel = get_max_accel_standard(v_ego, ev_tuning, truck_tuning)
if frogpilot_toggles.human_acceleration:
self.max_accel = min(get_max_accel_low_speeds(self.max_accel, self.frogpilot_planner.v_cruise), self.max_accel)
self.max_accel = min(get_max_accel_ramp_off(self.max_accel, self.frogpilot_planner.v_cruise, v_ego), self.max_accel)
if self.frogpilot_planner.tracking_lead:
self.min_accel = ACCEL_MIN
elif sm["frogpilotCarState"].forceCoast:
if sm["frogpilotCarState"].forceCoast:
self.min_accel = A_CRUISE_MIN_ECO
elif frogpilot_toggles.map_deceleration and (eco_gear or sport_gear):
if eco_gear:
@@ -79,4 +146,4 @@ class FrogPilotAcceleration:
elif frogpilot_toggles.deceleration_profile == 2:
self.min_accel = A_CRUISE_MIN_SPORT
else:
self.min_accel = CRUISE_MIN_ACCEL
self.min_accel = A_CRUISE_MIN
+24 -13
View File
@@ -1,16 +1,19 @@
#!/usr/bin/env python3
import numpy as np
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import COMFORT_BRAKE, STOP_DISTANCE, desired_follow_distance, get_jerk_factor, get_T_FOLLOW
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import COMFORT_BRAKE, desired_follow_distance, get_jerk_factor, get_T_FOLLOW
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT
TRAFFIC_MODE_BP = [0., CITY_SPEED_LIMIT]
PERSONALITY_BP = [20. * CV.KPH_TO_MS, 90. * CV.KPH_TO_MS]
class FrogPilotFollowing:
def __init__(self, FrogPilotPlanner):
self.frogpilot_planner = FrogPilotPlanner
self.disable_throttle = False
self.following_lead = False
self.slower_lead = False
@@ -47,12 +50,16 @@ class FrogPilotFollowing:
frogpilot_toggles.custom_personalities, sm["controlsState"].personality
)
self.t_follow = get_T_FOLLOW(
t_follow_param = get_T_FOLLOW(
frogpilot_toggles.aggressive_follow,
frogpilot_toggles.standard_follow,
frogpilot_toggles.relaxed_follow,
frogpilot_toggles.custom_personalities, sm["controlsState"].personality
)
if isinstance(t_follow_param, list):
self.t_follow = float(np.interp(v_ego, PERSONALITY_BP, t_follow_param))
else:
self.t_follow = float(t_follow_param)
else:
self.base_acceleration_jerk = 0
self.base_danger_jerk = 0
@@ -63,7 +70,12 @@ class FrogPilotFollowing:
self.danger_jerk = self.base_danger_jerk
self.speed_jerk = self.base_speed_jerk
self.following_lead = self.frogpilot_planner.tracking_lead and self.frogpilot_planner.lead_one.dRel < (self.t_follow + 1) * v_ego
self.following_lead = self.frogpilot_planner.tracking_lead and self.frogpilot_planner.lead_one.dRel < (self.t_follow * 2) * v_ego
self.disable_throttle = self.frogpilot_planner.tracking_lead and not self.following_lead
self.disable_throttle &= self.frogpilot_planner.lead_one.dRel + 6.0 < (self.t_follow * 2 * 2) * v_ego
self.disable_throttle &= self.frogpilot_planner.lead_one.vLead < v_ego * 0.75
if sm["controlsState"].enabled and self.frogpilot_planner.tracking_lead:
self.update_follow_values(self.frogpilot_planner.lead_one.dRel, v_ego, self.frogpilot_planner.lead_one.vLead, frogpilot_toggles)
@@ -75,21 +87,20 @@ class FrogPilotFollowing:
# Offset by FrogAi for FrogPilot for a more natural approach to a faster lead
if frogpilot_toggles.human_following and v_lead > v_ego:
distance_factor = max(lead_distance - (v_ego * self.t_follow), 1)
accelerating_offset = float(np.clip(STOP_DISTANCE - v_ego, 1, distance_factor))
self.acceleration_jerk /= accelerating_offset
self.speed_jerk /= accelerating_offset
self.t_follow /= accelerating_offset
from frogpilot.common.frogpilot_variables import get_frogpilot_toggles
fp_toggles = get_frogpilot_toggles()
acceleration_offset = float(np.clip(fp_toggles.stop_distance - v_ego, 1, distance_factor))
self.acceleration_jerk /= acceleration_offset
self.speed_jerk /= acceleration_offset
self.t_follow /= acceleration_offset
# Offset by FrogAi for FrogPilot for a more natural approach to a slower lead
if (frogpilot_toggles.conditional_slower_lead or frogpilot_toggles.human_following) and v_lead < v_ego:
distance_factor = max(lead_distance - (v_lead * self.t_follow), 1)
braking_offset = float(np.clip(min(v_ego - v_lead, v_lead) - COMFORT_BRAKE, 1, distance_factor))
if frogpilot_toggles.human_following:
if not self.following_lead and v_lead > CITY_SPEED_LIMIT:
far_lead_offset = max(lead_distance - (v_ego * self.t_follow) - STOP_DISTANCE, 0)
else:
far_lead_offset = 0
from frogpilot.common.frogpilot_variables import get_frogpilot_toggles
fp_toggles = get_frogpilot_toggles()
far_lead_offset = max(lead_distance - (v_ego * self.t_follow) - fp_toggles.stop_distance, 0)
self.t_follow /= braking_offset + far_lead_offset
self.slower_lead = braking_offset > 1
+1 -1
View File
@@ -51,7 +51,7 @@ class FrogPilotTracking:
self.sound = FrogPilotAudibleAlert.none
self.state = State.disabled
self.model_name = clean_model_name(dict(zip(frogpilot_toggles.available_models.split(","), frogpilot_toggles.available_model_names.split(",")))[frogpilot_toggles.model])
self.model_name = clean_model_name(frogpilot_toggles.model_name)
def update(self, now, time_validated, sm, frogpilot_toggles):
v_cruise = min(sm["controlsState"].vCruiseCluster, V_CRUISE_MAX) * CV.KPH_TO_MS
+2 -11
View File
@@ -18,6 +18,7 @@ class FrogPilotVCruise:
self.override_force_stop = False
self.override_force_stop_timer = 0
self.force_stop_timer = 0.0
def update(self, gps_position, now, time_validated, v_cruise, v_ego, sm, frogpilot_toggles):
force_stop = self.frogpilot_planner.cem.stop_light_detected and sm["controlsState"].enabled and frogpilot_toggles.force_stops
@@ -58,16 +59,6 @@ class FrogPilotVCruise:
self.csc_target = v_cruise
# Mike's extended lead linear braking
if self.frogpilot_planner.lead_one.vLead < v_ego > CRUISING_SPEED and sm["controlsState"].enabled and self.frogpilot_planner.tracking_lead and frogpilot_toggles.human_following:
if not self.frogpilot_planner.frogpilot_following.following_lead:
decel_rate = (v_ego - self.frogpilot_planner.lead_one.vLead)**2 / self.frogpilot_planner.lead_one.dRel
self.braking_target = max(v_ego - (decel_rate * DT_MDL), self.frogpilot_planner.lead_one.vLead + CRUISING_SPEED)
else:
self.braking_target = v_cruise
else:
self.braking_target = v_cruise
# Pfeiferj's Speed Limit Controller
self.slc.frogpilot_toggles = frogpilot_toggles
@@ -97,7 +88,7 @@ class FrogPilotVCruise:
self.tracked_model_length = self.frogpilot_planner.model_length
targets = [self.braking_target, self.csc_target, v_cruise]
targets = [self.csc_target, v_cruise]
if frogpilot_toggles.speed_limit_controller:
targets.append(max(self.slc.overridden_speed, self.slc_target + self.slc_offset) - v_ego_diff)
@@ -1,17 +1,20 @@
#!/usr/bin/env python3
# Twilsonco's Lateral Neural Network Feedforward
from collections import deque
from difflib import SequenceMatcher
from typing import NamedTuple
# Twilsonco's Lateral Neural Network Feedforward Controller
import json
import math
import numpy as np
import os
from collections import deque
from difflib import SequenceMatcher
from cereal import log
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.numpy_fast import interp
from openpilot.common.params import Params
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
from openpilot.selfdrive.controls.lib.pid import PIDController
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.frogpilot.common.frogpilot_variables import NNFF_MODELS_PATH, get_nnff_model_files, params
@@ -31,8 +34,7 @@ from openpilot.frogpilot.common.frogpilot_variables import NNFF_MODELS_PATH, get
ACTIVATION_FUNCTION_NAMES = {'σ': 'sigmoid'}
LOW_SPEED_X = [0, 10, 20, 30]
LOW_SPEED_Y = [15, 13, 10, 5]
LOW_SPEED_Y_NN = [12, 3, 1, 0]
LOW_SPEED_Y = [12, 3, 1, 0]
LAT_PLAN_MIN_IDX = 5
@@ -126,7 +128,7 @@ def get_nn_model_path(car, eps_firmware) -> str | None:
def find_valid_model(*queries):
for query in queries:
path, score = best_model_path(query)
if path and car in path and score >= 0.9:
if path and score >= 0.9:
return path
return None
@@ -152,21 +154,18 @@ def sign(x):
def similarity(s1: str, s2: str) -> float:
return SequenceMatcher(None, s1, s2).ratio()
class LatControlInputs(NamedTuple):
lateral_acceleration: float
roll_compensation: float
vego: float
aego: float
class NeuralNetworkFeedforward:
def __init__(self, CP, LatControlTorque):
self.lat_control_torque = LatControlTorque
class LatControlNNFF(LatControl):
def __init__(self, CP, CI, dt):
super().__init__(CP, CI, dt)
self.lat_torque_nn_model = get_nn_model(CP.carFingerprint, str(next((fw.fwVersion for fw in CP.carFw if fw.ecu == "eps"), "")).replace("\\", ""))
self.nnff_loaded = self.lat_torque_nn_model is not None
self.torque_from_lateral_accel = self.lat_control_torque.torque_from_lateral_accel
self.use_steering_angle = self.lat_control_torque.torque_params.useSteeringAngle
self.torque_params = CP.lateralTuning.torque
self.pid = PIDController(self.torque_params.kp, self.torque_params.ki,
pos_limit=self.steer_max, neg_limit=-self.steer_max)
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
self.use_steering_angle = self.torque_params.useSteeringAngle
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
# Instantaneous lateral jerk changes very rapidly, making it not useful on its own,
# however, we can "look ahead" to the future planned lateral jerk in order to gauge
@@ -184,7 +183,6 @@ class NeuralNetworkFeedforward:
self.lat_jerk_friction_factor = 0.4
# precompute time differences between ModelConstants.T_IDXS
self.lateral_delay = CP.steerActuatorDelay
self.t_diffs = np.diff(ModelConstants.T_IDXS)
self.pitch = FirstOrderFilter(0.0, 0.5, 0.01)
@@ -196,7 +194,7 @@ class NeuralNetworkFeedforward:
# setup future time offsets
self.future_times = [0.3, 0.6, 1.0, 1.5] # seconds in the future
self.nn_future_times = [time + self.lateral_delay for time in self.future_times]
self.nn_future_times = [time + CP.steerActuatorDelay for time in self.future_times]
# setup past time offsets
self.past_times = [-0.3, -0.2, -0.1]
@@ -207,97 +205,150 @@ class NeuralNetworkFeedforward:
self.roll_deque = deque(maxlen=history_check_frames[0])
def update_live_delay(self, lateral_delay):
self.lateral_delay = lateral_delay
self.nn_future_times = [time + self.lateral_delay for time in self.future_times]
self.nn_future_times = [time + lateral_delay for time in self.future_times]
self.past_future_len = len(self.past_times) + len(self.nn_future_times)
def compute_nnff(self, CS, VM, actual_lateral_accel, desired_lateral_accel, gravity_adjusted_lateral_accel, lateral_accel_deadzone, llk, measurement, model_data, params, pid_log, roll_compensation, setpoint, frogpilot_toggles):
if self.use_steering_angle:
actual_curvature_rate = -VM.calc_curvature(math.radians(CS.steeringRateDeg), CS.vEgo, 0.0)
actual_lateral_jerk = actual_curvature_rate * CS.vEgo ** 2
def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction):
self.torque_params.latAccelFactor = latAccelFactor
self.torque_params.latAccelOffset = latAccelOffset
self.torque_params.friction = friction
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay, llk, model_data, frogpilot_toggles):
pid_log = log.ControlsState.LateralTorqueState.new_message()
if not active:
output_torque = 0.0
pid_log.active = False
else:
actual_lateral_jerk = 0.0
actual_curvature_vm = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
if self.use_steering_angle:
actual_curvature = actual_curvature_vm
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
else:
actual_curvature_llk = llk.angularVelocityCalibrated.value[2] / CS.vEgo
actual_curvature = interp(CS.vEgo, [2.0, 5.0], [actual_curvature_vm, actual_curvature_llk])
curvature_deadzone = 0.0
desired_lateral_accel = desired_curvature * CS.vEgo ** 2
model_good = model_data is not None and len(model_data.orientation.x) >= CONTROL_N
# desired rate is the desired rate of change in the setpoint, not the absolute desired curvature
# desired_lateral_jerk = desired_curvature_rate * CS.vEgo ** 2
actual_lateral_accel = actual_curvature * CS.vEgo ** 2
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2
if model_good:
# prepare "look-ahead" desired lateral jerk
lookahead = interp(CS.vEgo, self.friction_look_ahead_bp, self.friction_look_ahead_v)
friction_upper_idx = next((idxs for idxs, value in enumerate(ModelConstants.T_IDXS) if value > lookahead), 16)
low_speed_factor = interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2
setpoint = desired_lateral_accel + low_speed_factor * desired_curvature
measurement = actual_lateral_accel + low_speed_factor * actual_curvature
gravity_adjusted_lateral_accel = desired_lateral_accel - roll_compensation
if self.nnff_loaded and frogpilot_toggles.nnff or frogpilot_toggles.nnff_lite:
if self.use_steering_angle:
actual_curvature_rate = -VM.calc_curvature(math.radians(CS.steeringRateDeg), CS.vEgo, 0.0)
actual_lateral_jerk = actual_curvature_rate * CS.vEgo ** 2
else:
actual_lateral_jerk = 0.0
predicted_lateral_jerk = get_predicted_lateral_jerk(model_data.acceleration.y, self.t_diffs)
desired_lateral_jerk = (interp(self.lateral_delay, ModelConstants.T_IDXS, model_data.acceleration.y) - desired_lateral_accel) / self.lateral_delay
model_good = model_data is not None and len(model_data.orientation.x) >= CONTROL_N
lookahead_lateral_jerk = get_lookahead_value(predicted_lateral_jerk[LAT_PLAN_MIN_IDX:friction_upper_idx], desired_lateral_jerk)
if model_good:
# prepare "look-ahead" desired lateral jerk
lookahead = interp(CS.vEgo, self.friction_look_ahead_bp, self.friction_look_ahead_v)
friction_upper_idx = next((idxs for idxs, value in enumerate(ModelConstants.T_IDXS) if value > lookahead), 16)
if not self.use_steering_angle or lookahead_lateral_jerk == 0.0:
lookahead_lateral_jerk = 0.0
actual_lateral_jerk = 0.0
self.lat_accel_friction_factor = 1.0
predicted_lateral_jerk = get_predicted_lateral_jerk(model_data.acceleration.y, self.t_diffs)
desired_lateral_jerk = (interp(lat_delay, ModelConstants.T_IDXS, model_data.acceleration.y) - desired_lateral_accel) / lat_delay
lateral_jerk_setpoint = self.lat_jerk_friction_factor * lookahead_lateral_jerk
lateral_jerk_measurement = self.lat_jerk_friction_factor * actual_lateral_jerk
else:
lateral_jerk_setpoint = 0
lateral_jerk_measurement = 0
lookahead_lateral_jerk = 0
lookahead_lateral_jerk = get_lookahead_value(predicted_lateral_jerk[LAT_PLAN_MIN_IDX:friction_upper_idx], desired_lateral_jerk)
if self.lat_control_torque.nnff_loaded and model_good and frogpilot_toggles.nnff:
# update past data
pitch = 0
roll = params.roll
if len(llk.calibratedOrientationNED.value) > 1:
pitch = self.pitch.update(llk.calibratedOrientationNED.value[1])
roll = roll_pitch_adjust(roll, pitch)
if not self.use_steering_angle or lookahead_lateral_jerk == 0.0:
lookahead_lateral_jerk = 0.0
actual_lateral_jerk = 0.0
self.lat_accel_friction_factor = 1.0
self.roll_deque.append(roll)
self.lateral_accel_desired_deque.append(desired_lateral_accel)
lateral_jerk_setpoint = self.lat_jerk_friction_factor * lookahead_lateral_jerk
lateral_jerk_measurement = self.lat_jerk_friction_factor * actual_lateral_jerk
else:
lateral_jerk_setpoint = 0
lateral_jerk_measurement = 0
lookahead_lateral_jerk = 0
# prepare past and future values
# adjust future times to account for longitudinal acceleration
adjusted_future_times = [time + 0.5 * CS.aEgo * (time / max(CS.vEgo, 1.0)) for time in self.nn_future_times]
past_rolls = [self.roll_deque[min(len(self.roll_deque)-1, offset)] for offset in self.history_frame_offsets]
future_rolls = [roll_pitch_adjust(interp(time, ModelConstants.T_IDXS, model_data.orientation.x) + roll, interp(time, ModelConstants.T_IDXS, model_data.orientation.y) + pitch) for time in adjusted_future_times]
if self.nnff_loaded and model_good and frogpilot_toggles.nnff:
# update past data
pitch = 0
roll = params.roll
if len(llk.calibratedOrientationNED.value) > 1:
pitch = self.pitch.update(llk.calibratedOrientationNED.value[1])
roll = roll_pitch_adjust(roll, pitch)
past_lateral_accels_desired = [self.lateral_accel_desired_deque[min(len(self.lateral_accel_desired_deque)-1, offset)] for offset in self.history_frame_offsets]
future_lateral_accels = [interp(time, ModelConstants.T_IDXS[:CONTROL_N], model_data.acceleration.y) for time in adjusted_future_times]
self.roll_deque.append(roll)
self.lateral_accel_desired_deque.append(desired_lateral_accel)
base_input = [CS.vEgo, roll]
nnff_common = past_rolls + future_rolls
# prepare past and future values
# adjust future times to account for longitudinal acceleration
adjusted_future_times = [time + 0.5 * CS.aEgo * (time / max(CS.vEgo, 1.0)) for time in self.nn_future_times]
past_rolls = [self.roll_deque[min(len(self.roll_deque)-1, offset)] for offset in self.history_frame_offsets]
future_rolls = [roll_pitch_adjust(interp(time, ModelConstants.T_IDXS, model_data.orientation.x) + roll, interp(time, ModelConstants.T_IDXS, model_data.orientation.y) + pitch) for time in adjusted_future_times]
# compute NNFF error response
nnff_setpoint_input = base_input[:1] + [setpoint, lateral_jerk_setpoint] + base_input[1:] + [setpoint] * self.past_future_len + nnff_common
nnff_measurement_input = base_input[:1] + [measurement, lateral_jerk_measurement] + base_input[1:] + [measurement] * self.past_future_len + nnff_common
past_lateral_accels_desired = [self.lateral_accel_desired_deque[min(len(self.lateral_accel_desired_deque)-1, offset)] for offset in self.history_frame_offsets]
future_lateral_accels = [interp(time, ModelConstants.T_IDXS[:CONTROL_N], model_data.acceleration.y) for time in adjusted_future_times]
torque_from_setpoint = self.lat_torque_nn_model.evaluate(nnff_setpoint_input)
torque_from_measurement = self.lat_torque_nn_model.evaluate(nnff_measurement_input)
base_input = [CS.vEgo, roll]
nnff_common = past_rolls + future_rolls
pid_log.error = torque_from_setpoint - torque_from_measurement
# compute NNFF error response
nnff_setpoint_input = base_input[:1] + [setpoint, lateral_jerk_setpoint] + base_input[1:] + [setpoint] * self.past_future_len + nnff_common
nnff_measurement_input = base_input[:1] + [measurement, lateral_jerk_measurement] + base_input[1:] + [measurement] * self.past_future_len + nnff_common
error_blend = interp(abs(desired_lateral_accel), [1.0, 2.0], [0.0, 1.0])
if error_blend > 0.0: # blend in stronger error response when in high lat accel
torque_from_error = self.lat_torque_nn_model.evaluate([CS.vEgo, setpoint - measurement, lateral_jerk_setpoint - lateral_jerk_measurement, 0.0])
if sign(pid_log.error) == sign(torque_from_error) and abs(pid_log.error) < abs(torque_from_error):
pid_log.error = pid_log.error * (1.0 - error_blend) + torque_from_error * error_blend
torque_from_setpoint = self.lat_torque_nn_model.evaluate(nnff_setpoint_input)
torque_from_measurement = self.lat_torque_nn_model.evaluate(nnff_measurement_input)
# compute feedforward (same as nn setpoint output)
error = setpoint - measurement
friction_input = self.lat_accel_friction_factor * error + self.lat_jerk_friction_factor * lookahead_lateral_jerk
nn_input = [CS.vEgo, desired_lateral_accel, friction_input, roll] + past_lateral_accels_desired + future_lateral_accels + nnff_common
ff = self.lat_torque_nn_model.evaluate(nn_input)
pid_log.error = torque_from_setpoint - torque_from_measurement
# apply friction override for cars with low NN friction response
if self.nn_friction_override:
pid_log.error += self.torque_from_lateral_accel(0.0, self.lat_control_torque.torque_params)
else:
torque_from_measurement = self.torque_from_lateral_accel(measurement, self.lat_control_torque.torque_params)
torque_from_setpoint = self.torque_from_lateral_accel(setpoint, self.lat_control_torque.torque_params)
error_blend = interp(abs(desired_lateral_accel), [1.0, 2.0], [0.0, 1.0])
if error_blend > 0.0: # blend in stronger error response when in high lat accel
torque_from_error = self.lat_torque_nn_model.evaluate([CS.vEgo, setpoint - measurement, lateral_jerk_setpoint - lateral_jerk_measurement, 0.0])
if sign(pid_log.error) == sign(torque_from_error) and abs(pid_log.error) < abs(torque_from_error):
pid_log.error = pid_log.error * (1.0 - error_blend) + torque_from_error * error_blend
pid_log.error = float(torque_from_setpoint - torque_from_measurement)
# compute feedforward (same as nn setpoint output)
error = setpoint - measurement
friction_input = self.lat_accel_friction_factor * error + self.lat_jerk_friction_factor * lookahead_lateral_jerk
nn_input = [CS.vEgo, desired_lateral_accel, friction_input, roll] + past_lateral_accels_desired + future_lateral_accels + nnff_common
ff = self.lat_torque_nn_model.evaluate(nn_input)
error = desired_lateral_accel - actual_lateral_accel
friction_input = self.lat_accel_friction_factor * error + self.lat_jerk_friction_factor * lookahead_lateral_jerk
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.lat_control_torque.torque_params)
# apply friction override for cars with low NN friction response
if self.nn_friction_override:
pid_log.error += self.torque_from_lateral_accel(0.0, self.torque_params)
else:
torque_from_measurement = self.torque_from_lateral_accel(measurement, self.torque_params)
torque_from_setpoint = self.torque_from_lateral_accel(setpoint, self.torque_params)
return pid_log, ff
pid_log.error = float(torque_from_setpoint - torque_from_measurement)
error = desired_lateral_accel - actual_lateral_accel
friction_input = self.lat_accel_friction_factor * error + self.lat_jerk_friction_factor * lookahead_lateral_jerk
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params)
else:
torque_from_measurement = self.torque_from_lateral_accel(measurement, self.torque_params)
torque_from_setpoint = self.torque_from_lateral_accel(setpoint, self.torque_params)
pid_log.error = float(torque_from_setpoint - torque_from_measurement)
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params)
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
output_torque = self.pid.update(pid_log.error,
feedforward=ff,
speed=CS.vEgo,
freeze_integrator=freeze_integrator)
pid_log.active = True
pid_log.p = float(self.pid.p)
pid_log.i = float(self.pid.i)
pid_log.d = float(self.pid.d)
pid_log.f = float(self.pid.f)
pid_log.output = float(-output_torque)
pid_log.actualLateralAccel = float(actual_lateral_accel)
pid_log.desiredLateralAccel = float(desired_lateral_accel)
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
# TODO left is positive in this convention
return -output_torque, 0.0, pid_log
+1 -1
View File
@@ -17,7 +17,7 @@ from openpilot.frogpilot.common.frogpilot_variables import MAPD_PATH, RESOURCES_
VERSION = "v2"
GITHUB_VERSION_URL = f"https://github.com/{RESOURCES_REPO}/raw/Versions/mapd_version_{VERSION}.json"
GITLAB_VERSION_URL = f"https://gitlab.com/{RESOURCES_REPO}/-/raw/Versions/mapd_version_{VERSION}.json"
GITLAB_VERSION_URL = f"https://gitlab.com/firestar5683/FrogPilot-Resources/-/raw/Versions/mapd_version_{VERSION}.json"
VERSION_PATH = Path("/data/media/0/osm/mapd_version")
Binary file not shown.
Binary file not shown.
+66 -27
View File
@@ -20,6 +20,30 @@ from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles
BASE_URL = "https://nominatim.openstreetmap.org"
MINIMUM_POPULATION = 100_000
SEARCH_RADIUS_DEGREES = 1.45
def get_population_value(population_str):
if population_str is None:
return None
try:
return int(str(population_str).replace(",", "").split(";")[0].strip())
except Exception:
return None
def search_nearby_major_cities(lat, lon, session, state_name, country_name):
viewbox = f"{lon - SEARCH_RADIUS_DEGREES},{lat + SEARCH_RADIUS_DEGREES},{lon + SEARCH_RADIUS_DEGREES},{lat - SEARCH_RADIUS_DEGREES}"
cities = (session.get(f"{BASE_URL}/search", params={
"addressdetails": 1, "bounded": 1, "extratags": 1, "format": "jsonv2", "limit": 20, "q": "city", "viewbox": viewbox
}, timeout=10).json() or [])
qualifying = [c for c in cities if (get_population_value((c.get("extratags") or {}).get("population")) or 0) >= MINIMUM_POPULATION]
if not qualifying:
return None
nearest = min(qualifying, key=lambda c: (float(c["lat"]) - lat) ** 2 + (float(c["lon"]) - lon) ** 2)
addr = nearest.get("address") or {}
return float(nearest["lat"]), float(nearest["lon"]), addr.get("city") or addr.get("town") or nearest.get("display_name", "").split(",")[0], state_name, country_name
def get_city_center(latitude, longitude):
try:
@@ -44,14 +68,7 @@ def get_city_center(latitude, longitude):
if data:
tags = data[0]
population = (tags.get("extratags") or {}).get("population")
population_value = None
if population is not None:
try:
population_value = int(str(population).replace(",", "").split(";")[0].strip())
except Exception:
population_value = None
population_value = get_population_value((tags.get("extratags") or {}).get("population"))
if population_value is not None and population_value >= MINIMUM_POPULATION:
latitude_value = float(tags["lat"])
@@ -62,6 +79,10 @@ def get_city_center(latitude, longitude):
return latitude_value, longitude_value, city_label, state_name, country_name
nearby_result = search_nearby_major_cities(latitude, longitude, session, state_name, country_name)
if nearby_result:
return nearby_result
query = f"{state_name} state capital" if country_code == "us" else f"capital of {state_name}, {country_name}"
response = session.get(f"{BASE_URL}/search", params={"addressdetails": 1, "extratags": 1, "format": "jsonv2", "limit": 5, "q": query}, timeout=10)
response.raise_for_status()
@@ -97,6 +118,19 @@ def get_city_center(latitude, longitude):
print(f"Falling back to (0, 0) for {latitude}, {longitude}")
return float(0.0), float(0.0), "N/A", "N/A", "N/A"
def update_branch_commits(now):
points = []
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
def is_up_to_date(build_metadata):
remote_commit = run_cmd(["git", "ls-remote", "origin", build_metadata.channel], f"Fetched remote commit", "Failed to fetch remote commit", report=False)
@@ -116,11 +150,9 @@ def send_stats():
if frogpilot_toggles.car_make == "mock":
return
bucket = os.environ.get("STATS_BUCKET", "")
org_ID = os.environ.get("STATS_ORG_ID", "")
token = os.environ.get("STATS_TOKEN", "")
url = os.environ.get("STATS_URL", "")
bucket = "StarPilot"
org_ID = "StarPilot"
url = "https://stats.firestar.link"
frogpilot_stats = json.loads(params.get("FrogPilotStats") or "{}")
location = json.loads(params.get("LastGPSPosition") or "{}")
@@ -144,15 +176,23 @@ def send_stats():
selected_theme = random.choice([item for item, count in most_common if count == max_count]).replace("-user_created", "").replace("_", " ")
point = (Point("user_stats")
now = datetime.now(timezone.utc)
user_point = (
Point("user_stats")
.tag("car_make", "GM" if frogpilot_toggles.car_make == "gm" else frogpilot_toggles.car_make.title())
.tag("car_model", frogpilot_toggles.car_model)
.tag("city", city)
.tag("country", country)
.tag("device", HARDWARE.get_device_type())
.tag("driving_model", clean_model_name(frogpilot_toggles.model_name))
.tag("state", state)
.tag("theme", selected_theme.title())
.tag("branch", build_metadata.channel)
.tag("dongle_id", params.get("FrogPilotDongleId", encoding="utf-8"))
.field("blocked_user", frogpilot_toggles.block_user)
.field("car_make", "GM" if frogpilot_toggles.car_make == "gm" else frogpilot_toggles.car_make.title())
.field("car_model", frogpilot_toggles.car_model)
.field("city", city)
.field("country", country)
.field("current_months_kilometers", int(frogpilot_stats.get("CurrentMonthsKilometers", 0)))
.field("device", HARDWARE.get_device_type())
.field("driving_model", clean_model_name(frogpilot_toggles.model_name))
.field("event", 1)
.field("frogpilot_drives", int(frogpilot_stats.get("FrogPilotDrives", 0)))
.field("frogpilot_hours", float(frogpilot_stats.get("FrogPilotSeconds", 0)) / (60 * 60))
@@ -162,13 +202,12 @@ def send_stats():
.field("has_openpilot_longitudinal", frogpilot_toggles.openpilot_longitudinal)
.field("has_pedal", frogpilot_toggles.has_pedal)
.field("has_sdsu", frogpilot_toggles.has_sdsu)
.field("has_sascm", frogpilot_toggles.has_sascm)
.field("has_zss", frogpilot_toggles.has_zss)
.field("latitude", latitude)
.field("longitude", longitude)
.field("rainbow_path", frogpilot_toggles.rainbow_path)
.field("random_events", frogpilot_toggles.random_events)
.field("state", state)
.field("theme", selected_theme.title())
.field("total_aol_seconds", float(frogpilot_stats.get("AOLTime", 0)))
.field("total_lateral_seconds", float(frogpilot_stats.get("LateralTime", 0)))
.field("total_longitudinal_seconds", float(frogpilot_stats.get("LongitudinalTime", 0)))
@@ -177,13 +216,13 @@ def send_stats():
.field("up_to_date", is_up_to_date(build_metadata))
.field("using_stock_acc", not (frogpilot_toggles.has_cc_long or frogpilot_toggles.openpilot_longitudinal))
.tag("branch", build_metadata.channel)
.tag("dongle_id", params.get("FrogPilotDongleId", encoding="utf-8"))
.time(datetime.now(timezone.utc))
.time(now)
)
InfluxDBClient(org=org_ID, token=token, url=url).write_api(write_options=SYNCHRONOUS).write(bucket=bucket, org=org_ID, record=point)
all_points = [user_point] + update_branch_commits(now)
client = InfluxDBClient(org=org_ID, token=org_ID, url=url)
client.write_api(write_options=SYNCHRONOUS).write(bucket=bucket, org=org_ID, record=all_points)
print("Successfully sent FrogPilot stats!")
except Exception as exception:
print(f"Failed to send FrogPilot stats: {exception}")
+6 -2
View File
@@ -13,11 +13,15 @@ class ModelConstants:
META_T_IDXS = [2., 4., 6., 8., 10.]
# model inputs constants
MODEL_FREQ = 20
HISTORY_FREQ = 5
HISTORY_LEN_SECONDS = 5
TEMPORAL_SKIP = MODEL_FREQ // HISTORY_FREQ
FULL_HISTORY_BUFFER_LEN = MODEL_FREQ * HISTORY_LEN_SECONDS
INPUT_HISTORY_BUFFER_LEN = HISTORY_FREQ * HISTORY_LEN_SECONDS
N_FRAMES = 2
MODEL_RUN_FREQ = 20
MODEL_CONTEXT_FREQ = 5 # "model_trained_fps"
FULL_HISTORY_BUFFER_LEN = MODEL_RUN_FREQ * MODEL_CONTEXT_FREQ
TEMPORAL_SKIP = MODEL_RUN_FREQ // MODEL_CONTEXT_FREQ
FEATURE_LEN = 512
+37 -6
View File
@@ -3,11 +3,25 @@ import capnp
import numpy as np
from cereal import log
from openpilot.frogpilot.tinygrad_modeld.constants import ModelConstants, Plan, Meta
from openpilot.selfdrive.controls.lib.drive_helpers import get_curvature_from_plan
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
ConfidenceClass = log.ModelDataV2.ConfidenceClass
# Return curvature for lateral action. If the model outputs desired_curvature and we're not in mlsim mode,
# use it directly; otherwise derive from the plan using yaw and yaw-rate.
def get_curvature_from_output(output: dict, plan: np.ndarray, v_ego: float, lat_action_t: float, mlsim: bool) -> float:
if not mlsim:
desired = output.get('desired_curvature')
if desired is not None:
return float(desired[0, 0])
# Use yaw (index 2) and yaw_rate (index 2)
theta = plan[:, Plan.T_FROM_CURRENT_EULER][:, 2]
theta_dot = plan[:, Plan.ORIENTATION_RATE][:, 2]
return float(get_curvature_from_plan(theta, theta_dot, ModelConstants.T_IDXS, v_ego, lat_action_t))
class PublishState:
def __init__(self):
@@ -82,15 +96,32 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
modelV2.timestampEof = timestamp_eof
modelV2.modelExecutionTime = model_execution_time
# normalize plan tensors to (IDX_N, WIDTH)
plan_arr = net_output_data['plan'][0]
plan_stds_arr = net_output_data['plan_stds'][0]
# plan
fill_xyzt(modelV2.position, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.POSITION].T, *net_output_data['plan_stds'][0,:,Plan.POSITION].T)
fill_xyzt(modelV2.velocity, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.VELOCITY].T)
fill_xyzt(modelV2.acceleration, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ACCELERATION].T)
fill_xyzt(modelV2.orientation, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.T_FROM_CURRENT_EULER].T)
fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T)
fill_xyzt(modelV2.position, ModelConstants.T_IDXS, *plan_arr[:,Plan.POSITION].T, *plan_stds_arr[:,Plan.POSITION].T)
fill_xyzt(modelV2.velocity, ModelConstants.T_IDXS, *plan_arr[:,Plan.VELOCITY].T)
fill_xyzt(modelV2.acceleration, ModelConstants.T_IDXS, *plan_arr[:,Plan.ACCELERATION].T)
fill_xyzt(modelV2.orientation, ModelConstants.T_IDXS, *plan_arr[:,Plan.T_FROM_CURRENT_EULER].T)
fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *plan_arr[:,Plan.ORIENTATION_RATE].T)
# temporal pose
temporal_pose = modelV2.temporalPose
if 'sim_pose' in net_output_data:
temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist()
temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist()
else:
temporal_pose.trans = plan_arr[0,Plan.VELOCITY].tolist()
temporal_pose.transStd = plan_stds_arr[0,Plan.VELOCITY].tolist()
temporal_pose.rot = plan_arr[0,Plan.ORIENTATION_RATE].tolist()
temporal_pose.rotStd = plan_stds_arr[0,Plan.ORIENTATION_RATE].tolist()
# poly path
fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T)
fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *plan_arr[:,Plan.POSITION].T)
# action
modelV2.action = action
@@ -1,13 +1,16 @@
import numpy as np
from openpilot.frogpilot.tinygrad_modeld.constants import ModelConstants
def safe_exp(x, out=None):
# -11 is around 10**14, more causes float16 overflow
return np.exp(np.clip(x, -np.inf, 11), out=out)
def sigmoid(x):
return 1. / (1. + safe_exp(-x))
def softmax(x, axis=-1):
x -= np.max(x, axis=axis, keepdims=True)
if x.dtype == np.float32 or x.dtype == np.float64:
@@ -17,15 +20,15 @@ def softmax(x, axis=-1):
x /= np.sum(x, axis=axis, keepdims=True)
return x
class Parser:
def __init__(self, ignore_missing=False):
self.ignore_missing = ignore_missing
def check_missing(self, outs, name):
missing = name not in outs
if missing and not self.ignore_missing:
if name not in outs and not self.ignore_missing:
raise ValueError(f"Missing output {name}")
return missing
return name not in outs
def parse_categorical_crossentropy(self, name, outs, out_shape=None):
if self.check_missing(outs, name):
@@ -85,45 +88,50 @@ class Parser:
outs[name] = pred_mu_final.reshape(final_shape)
outs[name + '_stds'] = pred_std_final.reshape(final_shape)
def is_mhp(self, outs, name, shape):
if self.check_missing(outs, name):
return False
if outs[name].shape[1] == 2 * shape:
return False
return True
def split_outputs(self, outs: dict[str, np.ndarray]) -> None:
if 'lead' in outs:
if outs['lead'].shape[1] == 2 * ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH:
self.parse_mdn('lead', outs, in_N=0, out_N=0,
out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH))
else:
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
out_shape=(ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH))
if 'plan' in outs:
plan_mhp = outs['plan'].shape[1] != 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH
plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0)
self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N,
out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
if 'planplus' in outs:
self.parse_mdn('planplus', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
if 'lane_lines' in outs:
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0,
out_shape=(ModelConstants.NUM_LANE_LINES, ModelConstants.IDX_N, ModelConstants.LANE_LINES_WIDTH))
if 'road_edges' in outs:
self.parse_mdn('road_edges', outs, in_N=0, out_N=0,
out_shape=(ModelConstants.NUM_ROAD_EDGES, ModelConstants.IDX_N, ModelConstants.LANE_LINES_WIDTH))
if 'sim_pose' in outs:
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
if 'lane_lines_prob' in outs:
self.parse_binary_crossentropy('lane_lines_prob', outs)
if 'lead_prob' in outs:
self.parse_binary_crossentropy('lead_prob', outs)
def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,))
self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_binary_crossentropy('lane_lines_prob', outs)
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH))
self.split_outputs(outs)
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN, ModelConstants.DESIRE_PRED_WIDTH))
self.parse_binary_crossentropy('meta', outs)
self.parse_binary_crossentropy('lead_prob', outs)
lead_mhp = self.is_mhp(outs, 'lead', ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH)
lead_in_N, lead_out_N = (ModelConstants.LEAD_MHP_N, ModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0)
lead_out_shape = (ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) if lead_mhp else \
(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH)
self.parse_mdn('lead', outs, in_N=lead_in_N, out_N=lead_out_N, out_shape=lead_out_shape)
return outs
def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH)
plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0)
self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.split_outputs(outs)
if 'lat_planner_solution' in outs:
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N, ModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
if 'desired_curvature' in outs:
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,))
for k in ['lead_prob', 'lane_lines_prob']:
self.parse_binary_crossentropy(k, outs)
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,))
self.parse_binary_crossentropy('lead_prob', outs)
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
return outs
def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
+263 -147
View File
@@ -2,10 +2,6 @@
import os
from openpilot.system.hardware import TICI
os.environ['DEV'] = 'QCOM' if TICI else 'LLVM'
USBGPU = "USBGPU" in os.environ
if USBGPU:
os.environ['DEV'] = 'AMD'
os.environ['AMD_IFACE'] = 'USB'
from tinygrad.tensor import Tensor
from tinygrad.dtype import dtypes
import time
@@ -14,6 +10,7 @@ import numpy as np
import cereal.messaging as messaging
from cereal import car, log
from pathlib import Path
from setproctitle import setproctitle
from cereal.messaging import PubMaster, SubMaster
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
from openpilot.common.swaglog import cloudlog
@@ -22,49 +19,47 @@ from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.realtime import config_realtime_process, DT_MDL
from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.common.transformations.model import get_warp_matrix
from openpilot.system import sentry
from openpilot.selfdrive.car.car_helpers import get_demo_car_params
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value, get_curvature_from_plan
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan_tomb_raider, smooth_value
from openpilot.frogpilot.tinygrad_modeld.parse_model_outputs import Parser
from openpilot.frogpilot.tinygrad_modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.frogpilot.tinygrad_modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState, get_curvature_from_output
from openpilot.frogpilot.tinygrad_modeld.constants import ModelConstants, Plan
from openpilot.frogpilot.tinygrad_modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
from openpilot.frogpilot.tinygrad_modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
from openpilot.frogpilot.common.frogpilot_variables import MODELS_PATH, get_frogpilot_toggles
from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles, MODELS_PATH
PROCESS_NAME = "frogpilot.tinygrad_modeld.tinygrad_modeld"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
VISION_PKL_PATH = Path(__file__).parent / 'models/driving_vision_tinygrad.pkl'
POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
LAT_SMOOTH_SECONDS = 0.1
LAT_SMOOTH_SECONDS = 0.0
LONG_SMOOTH_SECONDS = 0.3
MIN_LAT_CONTROL_SPEED = 0.3
def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,
lat_action_t: float, long_action_t: float, v_ego: float, use_curvature_from_plan: bool) -> log.ModelDataV2.Action:
lat_action_t: float, long_action_t: float, v_ego: float, mlsim: bool, is_v9: bool, frogpilot_toggles) -> log.ModelDataV2.Action:
plan = model_output['plan'][0]
desired_accel, should_stop = get_accel_from_plan(plan[:,Plan.VELOCITY][:,0],
plan[:,Plan.ACCELERATION][:,0],
ModelConstants.T_IDXS,
action_t=long_action_t)
if 'planplus' in model_output:
plan = plan + frogpilot_toggles.recovery_power*model_output['planplus'][0]
cloudlog.error(f"planplus applied: shape {model_output['planplus'].shape}, RECOVERY_POWER {frogpilot_toggles.recovery_power}")
desired_accel, should_stop = get_accel_from_plan_tomb_raider(plan[:,Plan.VELOCITY][:,0],
plan[:,Plan.ACCELERATION][:,0],
ModelConstants.T_IDXS,
action_t=long_action_t)
desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS)
if use_curvature_from_plan:
desired_curvature = get_curvature_from_plan(plan[:,Plan.T_FROM_CURRENT_EULER][:,2],
plan[:,Plan.ORIENTATION_RATE][:,2],
ModelConstants.T_IDXS,
v_ego,
lat_action_t)
if is_v9:
# V9: use desired_curvature if present; otherwise do NOT fall back to plan
if 'desired_curvature' in model_output:
desired_curvature = float(model_output['desired_curvature'][0, 0])
else:
desired_curvature = prev_action.desiredCurvature
else:
desired_curvature = model_output['desired_curvature'][0, 0]
desired_curvature = get_curvature_from_output(model_output, plan, v_ego, lat_action_t, mlsim=mlsim)
if v_ego > MIN_LAT_CONTROL_SPEED:
desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS)
else:
@@ -83,113 +78,196 @@ class FrameMeta:
if vipc is not None:
self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof
class InputQueues:
def __init__ (self, model_fps, env_fps, n_frames_input):
assert env_fps % model_fps == 0
assert env_fps >= model_fps
self.model_fps = model_fps
self.env_fps = env_fps
self.n_frames_input = n_frames_input
self.dtypes = {}
self.shapes = {}
self.q = {}
def update_dtypes_and_shapes(self, input_dtypes, input_shapes) -> None:
self.dtypes.update(input_dtypes)
if self.env_fps == self.model_fps:
self.shapes.update(input_shapes)
else:
for k in input_shapes:
shape = list(input_shapes[k])
if 'img' in k:
n_channels = shape[1] // self.n_frames_input
shape[1] = (self.env_fps // self.model_fps + (self.n_frames_input - 1)) * n_channels
else:
shape[1] = (self.env_fps // self.model_fps) * shape[1]
self.shapes[k] = tuple(shape)
def reset(self) -> None:
self.q = {k: np.zeros(self.shapes[k], dtype=self.dtypes[k]) for k in self.dtypes.keys()}
def enqueue(self, inputs:dict[str, np.ndarray]) -> None:
for k in inputs.keys():
if inputs[k].dtype != self.dtypes[k]:
raise ValueError(f'supplied input <{k}({inputs[k].dtype})> has wrong dtype, expected {self.dtypes[k]}')
input_shape = list(self.shapes[k])
input_shape[1] = -1
single_input = inputs[k].reshape(tuple(input_shape))
sz = single_input.shape[1]
self.q[k][:,:-sz] = self.q[k][:,sz:]
self.q[k][:,-sz:] = single_input
def get(self, *names) -> dict[str, np.ndarray]:
if self.env_fps == self.model_fps:
return {k: self.q[k] for k in names}
else:
out = {}
for k in names:
shape = self.shapes[k]
if 'img' in k:
n_channels = shape[1] // (self.env_fps // self.model_fps + (self.n_frames_input - 1))
out[k] = np.concatenate([self.q[k][:, s:s+n_channels] for s in np.linspace(0, shape[1] - n_channels, self.n_frames_input, dtype=int)], axis=1)
elif 'pulse' in k:
# any pulse within interval counts
out[k] = self.q[k].reshape((shape[0], shape[1] * self.model_fps // self.env_fps, self.env_fps // self.model_fps, -1)).max(axis=2)
else:
idxs = np.arange(-1, -shape[1], -self.env_fps // self.model_fps)[::-1]
out[k] = self.q[k][:, idxs]
return out
class ModelState:
frames: dict[str, DrivingModelFrame]
inputs: dict[str, np.ndarray]
output: np.ndarray
prev_desire: np.ndarray # for tracking the rising edge of the pulse
def __init__(self, context: CLContext, model: str):
with open(MODELS_PATH / f'{model}_driving_vision_metadata.pkl', 'rb') as f:
vision_metadata = pickle.load(f)
self.vision_input_shapes = vision_metadata['input_shapes']
self.vision_input_names = list(self.vision_input_shapes.keys())
self.vision_output_slices = vision_metadata['output_slices']
vision_output_size = vision_metadata['output_shapes']['outputs'][1]
def _build_policy_inputs(self, input_shapes: dict[str, tuple[int, ...]]) -> tuple[dict[str, np.ndarray], str | None]:
numpy_inputs: dict[str, np.ndarray] = {}
with open(MODELS_PATH / f'{model}_driving_policy_metadata.pkl', 'rb') as f:
policy_metadata = pickle.load(f)
self.policy_input_shapes = policy_metadata['input_shapes']
self.policy_output_slices = policy_metadata['output_slices']
policy_output_size = policy_metadata['output_shapes']['outputs'][1]
# Always-supported inputs (if model expects them)
desire_key_init = next((k for k in input_shapes if k.startswith('desire')), None)
if desire_key_init:
numpy_inputs[desire_key_init] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32)
if 'traffic_convention' in input_shapes:
numpy_inputs['traffic_convention'] = np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32)
if 'features_buffer' in input_shapes:
numpy_inputs['features_buffer'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
self.desire_type = 'desire_pulse' if 'desire_pulse' in self.policy_input_shapes else 'desire'
self.use_lateral_control_params = 'lateral_control_params' in self.policy_input_shapes
# Optional inputs for non-v11 (and some v10/v9 variants)
# Lateral control params
if 'lateral_control_params' in input_shapes:
numpy_inputs['lateral_control_params'] = np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32)
self.frames = {name: DrivingModelFrame(context, ModelConstants.MODEL_RUN_FREQ//ModelConstants.MODEL_CONTEXT_FREQ) for name in self.vision_input_names}
# Previous desired curvature: handle both singular and plural key names across model versions
prev_desired_curv_key = None
if 'prev_desired_curv' in input_shapes:
prev_desired_curv_key = 'prev_desired_curv'
numpy_inputs['prev_desired_curv'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
elif 'prev_desired_curvs' in input_shapes:
prev_desired_curv_key = 'prev_desired_curvs'
numpy_inputs['prev_desired_curvs'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
return numpy_inputs, prev_desired_curv_key
def __init__(self, context: CLContext):
# Dynamically build paths based on current model ID
params = Params()
model_id = params.get("Model", encoding="utf-8")
# Try to get ModelVersion, but handle case where parameter doesn't exist
model_version = None
try:
model_version = params.get("ModelVersion", encoding="utf-8")
except Exception as e:
cloudlog.warning(f"ModelVersion parameter not available: {e}")
model_dir = MODELS_PATH
# For the default "bd2" model, use built-in files from the models directory
if model_id == "bd2":
models_dir = Path(__file__).parent / "models"
VISION_PKL_PATH = models_dir / "driving_vision_tinygrad.pkl"
POLICY_PKL_PATH = models_dir / "driving_policy_tinygrad.pkl"
OFF_POLICY_PKL_PATH = models_dir / "driving_off_policy_tinygrad.pkl"
VISION_METADATA_PATH = models_dir / "driving_vision_metadata.pkl"
POLICY_METADATA_PATH = models_dir / "driving_policy_metadata.pkl"
OFF_POLICY_METADATA_PATH = models_dir / "driving_off_policy_metadata.pkl"
else:
VISION_PKL_PATH = model_dir / f"{model_id}_driving_vision_tinygrad.pkl"
POLICY_PKL_PATH = model_dir / f"{model_id}_driving_policy_tinygrad.pkl"
OFF_POLICY_PKL_PATH = model_dir / f"{model_id}_driving_off_policy_tinygrad.pkl"
VISION_METADATA_PATH = model_dir / f"{model_id}_driving_vision_metadata.pkl"
POLICY_METADATA_PATH = model_dir / f"{model_id}_driving_policy_metadata.pkl"
OFF_POLICY_METADATA_PATH = model_dir / f"{model_id}_driving_off_policy_metadata.pkl"
# If ModelVersion is not set or not available, try to determine it from available model data
if not model_version:
cloudlog.warning(f"ModelVersion not available for model {model_id}, attempting to determine from model data")
try:
# Try to get version from the model versions JSON file
versions_file = model_dir / ".model_versions.json"
if versions_file.is_file():
import json
with open(versions_file, "r") as f:
version_map = json.load(f)
if model_id in version_map:
model_version = version_map[model_id]
cloudlog.warning(f"Determined model version from JSON: {model_version}")
else:
cloudlog.error("Model versions JSON file not found, defaulting to v8")
model_version = "v8"
except Exception as e:
cloudlog.error(f"Failed to determine model version: {e}, defaulting to v8")
model_version = "v8"
try:
with open(VISION_METADATA_PATH, 'rb') as f:
vision_metadata = pickle.load(f)
except FileNotFoundError:
cloudlog.error(f"Missing metadata {VISION_METADATA_PATH}, downloading...")
from openpilot.frogpilot.assets.model_manager import ModelManager
ModelManager().download_model(model_id)
with open(VISION_METADATA_PATH, 'rb') as f:
vision_metadata = pickle.load(f)
self.vision_input_shapes = vision_metadata['input_shapes']
self.vision_input_names = list(self.vision_input_shapes.keys())
self.vision_output_slices = vision_metadata['output_slices']
vision_output_size = vision_metadata['output_shapes']['outputs'][1]
try:
with open(POLICY_METADATA_PATH, 'rb') as f:
policy_metadata = pickle.load(f)
except FileNotFoundError:
cloudlog.error(f"Missing metadata {POLICY_METADATA_PATH}, downloading...")
from openpilot.frogpilot.assets.model_manager import ModelManager
ModelManager().download_model(model_id)
with open(POLICY_METADATA_PATH, 'rb') as f:
policy_metadata = pickle.load(f)
self.policy_input_shapes = policy_metadata['input_shapes']
self.policy_output_slices = policy_metadata['output_slices']
policy_output_size = policy_metadata['output_shapes']['outputs'][1]
# Add policy_generation attribute after loading policy_metadata
self.policy_generation = model_version or "v8"
self.is_v11 = (self.policy_generation == "v11")
self.is_v9 = (self.policy_generation == "v9")
self.mlsim = (self.policy_generation in ("v8", "v10", "v11", "v12"))
self.frames = {name: DrivingModelFrame(context, ModelConstants.TEMPORAL_SKIP) for name in self.vision_input_names}
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
self.full_prev_desired_curv = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
self.temporal_idxs = slice(-1-(ModelConstants.TEMPORAL_SKIP*(ModelConstants.FULL_HISTORY_BUFFER_LEN-1)), None, ModelConstants.TEMPORAL_SKIP)
self.full_features_buffer = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
self.full_desire = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32)
self.temporal_idxs = slice(-1-(ModelConstants.TEMPORAL_SKIP*(ModelConstants.INPUT_HISTORY_BUFFER_LEN-1)), None, ModelConstants.TEMPORAL_SKIP)
# policy inputs (built dynamically to support all generations)
self.numpy_inputs, self.prev_desired_curv_key = self._build_policy_inputs(self.policy_input_shapes)
# Off-policy model (optional)
self.off_policy_enabled = False
self.off_policy_input_shapes: dict[str, tuple[int, ...]] = {}
self.off_policy_output_slices: dict[str, slice] = {}
self.off_policy_numpy_inputs: dict[str, np.ndarray] = {}
self.off_policy_prev_desired_curv_key: str | None = None
self.off_policy_desire_key: str | None = None
self.off_policy_inputs: dict[str, Tensor] | None = None
self.off_policy_output: np.ndarray | None = None
off_policy_metadata = None
if self.policy_generation == "v12" or OFF_POLICY_METADATA_PATH.is_file() or OFF_POLICY_PKL_PATH.is_file():
try:
with open(OFF_POLICY_METADATA_PATH, 'rb') as f:
off_policy_metadata = pickle.load(f)
except FileNotFoundError:
cloudlog.error(f"Missing metadata {OFF_POLICY_METADATA_PATH}, downloading...")
from openpilot.frogpilot.assets.model_manager import ModelManager
ModelManager().download_model(model_id)
try:
with open(OFF_POLICY_METADATA_PATH, 'rb') as f:
off_policy_metadata = pickle.load(f)
except FileNotFoundError:
cloudlog.warning(f"Off-policy metadata still missing: {OFF_POLICY_METADATA_PATH}")
if off_policy_metadata is not None:
self.off_policy_input_shapes = off_policy_metadata['input_shapes']
self.off_policy_output_slices = off_policy_metadata['output_slices']
off_policy_output_size = off_policy_metadata['output_shapes']['outputs'][1]
self.off_policy_numpy_inputs, self.off_policy_prev_desired_curv_key = self._build_policy_inputs(self.off_policy_input_shapes)
self.off_policy_desire_key = next((k for k in self.off_policy_numpy_inputs if k.startswith('desire')), None)
self.off_policy_inputs = {k: Tensor(v, device='NPY').realize() for k, v in self.off_policy_numpy_inputs.items()}
self.off_policy_output = np.zeros(off_policy_output_size, dtype=np.float32)
try:
with open(OFF_POLICY_PKL_PATH, "rb") as f:
self.off_policy_run = pickle.load(f)
self.off_policy_enabled = True
except FileNotFoundError:
cloudlog.warning(f"Missing off-policy model {OFF_POLICY_PKL_PATH}, skipping off-policy")
# Optional temporal buffer for previous desired curvature (allocate only if any model expects it)
if self.prev_desired_curv_key is not None or self.off_policy_prev_desired_curv_key is not None:
self.full_prev_desired_curv = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
# policy inputs
self.numpy_inputs = {k: np.zeros(self.policy_input_shapes[k], dtype=np.float32) for k in self.policy_input_shapes}
self.full_input_queues = InputQueues(ModelConstants.MODEL_CONTEXT_FREQ, ModelConstants.MODEL_RUN_FREQ, ModelConstants.N_FRAMES)
for k in [self.desire_type, 'features_buffer']:
self.full_input_queues.update_dtypes_and_shapes({k: self.numpy_inputs[k].dtype}, {k: self.numpy_inputs[k].shape})
self.full_input_queues.reset()
# img buffers are managed in openCL transform code
self.vision_inputs: dict[str, Tensor] = {}
self.vision_output = np.zeros(vision_output_size, dtype=np.float32)
self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
self.policy_output = np.zeros(policy_output_size, dtype=np.float32)
self.parser = Parser(ignore_missing=True)
self.parser = Parser()
self.off_policy_parser = Parser(ignore_missing=True)
with open(MODELS_PATH / f'{model}_driving_vision_tinygrad.pkl', "rb") as f:
with open(VISION_PKL_PATH, "rb") as f:
self.vision_run = pickle.load(f)
with open(MODELS_PATH / f'{model}_driving_policy_tinygrad.pkl', "rb") as f:
with open(POLICY_PKL_PATH, "rb") as f:
self.policy_run = pickle.load(f)
@property
def desire_key(self) -> str:
return next(key for key in self.numpy_inputs if key.startswith('desire'))
def slice_outputs(self, model_outputs: np.ndarray, output_slices: dict[str, slice]) -> dict[str, np.ndarray]:
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in output_slices.items()}
return parsed_model_outputs
@@ -197,15 +275,32 @@ class ModelState:
def run(self, bufs: dict[str, VisionBuf], transforms: dict[str, np.ndarray],
inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None:
# Model decides when action is completed, so desire input is just a pulse triggered on rising edge
inputs[self.desire_type][0] = 0
new_desire = np.where(inputs[self.desire_type] - self.prev_desire > .99, inputs[self.desire_type], 0)
self.prev_desire[:] = inputs[self.desire_type]
inputs[self.desire_key][0] = 0
new_desire = np.where(inputs[self.desire_key] - self.prev_desire > .99, inputs[self.desire_key], 0)
self.prev_desire[:] = inputs[self.desire_key]
if self.use_lateral_control_params:
self.full_desire[0,:-1] = self.full_desire[0,1:]
self.full_desire[0,-1] = new_desire
self.numpy_inputs[self.desire_key][:] = self.full_desire.reshape((1,ModelConstants.INPUT_HISTORY_BUFFER_LEN,ModelConstants.TEMPORAL_SKIP,-1)).max(axis=2)
if self.off_policy_enabled and self.off_policy_desire_key is not None:
self.off_policy_numpy_inputs[self.off_policy_desire_key][:] = self.numpy_inputs[self.desire_key]
if 'traffic_convention' in self.numpy_inputs:
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
if self.off_policy_enabled and 'traffic_convention' in self.off_policy_numpy_inputs:
self.off_policy_numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
if 'lateral_control_params' in self.numpy_inputs:
self.numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
if self.off_policy_enabled and 'lateral_control_params' in self.off_policy_numpy_inputs:
self.off_policy_numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
if prepare_only:
return None
imgs_cl = {name: self.frames[name].prepare(bufs[name], transforms[name].flatten()) for name in self.vision_input_names}
if TICI and not USBGPU:
if TICI:
# The imgs tensors are backed by opencl memory, only need init once
for key in imgs_cl:
if key not in self.vision_inputs:
@@ -215,54 +310,66 @@ class ModelState:
frame_input = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.vision_input_shapes[key])
self.vision_inputs[key] = Tensor(frame_input, dtype=dtypes.uint8).realize()
if prepare_only:
return None
self.vision_output = self.vision_run(**self.vision_inputs).contiguous().realize().uop.base.buffer.numpy()
vision_outputs_dict = self.parser.parse_vision_outputs(self.slice_outputs(self.vision_output, self.vision_output_slices))
self.full_input_queues.enqueue({'features_buffer': vision_outputs_dict['hidden_state'], self.desire_type: new_desire})
for k in [self.desire_type, 'features_buffer']:
self.numpy_inputs[k][:] = self.full_input_queues.get(k)[k]
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
self.full_features_buffer[0,:-1] = self.full_features_buffer[0,1:]
self.full_features_buffer[0,-1] = vision_outputs_dict['hidden_state'][0, :]
if 'features_buffer' in self.numpy_inputs:
self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs]
if self.off_policy_enabled and 'features_buffer' in self.off_policy_numpy_inputs:
self.off_policy_numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs]
self.policy_output = self.policy_run(**self.policy_inputs).contiguous().realize().uop.base.buffer.numpy()
policy_outputs_dict = self.parser.parse_policy_outputs(self.slice_outputs(self.policy_output, self.policy_output_slices))
if self.use_lateral_control_params:
# TODO model only uses last value now
# TODO model only uses last value now
if hasattr(self, 'full_prev_desired_curv') and 'desired_curvature' in policy_outputs_dict:
self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:]
self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :]
self.numpy_inputs['prev_desired_curv'][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs]
if self.prev_desired_curv_key is not None:
# v9 models expect zeros for prev_desired_curv(s); others use history
if self.is_v9:
self.numpy_inputs[self.prev_desired_curv_key][:] = 0 * self.full_prev_desired_curv[0, self.temporal_idxs]
else:
self.numpy_inputs[self.prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
if self.off_policy_enabled and self.off_policy_prev_desired_curv_key is not None:
if self.is_v9:
self.off_policy_numpy_inputs[self.off_policy_prev_desired_curv_key][:] = 0 * self.full_prev_desired_curv[0, self.temporal_idxs]
else:
self.off_policy_numpy_inputs[self.off_policy_prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict}
if self.off_policy_enabled:
self.off_policy_output = self.off_policy_run(**self.off_policy_inputs).contiguous().realize().uop.base.buffer.numpy()
off_policy_outputs_dict = self.off_policy_parser.parse_policy_outputs(
self.slice_outputs(self.off_policy_output, self.off_policy_output_slices)
)
combined_outputs_dict.update(off_policy_outputs_dict)
if SEND_RAW_PRED:
combined_outputs_dict['raw_pred'] = np.concatenate([self.vision_output.copy(), self.policy_output.copy()])
raw_pred = [self.vision_output.copy(), self.policy_output.copy()]
if self.off_policy_enabled and self.off_policy_output is not None:
raw_pred.append(self.off_policy_output.copy())
combined_outputs_dict['raw_pred'] = np.concatenate(raw_pred)
return combined_outputs_dict
def main(demo=False):
# FrogPilot variables
frogpilot_toggles = get_frogpilot_toggles()
cloudlog.warning("modeld init")
model_name = frogpilot_toggles.model
model_version = frogpilot_toggles.model_version
use_curvature_from_plan = frogpilot_toggles.model_version != "v7"
sentry.set_tag("daemon", PROCESS_NAME)
cloudlog.bind(daemon=PROCESS_NAME)
setproctitle(PROCESS_NAME)
config_realtime_process(7, 54)
cloudlog.warning("tinygrad_modeld init")
if not USBGPU:
# USB GPU currently saturates a core so can't do this yet,
# also need to move the aux USB interrupts for good timings
config_realtime_process(7, 54)
st = time.monotonic()
cloudlog.warning("setting up CL context")
cl_context = CLContext()
cloudlog.warning("CL context ready; loading model")
model = ModelState(cl_context, model_name)
cloudlog.warning(f"models loaded in {time.monotonic() - st:.1f}s, tinygrad_modeld starting")
model = ModelState(cl_context)
cloudlog.warning("models loaded, modeld starting")
# visionipc clients
while True:
@@ -295,7 +402,7 @@ def main(demo=False):
params = Params()
# setup filter to track dropped frames
frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_RUN_FREQ)
frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_FREQ)
frame_id = 0
last_vipc_frame_id = 0
run_count = 0
@@ -322,6 +429,9 @@ def main(demo=False):
DH = DesireHelper()
# FrogPilot variables
frogpilot_toggles = get_frogpilot_toggles()
while True:
# Keep receiving frames until we are at least 1 frame ahead of previous extra frame
while meta_main.timestamp_sof < meta_extra.timestamp_sof + 25000000:
@@ -391,11 +501,14 @@ def main(demo=False):
bufs = {name: buf_extra if 'big' in name else buf_main for name in model.vision_input_names}
transforms = {name: model_transform_extra if 'big' in name else model_transform_main for name in model.vision_input_names}
inputs:dict[str, np.ndarray] = {
model.desire_type: vec_desire,
model.desire_key: vec_desire,
'traffic_convention': traffic_convention,
**({'lateral_control_params': lateral_control_params} if model.use_lateral_control_params else {}),
}
# Include optional inputs only if the loaded model expects them
if 'lateral_control_params' in model.numpy_inputs:
inputs['lateral_control_params'] = lateral_control_params
mt1 = time.perf_counter()
model_output = model.run(bufs, transforms, inputs, prepare_only)
@@ -408,7 +521,7 @@ def main(demo=False):
drivingdata_send = messaging.new_message('drivingModelData')
posenet_send = messaging.new_message('cameraOdometry')
action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego, use_curvature_from_plan)
action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego, model.mlsim, model.is_v9, frogpilot_toggles)
prev_action = action
fill_model_msg(drivingdata_send, modelv2_send, model_output, action,
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
@@ -432,7 +545,7 @@ def main(demo=False):
pm.send('cameraOdometry', posenet_send)
last_vipc_frame_id = meta_main.frame_id
# Update FrogPilot variables
# Update FrogPilot parameters
if sm['frogpilotPlan'].togglesUpdated:
frogpilot_toggles = get_frogpilot_toggles()
@@ -444,4 +557,7 @@ if __name__ == "__main__":
args = parser.parse_args()
main(demo=args.demo)
except KeyboardInterrupt:
cloudlog.warning("got SIGINT")
cloudlog.warning(f"child {PROCESS_NAME} got SIGINT")
except Exception:
sentry.capture_exception()
raise
@@ -0,0 +1,766 @@
#include "frogpilot/ui/qt/offroad/expandable_multi_option_dialog.h"
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QScrollBar>
#include <QTimer>
#include <QHBoxLayout>
#include <QSpacerItem>
#include <QLayout>
#include <QLayoutItem>
#include <QGridLayout>
#include <QPoint>
#include <QSize>
#include <QSizePolicy>
#include <QSet>
#include <QVector>
#include <QSignalBlocker>
#include <QScroller>
#include <QPointer>
#include <QObject>
#include <algorithm>
#include "selfdrive/ui/qt/widgets/scrollview.h"
ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_text,
const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent,
const QStringList &userFavorites,
const QStringList &communityFavorites,
const QMap<QString, QString> &modelReleasedDates,
const QMap<QString, QString> &modelFileToNameMap,
const QString &initialSortMode)
: DialogBase(parent), seriesToModels(seriesToModels), currentSortMode(initialSortMode.isEmpty() ? QString("alphabetical") : initialSortMode),
userFavorites(userFavorites), communityFavorites(communityFavorites), modelReleasedDates(modelReleasedDates),
modelFileToNameMap(modelFileToNameMap), currentSelection(current) {
baseSeriesToModels = seriesToModels;
for (auto it = this->modelFileToNameMap.constBegin(); it != this->modelFileToNameMap.constEnd(); ++it) {
modelNameToFileMap.insert(it.value(), it.key());
}
for (auto it = seriesToModels.constBegin(); it != seriesToModels.constEnd(); ++it) {
const QStringList &models = it.value();
for (const QString &modelName : models) {
if (modelName.isEmpty() || modelNameToFileMap.contains(modelName)) {
continue;
}
this->modelFileToNameMap.insert(modelName, modelName);
modelNameToFileMap.insert(modelName, modelName);
}
}
currentSelectionKey = modelNameToFileMap.value(currentSelection);
if (!currentSelectionKey.isEmpty()) {
selectionKey = currentSelectionKey;
selection = this->modelFileToNameMap.value(currentSelectionKey, currentSelection);
currentSelection = selection;
} else {
selectionKey.clear();
selection.clear();
currentSelection.clear();
}
if (currentSortMode != "alphabetical" && currentSortMode != "date" &&
currentSortMode != "favorites" && currentSortMode != "date_oldest") {
currentSortMode = "alphabetical";
}
QFrame *container = new QFrame(this);
container->setStyleSheet(R"(
QFrame { background-color: #1B1B1B; }
QPushButton {
height: 135;
padding: 0px 50px;
text-align: left;
font-size: 55px;
font-weight: 300;
border-radius: 10px;
background-color: #4F4F4F;
border: 2px solid transparent;
}
QPushButton.model-option:checked {
background-color: #465BEA !important;
border: 3px solid #FFFFFF !important;
color: white !important;
font-weight: 500 !important;
}
QPushButton:hover { background-color: #5A5A5A; }
QPushButton.model-option:checked:hover { background-color: #5A6BEA; }
QPushButton:pressed {
background-color: #3049F4;
}
QPushButton.model-option:checked:pressed {
background-color: #3049F4;
border: 3px solid #CCCCCC;
}
QPushButton.series-header {
background-color: #333333;
font-weight: 500;
text-align: left;
padding-left: 80px;
}
QPushButton.series-header:hover { background-color: #404040; }
QPushButton.favorite-button {
background-color: transparent;
border: none;
font-size: 60px;
padding: 0px;
margin: 0px;
min-width: 80px;
max-width: 80px;
}
QPushButton.favorite-button:hover { background-color: #404040; }
QComboBox {
background-color: #4F4F4F;
border: 2px solid transparent;
border-radius: 10px;
padding: 10px;
font-size: 50px;
color: white;
min-width: 200px;
}
QComboBox:hover { background-color: #5A5A5A; }
QComboBox::drop-down {
border: none;
width: 50px;
}
QComboBox::down-arrow {
image: url("../../frogpilot/assets/toggle_icons/icon_dropdown.png");
width: 30px;
height: 30px;
}
QComboBox QAbstractItemView {
background-color: #4F4F4F;
border: 2px solid #FFFFFF;
border-radius: 10px;
color: white;
selection-background-color: #465BEA;
font-size: 50px;
}
)");
QVBoxLayout *main_layout = new QVBoxLayout(container);
main_layout->setContentsMargins(55, 50, 55, 50);
QLabel *title = new QLabel(prompt_text, this);
title->setStyleSheet("font-size: 70px; font-weight: 500;");
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
main_layout->addSpacing(25);
// Sort controls - simple cycling button
QHBoxLayout *sortLayout = new QHBoxLayout();
sortLayout->setContentsMargins(0, 0, 0, 0);
sortLayout->setSpacing(20);
sortLayout->addStretch(); // Push to the right
QLabel *sortLabel = new QLabel(tr("Sort by:"), this);
sortLabel->setStyleSheet("font-size: 50px; color: white;");
sortLayout->addWidget(sortLabel);
QPushButton *sortButton = new QPushButton(tr("Alphabetical"), this);
sortButton->setStyleSheet(R"(
QPushButton {
background-color: #4F4F4F;
border: 2px solid transparent;
border-radius: 10px;
padding: 10px 20px;
font-size: 50px;
color: white;
min-width: 250px;
text-align: center;
}
QPushButton:hover { background-color: #5A5A5A; }
)");
// Set initial button text based on sort mode
if (currentSortMode == "date") {
sortButton->setText(tr("Date (Newest)"));
} else if (currentSortMode == "date_oldest") {
sortButton->setText(tr("Date (Oldest)"));
} else if (currentSortMode == "favorites") {
sortButton->setText(tr("Favorites First"));
} else {
sortButton->setText(tr("Alphabetical"));
}
QWidget *sortWidget = new QWidget(container);
sortWidget->setLayout(sortLayout);
sortWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
sortLayout->setSizeConstraint(QLayout::SetFixedSize);
sortWidget->setStyleSheet("background: transparent;");
sortLayout->addWidget(sortButton);
auto updateSortOverlayGeometry = [sortWidget, sortLayout]() {
if (!sortWidget) return;
const QSize hint = sortLayout->sizeHint();
sortWidget->setFixedSize(hint);
};
updateSortOverlayGeometry();
QObject::connect(sortButton, &QPushButton::clicked, [this, sortButton, updateSortOverlayGeometry]() {
if (currentSortMode == "alphabetical") {
currentSortMode = "date";
sortButton->setText(tr("Date (Newest)"));
} else if (currentSortMode == "date") {
currentSortMode = "date_oldest";
sortButton->setText(tr("Date (Oldest)"));
} else if (currentSortMode == "date_oldest") {
currentSortMode = "favorites";
sortButton->setText(tr("Favorites First"));
} else {
currentSortMode = "alphabetical";
sortButton->setText(tr("Alphabetical"));
}
updateSortOverlayGeometry();
updateSorting();
});
listWidgetContainer = new QWidget(this);
listLayout = new QVBoxLayout(listWidgetContainer);
listLayout->setSpacing(10);
listLayout->setContentsMargins(0, 0, 0, 0);
confirmButton = new QPushButton(tr("Select"));
confirmButton->setObjectName("confirm_btn");
confirmButton->setEnabled(!selectionKey.isEmpty());
scrollView = new ScrollView(listWidgetContainer, this);
scrollView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
if (scrollView->viewport()) {
scrollView->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true);
}
QWidget *listContainer = new QWidget(container);
QGridLayout *overlayLayout = new QGridLayout(listContainer);
overlayLayout->setContentsMargins(0, 0, 0, 0);
overlayLayout->setSpacing(0);
overlayLayout->addWidget(scrollView, 0, 0);
overlayLayout->setRowStretch(0, 1);
overlayLayout->setColumnStretch(0, 1);
overlayLayout->addWidget(sortWidget, 0, 0, Qt::AlignRight | Qt::AlignTop);
// Create series headers and their expandable content
rebuildModelList(seriesToModels.keys(), seriesToModels);
main_layout->addWidget(listContainer);
main_layout->addSpacing(35);
// Cancel + confirm buttons
QHBoxLayout *blayout = new QHBoxLayout;
main_layout->addLayout(blayout);
blayout->setSpacing(50);
QPushButton *cancel_btn = new QPushButton(tr("Cancel"));
QObject::connect(cancel_btn, &QPushButton::clicked, this, &ConfirmationDialog::reject);
QObject::connect(confirmButton, &QPushButton::clicked, this, &ConfirmationDialog::accept);
blayout->addWidget(cancel_btn);
blayout->addWidget(confirmButton);
QVBoxLayout *outer_layout = new QVBoxLayout(this);
outer_layout->setContentsMargins(50, 50, 50, 50);
outer_layout->addWidget(container);
// Initial sorting
updateSorting();
}
void ExpandableMultiOptionDialog::toggleSeries(const QString &series, QPushButton *headerButton) {
if (!headerButton) return;
QWidget *container = seriesWidgets.value(series, nullptr);
if (!container) return;
bool expanded = seriesExpanded[series];
QString seriesName = series;
if (expanded) {
container->hide();
seriesExpanded[series] = false;
headerButton->setText("" + seriesName);
} else {
container->show();
seriesExpanded[series] = true;
headerButton->setText("" + seriesName);
// Auto-scroll to place the series at the top of the viewport when expanded
if (scrollView) {
QPointer<QPushButton> headerPtr(headerButton);
QPointer<ScrollView> scrollPtr(scrollView);
QTimer::singleShot(50, [headerPtr, scrollPtr]() {
if (!scrollPtr || !headerPtr) return;
QWidget *contents = scrollPtr->widget();
if (!contents) return;
if (QScrollBar *vScrollBar = scrollPtr->verticalScrollBar()) {
QPoint headerTop = headerPtr->mapTo(contents, QPoint(0, 0));
int targetValue = qMax(headerTop.y() - 20, 0);
vScrollBar->setValue(targetValue);
}
});
}
}
// Update the button's appearance
headerButton->update();
}
QString ExpandableMultiOptionDialog::getSelection(const QString &prompt_text,
const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent,
const QStringList &userFavorites,
const QStringList &communityFavorites,
const QMap<QString, QString> &modelReleasedDates,
const QMap<QString, QString> &modelFileToNameMap,
const QString &initialSortMode) {
ExpandableMultiOptionDialog d(prompt_text, seriesToModels, current, parent,
userFavorites, communityFavorites, modelReleasedDates, modelFileToNameMap, initialSortMode);
if (d.exec()) {
return d.selection;
}
return "";
}
QStringList ExpandableMultiOptionDialog::getUserFavorites() const {
QStringList filteredFavorites;
for (const QString &fav : userFavorites) {
if (modelFileToNameMap.contains(fav) && !filteredFavorites.contains(fav)) {
filteredFavorites.append(fav);
}
}
return filteredFavorites;
}
void ExpandableMultiOptionDialog::stopActiveScroll() {
if (!scrollView) {
return;
}
if (QScroller *scroller = QScroller::scroller(scrollView->viewport())) {
if (scroller->state() == QScroller::Scrolling) {
scroller->stop();
}
}
}
void ExpandableMultiOptionDialog::stopActiveScrollForInteraction() {
if (!scrollView) {
return;
}
if (QScroller *scroller = QScroller::scroller(scrollView->viewport())) {
const QScroller::State state = scroller->state();
if (state == QScroller::Scrolling || state == QScroller::Dragging || state == QScroller::Pressed) {
scroller->stop();
}
}
}
void ExpandableMultiOptionDialog::createModelButton(const QString &modelKey, const QString &modelName, const QString &displayName,
QVBoxLayout *layout) {
QString effectiveKey = modelKey.isEmpty() ? modelName : modelKey;
if (effectiveKey.isEmpty()) {
return;
}
if (!modelFileToNameMap.contains(effectiveKey)) {
const QString storedName = !modelName.isEmpty() ? modelName : displayName;
modelFileToNameMap.insert(effectiveKey, storedName);
}
if (!modelName.isEmpty()) {
modelNameToFileMap.insert(modelName, effectiveKey);
}
QWidget *modelWidget = new QWidget();
QHBoxLayout *modelLayout = new QHBoxLayout(modelWidget);
modelLayout->setContentsMargins(0, 0, 0, 0);
modelLayout->setSpacing(10);
// Star button
QPushButton *starButton = new QPushButton();
starButton->setProperty("class", "favorite-button");
starButton->setCheckable(true);
starButton->setCursor(Qt::PointingHandCursor);
starButton->setFocusPolicy(Qt::NoFocus);
// Check if this model is a favorite
bool isCommunityFav = communityFavorites.contains(effectiveKey);
bool isUserFav = userFavorites.contains(effectiveKey);
bool isFavorite = isCommunityFav || isUserFav;
starButton->setChecked(isFavorite);
starButton->setText(isFavorite ? QString::fromUtf16(u"\u2665") : QString::fromUtf16(u"\u2661"));
QObject::connect(starButton, &QPushButton::clicked, [this, effectiveKey]() {
stopActiveScrollForInteraction();
toggleFavorite(effectiveKey);
});
favoriteButtons[effectiveKey].append(starButton);
modelLayout->addWidget(starButton);
// Model button
QPushButton *modelButton = new QPushButton(displayName);
modelButton->setCheckable(true);
modelButton->setProperty("class", "model-option");
modelButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
modelButton->setCursor(Qt::PointingHandCursor);
modelButton->setFocusPolicy(Qt::NoFocus);
modelButton->setProperty("modelKey", effectiveKey);
modelButton->setProperty("modelName", modelName);
modelButtons[effectiveKey].append(modelButton);
if (selectionKey == effectiveKey && currentSelectionButton.isNull()) {
currentSelectionButton = modelButton;
}
modelLayout->addWidget(modelButton);
const QString resolvedSelection = modelFileToNameMap.value(effectiveKey, !modelName.isEmpty() ? modelName : displayName);
QObject::connect(modelButton, &QPushButton::clicked, this, [this, effectiveKey, modelButton, resolvedSelection]() {
stopActiveScrollForInteraction();
selectionKey = effectiveKey;
currentSelectionKey = effectiveKey;
selection = resolvedSelection;
currentSelection = resolvedSelection;
currentSelectionButton = modelButton;
if (confirmButton) {
confirmButton->setEnabled(true);
}
updateButtonStyles();
});
layout->addWidget(modelWidget);
}
void ExpandableMultiOptionDialog::toggleFavorite(const QString &modelKey) {
// Update local state
if (modelKey.isEmpty()) {
return;
}
if (userFavorites.contains(modelKey)) {
userFavorites.removeAll(modelKey);
} else {
userFavorites.append(modelKey);
}
updateSorting();
}
void ExpandableMultiOptionDialog::updateSorting() {
const QString favoritesSeriesName = QStringLiteral("♥ Favorites");
QMap<QString, QStringList> newSeriesToModels;
QStringList orderedSeries;
QSet<QString> validSeries;
QSet<QString> favoriteModelKeys;
QSet<QString> availableModelKeys;
displayOverrides.clear();
const bool sortByDate = (currentSortMode == "date" || currentSortMode == "date_oldest");
const bool sortDateNewestFirst = (currentSortMode == "date");
for (auto it = baseSeriesToModels.constBegin(); it != baseSeriesToModels.constEnd(); ++it) {
const QStringList &models = it.value();
for (const QString &modelName : models) {
const QString modelKey = modelNameToFileMap.value(modelName, modelName);
if (!modelKey.isEmpty()) {
availableModelKeys.insert(modelKey);
}
}
}
if (currentSortMode == "favorites") {
QStringList favoritesList;
for (const QString &modelKey : communityFavorites) {
if (availableModelKeys.contains(modelKey)) {
const QString modelName = modelFileToNameMap.value(modelKey);
favoritesList.append(modelName);
favoriteModelKeys.insert(modelKey);
displayOverrides.insert(modelKey, tr("%1 (Community Fav)").arg(modelName));
}
}
for (const QString &modelKey : userFavorites) {
if (availableModelKeys.contains(modelKey) && !favoriteModelKeys.contains(modelKey)) {
favoritesList.append(modelFileToNameMap.value(modelKey));
favoriteModelKeys.insert(modelKey);
}
}
if (!favoritesList.isEmpty()) {
std::sort(favoritesList.begin(), favoritesList.end());
newSeriesToModels.insert(favoritesSeriesName, favoritesList);
orderedSeries.append(favoritesSeriesName);
validSeries.insert(favoritesSeriesName);
seriesExpanded.insert(favoritesSeriesName, true);
} else {
seriesExpanded.remove(favoritesSeriesName);
}
} else {
seriesExpanded.remove(favoritesSeriesName);
}
struct SeriesInfo {
QString name;
QStringList models;
QString newestDate;
QString oldestDate;
};
QVector<SeriesInfo> seriesInfos;
for (auto it = baseSeriesToModels.constBegin(); it != baseSeriesToModels.constEnd(); ++it) {
QString series = it.key();
QStringList models = it.value();
if (sortByDate) {
std::sort(models.begin(), models.end(), [this, sortDateNewestFirst](const QString &a, const QString &b) {
QString keyA = modelNameToFileMap.value(a, a);
QString keyB = modelNameToFileMap.value(b, b);
QString dateA = modelReleasedDates.value(keyA, QStringLiteral("1970-01-01"));
QString dateB = modelReleasedDates.value(keyB, QStringLiteral("1970-01-01"));
if (dateA == dateB) {
return a < b;
}
return sortDateNewestFirst ? (dateA > dateB) : (dateA < dateB);
});
} else {
std::sort(models.begin(), models.end());
}
if (currentSortMode == "favorites" && !favoriteModelKeys.isEmpty()) {
QStringList filteredModels;
for (const QString &modelName : models) {
QString key = modelNameToFileMap.value(modelName, modelName);
if (!favoriteModelKeys.contains(key)) {
filteredModels.append(modelName);
}
}
models = filteredModels;
}
if (models.isEmpty()) {
continue;
}
QString newestDate = QStringLiteral("1970-01-01");
QString oldestDate = QStringLiteral("1970-01-01");
bool hasDate = false;
for (const QString &modelName : models) {
const QString key = modelNameToFileMap.value(modelName, modelName);
const QString date = modelReleasedDates.value(key, QStringLiteral("1970-01-01"));
if (!hasDate) {
newestDate = date;
oldestDate = date;
hasDate = true;
} else {
if (date > newestDate) {
newestDate = date;
}
if (date < oldestDate) {
oldestDate = date;
}
}
}
if (!hasDate) {
oldestDate = QStringLiteral("1970-01-01");
}
seriesInfos.push_back({series, models, newestDate, oldestDate});
newSeriesToModels.insert(series, models);
}
if (sortByDate) {
std::sort(seriesInfos.begin(), seriesInfos.end(), [sortDateNewestFirst](const SeriesInfo &a, const SeriesInfo &b) {
if (sortDateNewestFirst) {
if (a.newestDate == b.newestDate) {
return a.name < b.name;
}
return a.newestDate > b.newestDate;
} else {
if (a.oldestDate == b.oldestDate) {
return a.name < b.name;
}
return a.oldestDate < b.oldestDate;
}
});
} else {
std::sort(seriesInfos.begin(), seriesInfos.end(), [](const SeriesInfo &a, const SeriesInfo &b) {
return a.name < b.name;
});
}
for (const SeriesInfo &info : seriesInfos) {
orderedSeries.append(info.name);
validSeries.insert(info.name);
}
for (auto it = seriesExpanded.begin(); it != seriesExpanded.end(); ) {
if (!validSeries.contains(it.key())) {
it = seriesExpanded.erase(it);
} else {
++it;
}
}
rebuildModelList(orderedSeries, newSeriesToModels);
refreshFavoriteIcons();
}
void ExpandableMultiOptionDialog::rebuildModelList(const QStringList &orderedSeries, const QMap<QString, QStringList> &newSeriesToModels) {
if (!listLayout) return;
stopActiveScroll();
while (QLayoutItem *item = listLayout->takeAt(0)) {
if (QWidget *w = item->widget()) {
delete w;
} else if (QLayout *layout = item->layout()) {
delete layout;
}
delete item;
}
seriesWidgets.clear();
modelButtons.clear();
favoriteButtons.clear();
currentSelectionButton = nullptr;
for (const QString &series : orderedSeries) {
const QStringList models = newSeriesToModels.value(series);
if (models.isEmpty()) {
continue;
}
QPushButton *seriesHeader = new QPushButton("" + series);
seriesHeader->setProperty("class", "series-header");
seriesHeader->setCheckable(false);
bool expanded = seriesExpanded.value(series, false);
seriesExpanded.insert(series, expanded);
QObject::connect(seriesHeader, &QPushButton::clicked, [this, series, seriesHeader]() {
toggleSeries(series, seriesHeader);
});
QWidget *seriesContainer = new QWidget();
QVBoxLayout *seriesLayout = new QVBoxLayout(seriesContainer);
seriesLayout->setContentsMargins(20, 0, 0, 0);
seriesLayout->setSpacing(10);
for (const QString &modelName : models) {
QString modelKey = modelNameToFileMap.value(modelName, modelName);
if (!modelFileToNameMap.contains(modelKey)) {
modelFileToNameMap.insert(modelKey, modelName);
}
QString displayName = displayOverrides.value(modelKey, modelName);
createModelButton(modelKey, modelName, displayName, seriesLayout);
}
if (expanded) {
seriesContainer->show();
seriesHeader->setText("" + series);
} else {
seriesContainer->hide();
seriesHeader->setText("" + series);
}
seriesWidgets.insert(series, seriesContainer);
listLayout->addWidget(seriesHeader);
listLayout->addWidget(seriesContainer);
}
listLayout->addStretch(1);
seriesToModels = newSeriesToModels;
listWidgetContainer->updateGeometry();
listWidgetContainer->adjustSize();
if (scrollView && scrollView->widget()) {
scrollView->widget()->updateGeometry();
scrollView->widget()->adjustSize();
}
updateButtonStyles();
}
void ExpandableMultiOptionDialog::refreshFavoriteIcons() {
for (auto it = favoriteButtons.begin(); it != favoriteButtons.end(); ++it) {
const QString &modelKey = it.key();
const QList<QPushButton*> &buttons = it.value();
bool isCommunityFav = communityFavorites.contains(modelKey);
bool isUserFav = userFavorites.contains(modelKey);
bool isFavorite = isCommunityFav || isUserFav;
for (QPushButton *button : buttons) {
if (!button) continue;
button->setChecked(isFavorite);
button->setText(isFavorite ? QString::fromUtf16(u"\u2665") : QString::fromUtf16(u"\u2661"));
}
}
if (confirmButton && !selectionKey.isEmpty()) {
confirmButton->setEnabled(true);
}
updateButtonStyles();
}
void ExpandableMultiOptionDialog::updateButtonStyles() {
const QString selectedKey = selectionKey;
const QString selectedStyle = QStringLiteral(
"QPushButton {"
"background-color: #465BEA;"
"border: 3px solid #FFFFFF;"
"color: white;"
"font-weight: 500;"
"height: 135;"
"padding: 0px 50px;"
"text-align: left;"
"font-size: 55px;"
"border-radius: 10px;"
"}");
if (selectedKey.isEmpty()) {
currentSelectionButton = nullptr;
}
QPushButton *explicitButton = currentSelectionButton.data();
if (explicitButton && explicitButton->property("modelKey").toString() != selectedKey) {
explicitButton = nullptr;
}
for (auto it = modelButtons.begin(); it != modelButtons.end(); ++it) {
const QString &modelKey = it.key();
const QList<QPushButton*> &buttons = it.value();
const bool keyMatches = (!selectedKey.isEmpty() && modelKey == selectedKey);
bool activatedForKey = false;
for (QPushButton *button : buttons) {
if (!button) continue;
bool isActive = false;
if (explicitButton) {
isActive = (button == explicitButton);
} else if (keyMatches && !activatedForKey) {
isActive = true;
activatedForKey = true;
currentSelectionButton = button;
}
QSignalBlocker blocker(button);
button->setChecked(isActive);
button->setStyleSheet(isActive ? selectedStyle : QString());
}
}
}
@@ -0,0 +1,76 @@
#pragma once
#include <QDialog>
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
#include <QMap>
#include <QList>
#include <QPointer>
#include <QComboBox>
#include <QMenu>
#include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
class QPushButton;
class ExpandableMultiOptionDialog : public DialogBase {
Q_OBJECT
public:
explicit ExpandableMultiOptionDialog(const QString &prompt_text, const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent,
const QStringList &userFavorites = QStringList(),
const QStringList &communityFavorites = QStringList(),
const QMap<QString, QString> &modelReleasedDates = QMap<QString, QString>(),
const QMap<QString, QString> &modelFileToNameMap = QMap<QString, QString>(),
const QString &initialSortMode = "alphabetical");
static QString getSelection(const QString &prompt_text, const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent,
const QStringList &userFavorites = QStringList(),
const QStringList &communityFavorites = QStringList(),
const QMap<QString, QString> &modelReleasedDates = QMap<QString, QString>(),
const QMap<QString, QString> &modelFileToNameMap = QMap<QString, QString>(),
const QString &initialSortMode = QString());
QString selection;
QString getCurrentSortMode() const { return currentSortMode; }
QStringList getUserFavorites() const;
private:
void toggleSeries(const QString &series, QPushButton *headerButton);
void toggleFavorite(const QString &modelKey);
void updateSorting();
void rebuildModelList(const QStringList &orderedSeries, const QMap<QString, QStringList> &newSeriesToModels);
void createModelButton(const QString &modelKey, const QString &modelName, const QString &displayName,
QVBoxLayout *layout);
void refreshFavoriteIcons();
void updateButtonStyles();
void stopActiveScroll();
void stopActiveScrollForInteraction();
QMap<QString, QStringList> seriesToModels;
QMap<QString, QStringList> baseSeriesToModels;
QMap<QString, QWidget*> seriesWidgets;
QMap<QString, bool> seriesExpanded;
QMap<QString, QList<QPushButton*>> modelButtons;
QMap<QString, QList<QPushButton*>> favoriteButtons;
QStringList userFavorites;
QStringList communityFavorites;
QMap<QString, QString> modelReleasedDates;
QMap<QString, QString> modelFileToNameMap;
QMap<QString, QString> modelNameToFileMap;
QMap<QString, QString> displayOverrides;
QString currentSortMode;
QString currentSelection;
QString currentSelectionKey;
QString selectionKey;
ScrollView *scrollView = nullptr;
QVBoxLayout *listLayout = nullptr;
QPushButton *confirmButton = nullptr;
QWidget *listWidgetContainer = nullptr;
QPointer<QPushButton> currentSelectionButton;
};
+29 -6
View File
@@ -269,28 +269,38 @@ void FrogPilotSettingsWindow::updateVariables() {
hasPedal = CP.getEnableGasInterceptor();
hasRadar = !CP.getRadarUnavailable();
hasSDSU = frogpilot_toggles.value("has_sdsu").toBool();
hasSASCM = frogpilot_toggles.value("has_sascm").toBool();
hasSNG = hasOpenpilotLongitudinal && CP.getAutoResumeSng();
hasZSS = frogpilot_toggles.value("has_zss").toBool();
isAngleCar = CP.getSteerControlType() == cereal::CarParams::SteerControlType::ANGLE;
isBolt = carFingerprint == "CHEVROLET_BOLT_CC" || carFingerprint == "CHEVROLET_BOLT_EUV";
isBolt = carFingerprint == "CHEVROLET_BOLT_ACC_2022_2023" ||
carFingerprint == "CHEVROLET_BOLT_CC_2022_2023" ||
carFingerprint == "CHEVROLET_BOLT_CC_2019_2021" ||
carFingerprint == "CHEVROLET_BOLT_CC_2017";
isGM = carMake == "gm";
isHKG = carMake == "hyundai";
isHKGCanFd = isHKG && safetyModel == cereal::CarParams::SafetyModel::HYUNDAI_CANFD;
isSubaru = carMake == "subaru";
isTorqueCar = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::TORQUE;
isToyota = carMake == "toyota";
isTSK = CP.getSecOcRequired();
isVolt = carFingerprint == "CHEVROLET_VOLT";
longitudinalActuatorDelay = CP.getLongitudinalActuatorDelay();
startAccel = CP.getStartAccel();
steerActuatorDelay = CP.getSteerActuatorDelay();
steerOffset = 0.0f;
steerKp = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::PID ? CP.getLateralTuning().getPid().getKpV()[0] : 0.6;
steerRatio = CP.getSteerRatio();
stopAccel = CP.getStopAccel();
stoppingDecelRate = CP.getStoppingDecelRate();
vEgoStarting = CP.getVEgoStarting();
vEgoStopping = CP.getVEgoStopping();
friction = CP.getLateralTuning().getTorque().getFriction();
latAccelFactor = CP.getLateralTuning().getTorque().getLatAccelFactor();
float currentDelayStock = params.getFloat("SteerDelayStock");
float currentFrictionStock = params.getFloat("SteerFrictionStock");
float currentSteerOffsetStock = params.getFloat("SteerOffsetStock");
float currentKPStock = params.getFloat("SteerKPStock");
float currentLatAccelStock = params.getFloat("SteerLatAccelStock");
float currentLongDelayStock = params.getFloat("LongitudinalActuatorDelayStock");
@@ -315,6 +325,13 @@ void FrogPilotSettingsWindow::updateVariables() {
params.putFloat("SteerFrictionStock", friction);
}
if (currentSteerOffsetStock != steerOffset) {
if (params.getFloat("SteerOffset") == currentSteerOffsetStock) {
params.putFloat("SteerOffset", steerOffset);
}
params.putFloat("SteerOffsetStock", steerOffset);
}
if (currentKPStock != steerKp && steerKp != 0) {
if (params.getFloat("SteerKP") == currentKPStock || currentKPStock == 0) {
params.putFloat("SteerKP", steerKp);
@@ -387,12 +404,18 @@ void FrogPilotSettingsWindow::updateVariables() {
canUsePedal = FPCP.getCanUsePedal();
canUseSDSU = FPCP.getCanUseSDSU();
friction = FPCP.getLateralTuning().getTorque().getFriction();
hasAutoTune = (carMake == "hyundai" || carMake == "toyota") && FPCP.getLateralTuning().which() == cereal::FrogPilotCarParams::LateralTuning::TORQUE;
isTorqueCar = FPCP.getLateralTuning().which() == cereal::FrogPilotCarParams::LateralTuning::TORQUE;
latAccelFactor = FPCP.getLateralTuning().getTorque().getLatAccelFactor();
canUseSASCM = FPCP.getCanUseSASCM();
openpilotLongitudinalControlDisabled = FPCP.getOpenpilotLongitudinalControlDisabled();
steerKp = FPCP.getLateralTuning().getTorque().getKp();
}
std::string liveTorqueParameters = params.get("LiveTorqueParameters");
if (!liveTorqueParameters.empty()) {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader reader(aligned_buf.align(liveTorqueParameters.data(), liveTorqueParameters.size()));
cereal::Event::Reader event = reader.getRoot<cereal::Event>();
cereal::LiveTorqueParametersData::Reader LTP = event.getLiveTorqueParameters();
hasAutoTune = LTP.getUseParams();
}
isC3 = util::read_file("/sys/firmware/devicetree/base/model").find("tici") != std::string::npos;
@@ -13,6 +13,7 @@ public:
bool canUsePedal = false;
bool canUseSDSU = false;
bool canUseSASCM = false;
bool forceOpenDescriptions = false;
bool hasAutoTune = true;
bool hasBSM = true;
@@ -24,6 +25,7 @@ public:
bool hasPedal = false;
bool hasRadar = true;
bool hasSDSU = false;
bool hasSASCM = false;
bool hasSNG = false;
bool hasZSS = false;
bool isAngleCar = false;
@@ -45,6 +47,7 @@ public:
float longitudinalActuatorDelay;
float startAccel;
float steerActuatorDelay;
float steerOffset;
float steerKp;
float steerRatio;
float stopAccel;
+73 -74
View File
@@ -39,11 +39,12 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
const std::vector<std::tuple<QString, QString, QString, QString>> lateralToggles {
{"AdvancedLateralTune", tr("Advanced Lateral Tuning"), tr("<b>Advanced steering control changes to fine-tune how openpilot drives.</b>"), "../../frogpilot/assets/toggle_icons/icon_advanced_lateral_tune.png"},
{"SteerDelay", steerActuatorDelay != 0 ? QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(steerActuatorDelay, 'f', 2)) : tr("Actuator Delay"), tr("<b>The time between openpilot's steering command and the vehicle's response.</b> Increase if the vehicle reacts late; decrease if it feels jumpy. Auto-learned by default."), ""},
{"SteerFriction", friction != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(friction, 'f', 2)) : tr("Friction"), tr("<b>Compensates for steering friction.</b> Increase if the wheel sticks near center; decrease if it jitters. Auto-learned by default."), ""},
{"SteerKP", steerKp != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKp, 'f', 2)) : tr("Kp Factor"), tr("<b>How strongly openpilot corrects lane position.</b> Higher is tighter but twitchier; lower is smoother but slower. Auto-learned by default."), ""},
{"SteerLatAccel", latAccelFactor != 0 ? QString(tr("Lateral Acceleration (Default: %1)")).arg(QString::number(latAccelFactor, 'f', 2)) : tr("Lateral Acceleration"), tr("<b>Maps steering torque to turning response.</b> Increase for sharper turns; decrease for gentler steering. Auto-learned by default."), ""},
{"SteerRatio", steerRatio != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatio, 'f', 2)) : tr("Steer Ratio"), tr("<b>The relationship between steering wheel rotation and road wheel angle.</b> Increase if steering feels too quick or twitchy; decrease if it feels too slow or weak. Auto-learned by default."), ""},
{"SteerDelay", parent->steerActuatorDelay != 0 ? QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(parent->steerActuatorDelay, 'f', 2)) : tr("Actuator Delay"), tr("<b>The time between openpilot's steering command and the vehicle's response.</b> Increase if the vehicle reacts late; decrease if it feels jumpy. Auto-learned by default."), ""},
{"SteerFriction", parent->friction != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(parent->friction, 'f', 2)) : tr("Friction"), tr("<b>Compensates for steering friction.</b> Increase if the wheel sticks near center; decrease if it jitters. Auto-learned by default."), ""},
{"SteerOffset", parent->steerOffset != 0 ? QString(tr("Steer Offset (Default: %1)")).arg(QString::number(parent->steerOffset, 'f', 3)) : tr("Steer Offset"), tr("<b>Offsets steering torque to help compensate for alignment or tire issues.</b> More negative pulls the car right; more positive pulls it left. Most users should not need to touch this."), ""},
{"SteerKP", parent->steerKp != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2)) : tr("Kp Factor"), tr("<b>How strongly openpilot corrects lane position.</b> Higher is tighter but twitchier; lower is smoother but slower. Auto-learned by default."), ""},
{"SteerLatAccel", parent->latAccelFactor != 0 ? QString(tr("Lateral Acceleration (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2)) : tr("Lateral Acceleration"), tr("<b>Maps steering torque to turning response.</b> Increase for sharper turns; decrease for gentler steering. Auto-learned by default."), ""},
{"SteerRatio", parent->steerRatio != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(parent->steerRatio, 'f', 2)) : tr("Steer Ratio"), tr("<b>The relationship between steering wheel rotation and road wheel angle.</b> Increase if steering feels too quick or twitchy; decrease if it feels too slow or weak. Auto-learned by default."), ""},
{"ForceAutoTune", tr("Force Auto-Tune On"), tr("<b>Force-enable openpilot's live auto-tuning for \"Friction\" and \"Lateral Acceleration\".</b>"), ""},
{"ForceAutoTuneOff", tr("Force Auto-Tune Off"), tr("<b>Force-disable openpilot's live auto-tuning for \"Friction\" and \"Lateral Acceleration\" and use the set value instead.</b>"), ""},
{"ForceTorqueController", tr("Force Torque Controller"), tr("<b>Use torque-based steering control instead of angle-based control for smoother lane keeping, especially in curves.</b>"), ""},
@@ -87,15 +88,18 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
} else if (param == "SteerFriction") {
std::vector<QString> steerFrictionButton{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 0.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerFrictionButton, false, false);
} else if (param == "SteerOffset") {
std::vector<QString> steerOffsetButton{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, -0.2, 0.2, QString(), std::map<float, QString>(), 0.005, false, {}, steerOffsetButton, false, false);
} else if (param == "SteerKP") {
std::vector<QString> steerKPButton{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerKp * 0.5, steerKp * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerKPButton, false, false);
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->steerKp * 0.5, parent->steerKp * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerKPButton, false, false);
} else if (param == "SteerLatAccel") {
std::vector<QString> steerLatAccelButton{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, latAccelFactor * 0.75, latAccelFactor * 1.25, QString(), std::map<float, QString>(), 0.01, false, {}, steerLatAccelButton, false, false);
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->latAccelFactor * 0.75, parent->latAccelFactor * 1.25, QString(), std::map<float, QString>(), 0.01, false, {}, steerLatAccelButton, false, false);
} else if (param == "SteerRatio") {
std::vector<QString> steerRatioButton{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerRatio * 0.5, steerRatio * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerRatioButton, false, false);
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->steerRatio * 0.5, parent->steerRatio * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerRatioButton, false, false);
} else if (param == "AlwaysOnLateral") {
FrogPilotManageControl *aolToggle = new FrogPilotManageControl(param, title, desc, icon);
@@ -191,12 +195,6 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
Hardware::reboot();
}
} else if (key == "NNFF" || key == "NNFFLite") {
if (!isTorqueCar) {
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
Hardware::reboot();
}
}
} else if (key != "AlwaysOnLateral") {
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
Hardware::reboot();
@@ -207,41 +205,49 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
}
steerDelayToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerDelay"]);
QObject::connect(steerDelayToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
QObject::connect(steerDelayToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Actuator Delay</b> to its default value?"), this)) {
params.putFloat("SteerDelay", steerActuatorDelay);
params.putFloat("SteerDelay", parent->steerActuatorDelay);
steerDelayToggle->refresh();
}
});
steerFrictionToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerFriction"]);
QObject::connect(steerFrictionToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
QObject::connect(steerFrictionToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Friction</b> to its default value?"), this)) {
params.putFloat("SteerFriction", friction);
params.putFloat("SteerFriction", parent->friction);
steerFrictionToggle->refresh();
}
});
steerOffsetToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerOffset"]);
QObject::connect(steerOffsetToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Steer Offset</b> to its default value?"), this)) {
params.putFloat("SteerOffset", parent->steerOffset);
steerOffsetToggle->refresh();
}
});
steerKPToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerKP"]);
QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Kp Factor</b> to its default value?"), this)) {
params.putFloat("SteerKP", steerKp);
params.putFloat("SteerKP", parent->steerKp);
steerKPToggle->refresh();
}
});
steerLatAccelToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerLatAccel"]);
QObject::connect(steerLatAccelToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
QObject::connect(steerLatAccelToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Lateral Accel</b> to its default value?"), this)) {
params.putFloat("SteerLatAccel", latAccelFactor);
params.putFloat("SteerLatAccel", parent->latAccelFactor);
steerLatAccelToggle->refresh();
}
});
steerRatioToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerRatio"]);
QObject::connect(steerRatioToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
QObject::connect(steerRatioToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Steer Ratio</b> to its default value?"), this)) {
params.putFloat("SteerRatio", steerRatio);
params.putFloat("SteerRatio", parent->steerRatio);
steerRatioToggle->refresh();
}
});
@@ -258,27 +264,16 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
void FrogPilotLateralPanel::showEvent(QShowEvent *event) {
frogpilotToggleLevels = parent->frogpilotToggleLevels;
friction = parent->friction;
hasAutoTune = parent->hasAutoTune;
hasNNFFLog = parent->hasNNFFLog;
hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal;
isAngleCar = parent->isAngleCar;
isHKGCanFd = parent->isHKGCanFd;
isTorqueCar = parent->isTorqueCar;
latAccelFactor = parent->latAccelFactor;
steerActuatorDelay = parent->steerActuatorDelay;
steerKp = parent->steerKp;
steerRatio = parent->steerRatio;
tuningLevel = parent->tuningLevel;
steerDelayToggle->setTitle(QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(steerActuatorDelay, 'f', 2)));
steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(friction, 'f', 2)));
steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKp, 'f', 2)));
steerKPToggle->updateControl(steerKp * 0.5, steerKp * 1.5);
steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(latAccelFactor, 'f', 2)));
steerLatAccelToggle->updateControl(latAccelFactor * 0.75, latAccelFactor * 1.25);
steerRatioToggle->setTitle(QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatio, 'f', 2)));
steerRatioToggle->updateControl(steerRatio * 0.5, steerRatio * 1.5);
steerDelayToggle->setTitle(QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(parent->steerActuatorDelay, 'f', 2)));
steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(parent->friction, 'f', 2)));
steerOffsetToggle->setTitle(QString(tr("Steer Offset (Default: %1)")).arg(QString::number(parent->steerOffset, 'f', 3)));
steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2)));
steerKPToggle->updateControl(parent->steerKp * 0.5, parent->steerKp * 1.5);
steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2)));
steerLatAccelToggle->updateControl(parent->latAccelFactor * 0.75, parent->latAccelFactor * 1.25);
steerRatioToggle->setTitle(QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(parent->steerRatio, 'f', 2)));
steerRatioToggle->updateControl(parent->steerRatio * 0.5, parent->steerRatio * 1.5);
updateToggles();
}
@@ -358,41 +353,40 @@ void FrogPilotLateralPanel::updateToggles() {
}
}
bool forcingAutoTune = !hasAutoTune && params.getBool("ForceAutoTune");
bool forcingAutoTuneOff = hasAutoTune && params.getBool("ForceAutoTuneOff");
bool forcingTorqueController = !isAngleCar && params.getBool("ForceTorqueController");
bool usingNNFF = hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
bool forcingAutoTuneOff = parent->hasAutoTune && params.getBool("ForceAutoTuneOff");
bool forcingTorqueController = !parent->isAngleCar && params.getBool("ForceTorqueController");
bool usingNNFF = parent->hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
for (auto &[key, toggle] : toggles) {
if (parentKeys.contains(key)) {
continue;
}
bool setVisible = tuningLevel >= frogpilotToggleLevels[key].toDouble();
bool setVisible = parent->tuningLevel >= frogpilotToggleLevels[key].toDouble();
if (key == "AlwaysOnLateralLKAS") {
setVisible &= isHKGCanFd;
setVisible &= !hasOpenpilotLongitudinal;
setVisible &= parent->isHKGCanFd;
setVisible &= !parent->hasOpenpilotLongitudinal;
}
else if (key == "AlwaysOnLateralMain") {
setVisible &= !isHKGCanFd;
setVisible |= hasOpenpilotLongitudinal;
setVisible &= !parent->isHKGCanFd;
setVisible |= parent->hasOpenpilotLongitudinal;
}
else if (key == "ForceAutoTune") {
setVisible &= !hasAutoTune;
setVisible &= !isAngleCar;
setVisible &= isTorqueCar || forcingTorqueController;
setVisible &= !parent->hasAutoTune;
setVisible &= !parent->isAngleCar;
setVisible &= parent->isTorqueCar || forcingTorqueController;
}
else if (key == "ForceAutoTuneOff") {
setVisible &= hasAutoTune;
setVisible &= parent->hasAutoTune;
}
else if (key == "ForceTorqueController") {
setVisible &= !isAngleCar;
setVisible &= !isTorqueCar;
setVisible &= !parent->isAngleCar;
setVisible &= !parent->isTorqueCar;
}
else if (key == "LaneChangeTime") {
@@ -404,42 +398,47 @@ void FrogPilotLateralPanel::updateToggles() {
}
else if (key == "NNFF") {
setVisible &= hasNNFFLog;
setVisible &= !isAngleCar;
setVisible &= parent->hasNNFFLog;
setVisible &= !parent->isAngleCar;
}
else if (key == "NNFFLite") {
setVisible &= !usingNNFF;
setVisible &= !isAngleCar;
setVisible &= !parent->isAngleCar;
}
else if (key == "SteerDelay") {
setVisible &= steerActuatorDelay != 0;
setVisible &= parent->steerActuatorDelay != 0;
}
else if (key == "SteerFriction") {
setVisible &= friction != 0;
setVisible &= hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
setVisible &= isTorqueCar || forcingTorqueController;
setVisible &= parent->friction != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= !usingNNFF;
}
else if (key == "SteerOffset") {
setVisible &= parent->isGM;
setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= !usingNNFF;
}
else if (key == "SteerKP") {
setVisible &= steerKp != 0;
setVisible &= hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
setVisible &= isTorqueCar || forcingTorqueController;
setVisible &= parent->steerKp != 0;
setVisible &= !parent->isAngleCar;
}
else if (key == "SteerLatAccel") {
setVisible &= latAccelFactor != 0;
setVisible &= hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
setVisible &= isTorqueCar || forcingTorqueController;
setVisible &= parent->latAccelFactor != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= !usingNNFF;
}
else if (key == "SteerRatio") {
setVisible &= steerRatio != 0;
setVisible &= hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
setVisible &= parent->steerRatio != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
}
toggle->setVisible(setVisible);
+3 -1
View File
@@ -31,6 +31,7 @@ private:
float friction;
float latAccelFactor;
float steerActuatorDelay;
float steerOffset;
float steerKp;
float steerRatio;
@@ -38,7 +39,7 @@ private:
std::map<QString, AbstractControl*> toggles;
QSet<QString> advancedLateralTuneKeys = {"ForceAutoTune", "ForceAutoTuneOff", "ForceTorqueController", "SteerDelay", "SteerFriction", "SteerLatAccel", "SteerKP", "SteerRatio"};
QSet<QString> advancedLateralTuneKeys = {"ForceAutoTune", "ForceAutoTuneOff", "ForceTorqueController", "SteerDelay", "SteerFriction", "SteerOffset", "SteerLatAccel", "SteerKP", "SteerRatio"};
QSet<QString> aolKeys = {"AlwaysOnLateralLKAS", "AlwaysOnLateralMain", "PauseAOLOnBrake"};
QSet<QString> laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"};
QSet<QString> lateralTuneKeys = {"NNFF", "NNFFLite", "TurnDesires"};
@@ -48,6 +49,7 @@ private:
FrogPilotParamValueButtonControl *steerDelayToggle;
FrogPilotParamValueButtonControl *steerFrictionToggle;
FrogPilotParamValueButtonControl *steerOffsetToggle;
FrogPilotParamValueButtonControl *steerLatAccelToggle;
FrogPilotParamValueButtonControl *steerKPToggle;
FrogPilotParamValueButtonControl *steerRatioToggle;
File diff suppressed because it is too large Load Diff
@@ -40,19 +40,19 @@ private:
std::map<QString, AbstractControl*> toggles;
QSet<QString> advancedLongitudinalTuneKeys = {"LongitudinalActuatorDelay", "StartAccel", "StopAccel", "StoppingDecelRate", "VEgoStarting", "VEgoStopping"};
QSet<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"};
QSet<QString> advancedLongitudinalTuneKeys = {"EVTuning", "TruckTuning", "LongitudinalActuatorDelay", "StartAccel", "StopAccel", "StoppingDecelRate", "VEgoStarting", "VEgoStopping"};
QSet<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveFollowHigh", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"};
QSet<QString> conditionalExperimentalKeys = {"CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CENavigation", "CESignalSpeed", "ShowCEMStatus"};
QSet<QString> curveSpeedKeys = {"CalibratedLateralAcceleration", "CalibrationProgress", "ResetCurveData", "ShowCSCStatus"};
QSet<QString> customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"};
QSet<QString> longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TacoTune"};
QSet<QString> longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TrailerLoad", "TacoTune"};
QSet<QString> qolKeys = {"CustomCruise", "CustomCruiseLong", "ForceStops", "IncreasedStoppedDistance", "MapGears", "ReverseCruise", "SetSpeedOffset"};
QSet<QString> relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"};
QSet<QString> relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedFollowHigh", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"};
QSet<QString> speedLimitControllerKeys = {"SLCOffsets", "SLCFallback", "SLCOverride", "SLCPriority", "SLCQOL", "SLCVisuals"};
QSet<QString> speedLimitControllerOffsetsKeys = {"Offset1", "Offset2", "Offset3", "Offset4", "Offset5", "Offset6", "Offset7"};
QSet<QString> speedLimitControllerQOLKeys = {"ForceMPHDashboard", "SetSpeedLimit", "SLCConfirmation", "SLCLookaheadHigher", "SLCLookaheadLower", "SLCMapboxFiller"};
QSet<QString> speedLimitControllerVisualKeys = {"ShowSLCOffset", "SpeedLimitSources"};
QSet<QString> standardPersonalityKeys = {"StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"};
QSet<QString> standardPersonalityKeys = {"StandardFollow", "StandardFollowHigh", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"};
QSet<QString> trafficPersonalityKeys = {"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", "ResetTrafficPersonality"};
QSet<QString> parentKeys;
+466 -264
View File
@@ -1,39 +1,15 @@
#include "frogpilot/ui/qt/offroad/model_settings.h"
bool hasAllTinygradFiles(const QDir &modelDir, const QString &modelKey) {
QStringList tinygradSuffixes = {
"_driving_policy_metadata.pkl",
"_driving_policy_tinygrad.pkl",
"_driving_vision_metadata.pkl",
"_driving_vision_tinygrad.pkl"
};
for (const QString &suffix : tinygradSuffixes) {
if (!modelDir.exists(modelKey + suffix)) {
return false;
}
}
return true;
}
QString normalizeModelKey(QString key) {
key = key.toLower();
if (key.endsWith("_default")) {
key.chop(QString("_default").size());
}
return key;
}
#include "frogpilot/ui/qt/offroad/expandable_multi_option_dialog.h"
#include <QFile>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDoubleSpinBox>
#include <QPushButton>
#include <QDialog>
#include <algorithm>
FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
QJsonObject shownDescriptions = QJsonDocument::fromJson(QString::fromStdString(params.get("ShownToggleDescriptions")).toUtf8()).object();
QString className = this->metaObject()->className();
if (!shownDescriptions.value(className).toBool(false)) {
forceOpenDescriptions = true;
shownDescriptions.insert(className, true);
params.put("ShownToggleDescriptions", QJsonDocument(shownDescriptions).toJson(QJsonDocument::Compact).toStdString());
}
QStackedLayout *modelLayout = new QStackedLayout();
addItem(modelLayout);
@@ -50,58 +26,92 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
modelLayout->addWidget(modelLabelsPanel);
const std::vector<std::tuple<QString, QString, QString, QString>> modelToggles {
{"AutomaticallyDownloadModels", tr("Automatically Download New Models"), tr("<b>Automatically download new driving models</b> as they become available."), ""},
{"DeleteModel", tr("Delete Driving Models"), tr("<b>Delete downloaded driving models</b> to free up storage space."), ""},
{"DownloadModel", tr("Download Driving Models"), tr("<b>Manually download driving models</b> to the device."), ""},
{"ModelRandomizer", tr("Model Randomizer"), tr("<b>Select a random driving model each drive</b> and use feedback prompts at the end of the drive to help find the model that best suits you!"), ""},
{"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("<b>Add or remove driving models from the \"Model Randomizer\" blacklist.</b>"), ""},
{"ManageScores", tr("Manage Model Ratings"), tr("<b>View or reset saved model ratings</b> used by the \"Model Randomizer\"."), ""},
{"SelectModel", tr("Select Driving Model"), tr("<b>Choose which driving model openpilot uses.</b>"), ""},
{"UpdateTinygrad", tr("Update Model Manager"), tr("<b>Update the \"Model Manager\"</b> to support the latest models."), ""}
{"AutomaticallyDownloadModels", tr("Automatically Download New Models"), tr("Automatically download new driving models as they become available."), ""},
{"DeleteModel", tr("Delete Driving Models"), tr("Delete driving models from the device."), ""},
{"DownloadModel", tr("Download Driving Models"), tr("Download driving models to the device."), ""},
{"ModelRandomizer", tr("Model Randomizer"), tr("Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs."), ""},
{"RecoveryPower", tr("Recovery Power"), tr("Adjust the strength of planplus lane recovery corrections (0.5 to 2.0)."), ""},
{"StopDistance", tr("Stop Distance"), tr("Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6."), ""},
{"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("Add or remove models from the <b>Model Randomizer</b>'s blacklist list."), ""},
{"ManageScores", tr("Manage Model Ratings"), tr("Reset or view the saved ratings for the driving models."), ""},
{"SelectModel", tr("Select Driving Model"), tr("Select the active driving model."), ""},
};
FrogPilotParamValueButtonControl *recoveryPowerToggle = nullptr;
FrogPilotParamValueButtonControl *stopDistanceToggle = nullptr;
for (const auto &[param, title, desc, icon] : modelToggles) {
AbstractControl *modelToggle;
if (param == "DeleteModel") {
deleteModelButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DELETE"), tr("DELETE ALL")});
QObject::connect(deleteModelButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
QStringList deletableModels;
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
if (base.startsWith(modelKey)) {
QString modelName = modelFileToNameMapProcessed.value(modelKey);
if (!deletableModels.contains(modelName)) {
deletableModels.append(modelName);
}
}
}
QMap<QString, QString> deletableModelsMap = getDeletableModelDisplayNames();
noModelsDownloaded = deletableModelsMap.isEmpty();
if (noModelsDownloaded) {
return;
}
deletableModels.removeAll(processModelName(currentModel));
deletableModels.removeAll(modelFileToNameMapProcessed.value(normalizeModelKey(QString::fromStdString(params_default.get("Model")))));
noModelsDownloaded = deletableModels.isEmpty();
if (id == 0) {
QString modelToDelete = MultiOptionDialog::getSelection(tr("Select a driving model to delete"), deletableModels, "", this);
if (!modelToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the \"%1\" model?").arg(modelToDelete), tr("Delete"), this)) {
QString modelFile = modelFileToNameMapProcessed.key(modelToDelete);
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
if (base.startsWith(modelFile)) {
QFile::remove(modelDir.filePath(file));
// Group deletable models by series and keep a lookup for selected names
QMap<QString, QStringList> deletableSeriesToModels;
QMap<QString, QString> displayNameToKey;
QMap<QString, QString> deletableFileToNameMap;
for (auto it = deletableModelsMap.constBegin(); it != deletableModelsMap.constEnd(); ++it) {
const QString &modelKey = it.key();
const QString &displayName = it.value();
QString series = modelSeriesMap.value(modelKey, tr("Custom Series"));
deletableSeriesToModels[series].append(displayName);
displayNameToKey.insert(displayName, modelKey);
deletableFileToNameMap.insert(modelKey, displayName);
}
// Sort models within each series
for (QString &series : deletableSeriesToModels.keys()) {
QStringList &models = deletableSeriesToModels[series];
models.removeDuplicates();
std::sort(models.begin(), models.end());
}
QString savedSortMode = QString::fromStdString(params.get("ModelSortMode"));
if (savedSortMode.isEmpty()) savedSortMode = "alphabetical";
QString modelToDelete = ExpandableMultiOptionDialog::getSelection(tr("Select a driving model to delete"), deletableSeriesToModels, "", this,
QStringList(), QStringList(), QMap<QString, QString>(),
deletableFileToNameMap, savedSortMode);
if (!modelToDelete.isEmpty()) {
QString modelKey = displayNameToKey.value(modelToDelete);
if (modelKey.isEmpty()) {
QString processedName = processModelName(modelToDelete);
for (auto it = deletableModelsMap.constBegin(); it != deletableModelsMap.constEnd(); ++it) {
if (processModelName(it.value()) == processedName) {
modelKey = it.key();
break;
}
}
}
allModelsDownloaded = false;
if (!modelKey.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the \"%1\" model?").arg(modelToDelete), tr("Delete"), this)) {
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
if (base.startsWith(modelKey)) {
QFile::remove(modelDir.filePath(file));
}
}
allModelsDownloaded = false;
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
}
}
} else if (id == 1) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete all of your downloaded driving models?"), tr("Delete"), this)) {
const QList<QString> deletableKeys = deletableModelsMap.keys();
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
QString modelName = modelFileToNameMapProcessed.value(modelKey);
if (deletableModels.contains(modelName) && base.startsWith(modelKey)) {
for (const QString &modelKey : deletableKeys) {
if (base.startsWith(modelKey)) {
QFile::remove(modelDir.filePath(file));
break;
}
@@ -110,6 +120,7 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
allModelsDownloaded = false;
noModelsDownloaded = true;
deleteModelButton->setEnabled(false);
}
}
});
@@ -117,45 +128,96 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
} else if (param == "DownloadModel") {
downloadModelButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DOWNLOAD"), tr("DOWNLOAD ALL")});
QObject::connect(downloadModelButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
if (tinygradUpdate) {
if (FrogPilotConfirmationDialog::yesorno(tr("Tinygrad is out of date and must be updated before you can download new models. Update now?"), this)) {
if (FrogPilotConfirmationDialog::yesorno(tr("Updating Tinygrad will delete all existing Tinygrad-based models which will need to be re-downloaded. Proceed?"), this)) {
params_memory.putBool("UpdateTinygrad", true);
params_memory.put("ModelDownloadProgress", "Downloading...");
updateTinygradButton->setText(0, tr("CANCEL"));
updateTinygradButton->setValue(tr("Updating..."));
updatingTinygrad = true;
}
}
} else if (id == 0) {
if (id == 0) {
if (modelDownloading) {
params_memory.putBool("CancelModelDownload", true);
cancellingDownload = true;
} else {
QStringList downloadableModels = availableModelNames;
for (const QString &modelKey : modelFileToNameMap.keys()) {
QString modelName = modelFileToNameMap.value(modelKey);
if (modelDir.exists(modelKey + ".thneed") || hasAllTinygradFiles(modelDir, modelKey)) {
downloadableModels.removeAll(modelName);
}
} else {
QMap<QString, QStringList> downloadableSeriesToModels;
QStringList downloadableModelNames;
for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
const QString &modelKey = it.key();
const QString &modelName = it.value();
if (modelName.isEmpty() || isModelInstalled(modelKey)) {
continue;
}
allModelsDownloaded = downloadableModels.isEmpty();
QString modelToDownload = MultiOptionDialog::getSelection(tr("Select a driving model to download"), downloadableModels, "", this);
QString series = modelSeriesMap.value(modelKey, tr("Custom Series"));
downloadableSeriesToModels[series].append(modelName);
if (!downloadableModelNames.contains(modelName)) {
downloadableModelNames.append(modelName);
}
}
allModelsDownloaded = downloadableModelNames.isEmpty();
if (allModelsDownloaded) {
return;
}
for (QString &series : downloadableSeriesToModels.keys()) {
QStringList &models = downloadableSeriesToModels[series];
models.removeDuplicates();
std::sort(models.begin(), models.end());
}
QStringList userFavorites = QString::fromStdString(params.get("UserFavorites")).split(",");
userFavorites.removeAll("");
QStringList communityFavorites = QString::fromStdString(params.get("CommunityFavorites")).split(",");
communityFavorites.removeAll("");
QString savedSortMode = QString::fromStdString(params.get("ModelSortMode"));
if (savedSortMode.isEmpty()) savedSortMode = "alphabetical";
ExpandableMultiOptionDialog dialog(
tr("Select a driving model to download"),
downloadableSeriesToModels,
"",
this,
userFavorites,
communityFavorites,
modelReleasedDates,
modelFileToNameMap,
savedSortMode);
int dialogResult = dialog.exec();
QString sortMode = dialog.getCurrentSortMode();
QStringList newUserFavs = dialog.getUserFavorites();
params.put("ModelSortMode", sortMode.toStdString());
params.put("UserFavorites", newUserFavs.join(",").toStdString());
userFavorites = newUserFavs;
if (dialogResult == QDialog::Accepted) {
QString modelToDownload = dialog.selection;
if (!modelToDownload.isEmpty()) {
params_memory.put("ModelToDownload", modelFileToNameMap.key(modelToDownload).toStdString());
params_memory.put("ModelDownloadProgress", "Downloading...");
QString modelKey = modelFileToNameMap.key(modelToDownload);
params_memory.put("ModelToDownload", modelKey.toStdString());
// Also persist the version for this downloaded model if known
{
QFile vf("/data/models/.model_versions.json");
if (vf.open(QIODevice::ReadOnly)) {
auto doc = QJsonDocument::fromJson(vf.readAll());
if (doc.isObject()) {
auto obj = doc.object();
if (obj.contains(modelKey)) {
params.put("ModelVersion", obj.value(modelKey).toString().toStdString());
}
}
}
}
params_memory.put("ModelDownloadProgress", "Downloading...");
downloadModelButton->setText(0, tr("CANCEL"));
downloadModelButton->setText(0, tr("CANCEL"));
downloadModelButton->setValue("Downloading...");
downloadModelButton->setValue("Downloading...");
downloadModelButton->setVisibleButton(1, false);
downloadModelButton->setVisibleButton(1, false);
modelDownloading = true;
modelDownloading = true;
}
}
}
} else if (id == 1) {
@@ -179,8 +241,8 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
});
modelToggle = downloadModelButton;
} else if (param == "ManageBlacklistedModels") {
FrogPilotButtonsControl *blacklistButton = new FrogPilotButtonsControl(title, desc, icon, {tr("ADD"), tr("REMOVE"), tr("REMOVE ALL")});
QObject::connect(blacklistButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
FrogPilotButtonsControl *blacklistBtn = new FrogPilotButtonsControl(title, desc, icon, {tr("ADD"), tr("REMOVE"), tr("REMOVE ALL")});
QObject::connect(blacklistBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
QStringList blacklistedModels = QString::fromStdString(params.get("BlacklistedModels")).split(",");
blacklistedModels.removeAll("");
@@ -193,9 +255,22 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
}
if (blacklistableModels.size() <= 1) {
ConfirmationDialog::alert(tr("There are no more driving models to blacklist. The only available model is \"%1\"!").arg(blacklistableModels.first()), this);
ConfirmationDialog::alert(tr("There are no more models to blacklist! The only available model is \"%1\"!").arg(blacklistableModels.first()), this);
} else {
QString modelToBlacklist = MultiOptionDialog::getSelection(tr("Select a driving model to add to the blacklist"), blacklistableModels, "", this);
// Group blacklistable models by series
QMap<QString, QStringList> blacklistableSeriesToModels;
for (const QString &modelName : blacklistableModels) {
QString modelKey = modelFileToNameMapProcessed.key(modelName);
QString series = modelSeriesMap.value(modelKey, "Custom Series");
blacklistableSeriesToModels[series].append(modelName);
}
// Sort models within each series
for (QString &series : blacklistableSeriesToModels.keys()) {
blacklistableSeriesToModels[series].sort();
}
QString modelToBlacklist = ExpandableMultiOptionDialog::getSelection(tr("Select a model to add to the blacklist"), blacklistableSeriesToModels, "", this);
if (!modelToBlacklist.isEmpty()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to add the \"%1\" model to the blacklist?").arg(modelToBlacklist), tr("Add"), this)) {
blacklistedModels.append(modelFileToNameMapProcessed.key(modelToBlacklist));
@@ -210,9 +285,21 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
QString modelName = modelFileToNameMapProcessed.value(model);
whitelistableModels.append(modelName);
}
whitelistableModels.sort();
QString modelToWhitelist = MultiOptionDialog::getSelection(tr("Select a driving model to remove from the blacklist"), whitelistableModels, "", this);
// Group whitelistable models by series
QMap<QString, QStringList> whitelistableSeriesToModels;
for (const QString &modelName : whitelistableModels) {
QString modelKey = modelFileToNameMapProcessed.key(modelName);
QString series = modelSeriesMap.value(modelKey, "Custom Series");
whitelistableSeriesToModels[series].append(modelName);
}
// Sort models within each series
for (QString &series : whitelistableSeriesToModels.keys()) {
whitelistableSeriesToModels[series].sort();
}
QString modelToWhitelist = ExpandableMultiOptionDialog::getSelection(tr("Select a model to remove from the blacklist"), whitelistableSeriesToModels, "", this);
if (!modelToWhitelist.isEmpty()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to remove the \"%1\" model from the blacklist?").arg(modelToWhitelist), tr("Remove"), this)) {
blacklistedModels.removeAll(modelFileToNameMapProcessed.key(modelToWhitelist));
@@ -221,18 +308,18 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
}
}
} else if (id == 2) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove all of your blacklisted driving models?"), this)) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove all of your blacklisted models?"), this)) {
params.remove("BlacklistedModels");
params_cache.remove("BlacklistedModels");
}
}
});
modelToggle = blacklistButton;
modelToggle = blacklistBtn;
} else if (param == "ManageScores") {
FrogPilotButtonsControl *manageScoresButton = new FrogPilotButtonsControl(title, desc, icon, {tr("RESET"), tr("VIEW")});
QObject::connect(manageScoresButton, &FrogPilotButtonsControl::buttonClicked, [modelLayout, modelLabelsList, modelLabelsPanel, this](int id) {
FrogPilotButtonsControl *manageScoresBtn = new FrogPilotButtonsControl(title, desc, icon, {tr("RESET"), tr("VIEW")});
QObject::connect(manageScoresBtn, &FrogPilotButtonsControl::buttonClicked, [this, modelLayout, modelLabelsList, modelLabelsPanel](int id) {
if (id == 0) {
if (FrogPilotConfirmationDialog::yesorno(tr("Reset all model drives and ratings? This clears your drive history and collected feedback!"), this)) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to reset all of your model drives and scores?"), this)) {
params.remove("ModelDrivesAndScores");
params_cache.remove("ModelDrivesAndScores");
}
@@ -244,82 +331,116 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
modelLayout->setCurrentWidget(modelLabelsPanel);
}
});
modelToggle = manageScoresButton;
modelToggle = manageScoresBtn;
} else if (param == "SelectModel") {
selectModelButton = new ButtonControl(title, tr("SELECT"), desc);
QObject::connect(selectModelButton, &ButtonControl::clicked, [this]() {
QStringList selectableModels;
// Group models by series for the enhanced dialog
QMap<QString, QStringList> seriesToModels;
QMap<QString, QString> installedModelFileToNameMap;
QMap<QString, QString> installedReleasedDates;
// Add all available models by series
for (const QString &modelKey : modelFileToNameMap.keys()) {
if (!isModelInstalled(modelKey)) {
continue;
}
QString modelName = modelFileToNameMap.value(modelKey);
if (modelName.contains("(Default)")) {
continue;
}
if (modelDir.exists(modelKey + ".thneed") || hasAllTinygradFiles(modelDir, modelKey)) {
selectableModels.append(modelName);
installedModelFileToNameMap.insert(modelKey, modelName);
if (modelReleasedDates.contains(modelKey)) {
installedReleasedDates.insert(modelKey, modelReleasedDates.value(modelKey));
}
QString series = modelSeriesMap.value(modelKey, "Custom Series");
seriesToModels[series].append(modelName);
}
selectableModels.sort();
selectableModels.prepend(modelFileToNameMap.value(normalizeModelKey(QString::fromStdString(params_default.get("Model")))));
QString modelToSelect = MultiOptionDialog::getSelection(tr("Select a Model — 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"), selectableModels, currentModel, this);
if (!modelToSelect.isEmpty()) {
currentModel = modelToSelect;
// Sort models alphabetically within each series
for (QString &series : seriesToModels.keys()) {
seriesToModels[series].sort();
}
params.put("Model", modelFileToNameMap.key(modelToSelect).toStdString());
// Add default model to the beginning of its series
QString defaultModelName = modelFileToNameMap.value(QString::fromStdString(params_default.get("Model")));
QString defaultSeries = modelSeriesMap.value(QString::fromStdString(params_default.get("Model")), "Custom Series");
if (seriesToModels.contains(defaultSeries) && seriesToModels[defaultSeries].contains(defaultModelName)) {
seriesToModels[defaultSeries].removeAll(defaultModelName);
seriesToModels[defaultSeries].prepend(defaultModelName);
}
updateFrogPilotToggles();
// Prepare favorites and dates for the enhanced dialog
QStringList userFavs = QString::fromStdString(params.get("UserFavorites")).split(",");
userFavs.removeAll("");
if (started) {
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
Hardware::reboot();
}
}
selectModelButton->setValue(modelToSelect);
QStringList communityFavs = QString::fromStdString(params.get("CommunityFavorites")).split(",");
communityFavs.removeAll("");
QStringList deletableModels;
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
if (base.startsWith(modelKey)) {
QString modelName = modelFileToNameMapProcessed.value(modelKey);
if (!deletableModels.contains(modelName)) {
deletableModels.append(modelName);
// Create dialog instance to access sort mode and favorites after selection
QString savedSortMode = QString::fromStdString(params.get("ModelSortMode"));
if (savedSortMode.isEmpty()) savedSortMode = "alphabetical";
ExpandableMultiOptionDialog dialog(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"),
seriesToModels, currentModel, this,
userFavs, communityFavs, installedReleasedDates, installedModelFileToNameMap, savedSortMode);
int dialogResult = dialog.exec();
// Persist sort mode and user favorites even if no selection was made
QString sortMode = dialog.getCurrentSortMode();
QStringList newUserFavs = dialog.getUserFavorites();
params.put("ModelSortMode", sortMode.toStdString());
params.put("UserFavorites", newUserFavs.join(",").toStdString());
if (dialogResult == QDialog::Accepted) {
QString modelToSelect = dialog.selection;
if (!modelToSelect.isEmpty()) {
currentModel = modelToSelect;
params.put("Model", modelFileToNameMap.key(modelToSelect).toStdString());
// Sync ModelVersion with the selected model if known
{
QString modelKey = modelFileToNameMap.key(modelToSelect);
QFile vf("/data/models/.model_versions.json");
if (vf.open(QIODevice::ReadOnly)) {
auto doc = QJsonDocument::fromJson(vf.readAll());
if (doc.isObject()) {
auto obj = doc.object();
if (obj.contains(modelKey)) {
params.put("ModelVersion", obj.value(modelKey).toString().toStdString());
}
}
}
}
updateFrogPilotToggles();
if (started) {
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
Hardware::reboot();
}
}
selectModelButton->setValue(modelToSelect);
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
}
deletableModels.removeAll(processModelName(currentModel));
deletableModels.removeAll(modelFileToNameMapProcessed.value(normalizeModelKey(QString::fromStdString(params_default.get("Model")))));
noModelsDownloaded = deletableModels.isEmpty();
}
});
modelToggle = selectModelButton;
} else if (param == "UpdateTinygrad") {
updateTinygradButton = new FrogPilotButtonsControl(title, desc, icon, {tr("UPDATE")});
QObject::connect(updateTinygradButton, &FrogPilotButtonsControl::buttonClicked, [this]() {
if (updatingTinygrad) {
params_memory.putBool("CancelModelDownload", true);
updateTinygradButton->setEnabled(false);
updateTinygradButton->setValue(tr("Cancelling..."));
cancellingDownload = true;
} else {
if (FrogPilotConfirmationDialog::yesorno(tr("Updating Tinygrad will delete existing Tinygrad-based driving models and need to be re-downloaded. Proceed?"), this)) {
params_memory.putBool("UpdateTinygrad", true);
params_memory.put("ModelDownloadProgress", "Downloading...");
updateTinygradButton->setText(0, tr("CANCEL"));
updateTinygradButton->setValue(tr("Updating..."));
updatingTinygrad = true;
}
}
});
modelToggle = updateTinygradButton;
} else if (param == "RecoveryPower") {
std::vector<QString> recoveryPowerButton{"Reset"};
modelToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0.5, 2.0, QString(), std::map<float, QString>(), 0.1, false, {}, recoveryPowerButton, false, false);
recoveryPowerToggle = static_cast<FrogPilotParamValueButtonControl*>(modelToggle);
} else if (param == "StopDistance") {
std::vector<QString> stopDistanceButton{"Reset"};
modelToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 4.0, 10.0, QString(), std::map<float, QString>(), 0.1, false, {}, stopDistanceButton, false, false);
stopDistanceToggle = static_cast<FrogPilotParamValueButtonControl*>(modelToggle);
} else {
modelToggle = new ParamControl(param, title, desc, icon);
}
@@ -328,21 +449,16 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
modelList->addItem(modelToggle);
QObject::connect(modelToggle, &AbstractControl::hideDescriptionEvent, [this]() {
update();
});
QObject::connect(modelToggle, &AbstractControl::showDescriptionEvent, [this]() {
update();
});
}
openDescriptions(forceOpenDescriptions, toggles);
QObject::connect(static_cast<ToggleControl*>(toggles["ModelRandomizer"]), &ToggleControl::toggleFlipped, [this](bool state) {
updateToggles();
if (state && !allModelsDownloaded) {
if (FrogPilotConfirmationDialog::yesorno(tr("The \"Model Randomizer\" works only with downloaded models. Download all models now?"), this)) {
if (FrogPilotConfirmationDialog::yesorno(tr("The \"Model Randomizer\" only works with downloaded models. Do you want to download all the driving models?"), this)) {
params_memory.putBool("DownloadAllModels", true);
params_memory.put("ModelDownloadProgress", "Downloading...");
@@ -353,13 +469,120 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
}
});
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubPanel, [modelLayout, modelPanel, this] {
openDescriptions(forceOpenDescriptions, toggles);
modelLayout->setCurrentWidget(modelPanel);
});
if (recoveryPowerToggle) {
QObject::connect(recoveryPowerToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this, recoveryPowerToggle]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset your <b>Recovery Power</b> to the default of 1.0?"), tr("Reset"), this)) {
params.putFloat("RecoveryPower", 1.0);
recoveryPowerToggle->refresh();
updateFrogPilotToggles();
}
});
}
if (stopDistanceToggle) {
QObject::connect(stopDistanceToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this, stopDistanceToggle]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset your <b>Stop Distance</b> to the default of 6 meters?"), tr("Reset"), this)) {
params.putFloat("StopDistance", 6.0);
stopDistanceToggle->refresh();
updateFrogPilotToggles();
}
});
}
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubPanel, [modelLayout, modelPanel] {modelLayout->setCurrentWidget(modelPanel);});
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotModelPanel::updateState);
}
bool FrogPilotModelPanel::isModelInstalled(const QString &key) const {
if (key.isEmpty()) {
return false;
}
bool has_thneed = false;
bool has_policy_meta = false;
bool has_policy_tg = false;
bool has_vision_meta = false;
bool has_vision_tg = false;
bool has_off_policy_meta = false;
bool has_off_policy_tg = false;
bool foundAny = false;
for (const QString &file : modelDir.entryList(QDir::Files)) {
QFileInfo fi(modelDir.filePath(file));
const QString base = fi.baseName();
const QString ext = fi.suffix();
if (!(base.startsWith(key) || base.startsWith(key + "_"))) continue;
foundAny = true;
if (ext == "thneed") {
has_thneed = true;
} else if (ext == "pkl") {
if (base.contains("_driving_policy_metadata")) {
has_policy_meta = true;
} else if (base.contains("_driving_policy_tinygrad")) {
has_policy_tg = true;
} else if (base.contains("_driving_off_policy_metadata")) {
has_off_policy_meta = true;
} else if (base.contains("_driving_off_policy_tinygrad")) {
has_off_policy_tg = true;
} else if (base.contains("_driving_vision_metadata")) {
has_vision_meta = true;
} else if (base.contains("_driving_vision_tinygrad")) {
has_vision_tg = true;
}
}
}
if (has_thneed) {
return true;
}
if (has_policy_meta && has_policy_tg && has_vision_meta && has_vision_tg) {
if (has_off_policy_meta || has_off_policy_tg) {
return has_off_policy_meta && has_off_policy_tg;
}
return true;
}
return foundAny;
}
QMap<QString, QString> FrogPilotModelPanel::getDeletableModelDisplayNames() {
QMap<QString, QString> deletable;
QString defaultModelKey = QString::fromStdString(params_default.get("Model"));
QString defaultModelName = modelFileToNameMap.value(defaultModelKey);
QString processedDefault = processModelName(defaultModelName);
QString processedCurrent = processModelName(currentModel);
for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
const QString &modelKey = it.key();
const QString &displayName = it.value();
if (displayName.isEmpty()) {
continue;
}
if (!isModelInstalled(modelKey)) {
continue;
}
QString processedName = processModelName(displayName);
if (!processedCurrent.isEmpty() && processedName == processedCurrent) {
continue;
}
if (!processedDefault.isEmpty() && processedName == processedDefault) {
continue;
}
deletable.insert(modelKey, displayName);
}
return deletable;
}
void FrogPilotModelPanel::showEvent(QShowEvent *event) {
FrogPilotUIState &fs = *frogpilotUIState();
UIState &s = *uiState();
@@ -368,68 +591,90 @@ void FrogPilotModelPanel::showEvent(QShowEvent *event) {
tuningLevel = parent->tuningLevel;
allModelsDownloading = params_memory.getBool("DownloadAllModels");
modelDownloading = !params_memory.get("ModelDownloadProgress").empty();
tinygradUpdate = params.getBool("TinygradUpdateAvailable");
updatingTinygrad = params_memory.getBool("UpdateTinygrad");
modelDownloading &= !updatingTinygrad;
modelDownloading = !params_memory.get("ModelToDownload").empty();
QStringList availableModels = QString::fromStdString(params.get("AvailableModels")).split(",");
availableModels.sort();
availableModelNames = QString::fromStdString(params.get("AvailableModelNames")).split(",");
availableModelNames.sort();
availableModelSeries = QString::fromStdString(params.get("AvailableModelSeries")).split(",");
QStringList releasedDatesParam = QString::fromStdString(params.get("ModelReleasedDates")).split(",");
QStringList communityFavsParam = QString::fromStdString(params.get("CommunityFavorites")).split(",");
QStringList userFavsParam = QString::fromStdString(params.get("UserFavorites")).split(",");
// Build a simple model->version map for quick lookups elsewhere
{
QStringList versionList = QString::fromStdString(params.get("ModelVersions")).split(",");
QJsonObject versionObj;
int verCount = qMin(availableModels.size(), versionList.size());
for (int i = 0; i < verCount; ++i) {
versionObj.insert(availableModels[i], versionList[i]);
}
QFile out("/data/models/.model_versions.json");
if (out.open(QIODevice::WriteOnly)) {
out.write(QJsonDocument(versionObj).toJson());
out.close();
}
}
modelFileToNameMap.clear();
modelFileToNameMapProcessed.clear();
for (int i = 0; i < qMin(availableModels.size(), availableModelNames.size()); ++i) {
modelFileToNameMap.insert(availableModels[i], availableModelNames[i]);
modelFileToNameMapProcessed.insert(availableModels[i], processModelName(availableModelNames[i]));
}
QStringList downloadableModels = availableModelNames;
for (const QString &modelKey : modelFileToNameMap.keys()) {
QString modelName = modelFileToNameMap.value(modelKey);
if (modelDir.exists(modelKey + ".thneed") || hasAllTinygradFiles(modelDir, modelKey)) {
downloadableModels.removeAll(modelName);
modelSeriesMap.clear();
modelReleasedDates.clear();
int size = qMin(availableModels.size(), availableModelNames.size());
for (int i = 0; i < size; ++i) {
const QString modelKey = availableModels[i].trimmed();
const QString modelName = availableModelNames[i].trimmed();
if (modelKey.isEmpty() || modelName.isEmpty()) {
continue;
}
}
allModelsDownloaded = downloadableModels.isEmpty();
QStringList deletableModels;
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
if (base.startsWith(modelKey)) {
QString modelName = modelFileToNameMapProcessed.value(modelKey);
if (!deletableModels.contains(modelName)) {
deletableModels.append(modelName);
}
QString series;
if (i < availableModelSeries.size()) {
series = availableModelSeries[i].trimmed();
}
if (series.isEmpty()) {
series = tr("Custom Series");
}
modelFileToNameMap.insert(modelKey, modelName);
modelFileToNameMapProcessed.insert(modelKey, processModelName(modelName));
modelSeriesMap.insert(modelKey, series);
if (i < releasedDatesParam.size()) {
const QString released = releasedDatesParam[i].trimmed();
if (!released.isEmpty()) {
this->modelReleasedDates.insert(modelKey, released);
}
}
}
deletableModels.removeAll(processModelName(currentModel));
deletableModels.removeAll(modelFileToNameMapProcessed.value(normalizeModelKey(QString::fromStdString(params_default.get("Model")))));
noModelsDownloaded = deletableModels.isEmpty();
allModelsDownloaded = true;
for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
if (it.value().isEmpty()) {
continue;
}
if (!isModelInstalled(it.key())) {
allModelsDownloaded = false;
break;
}
}
QString modelKey = normalizeModelKey(QString::fromStdString(params.get("Model")));
if (!modelDir.exists(modelKey + ".thneed") && !hasAllTinygradFiles(modelDir, modelKey)) {
modelKey = normalizeModelKey(QString::fromStdString(params_default.get("Model")));
QString modelKey = QString::fromStdString(params.get("Model"));
if (!isModelInstalled(modelKey)) {
modelKey = QString::fromStdString(params_default.get("Model"));
}
currentModel = modelFileToNameMap.value(modelKey);
selectModelButton->setValue(currentModel);
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
bool parked = !s.scene.started || fs.frogpilot_scene.parked || fs.frogpilot_toggles.value("frogs_go_moo").toBool();
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
downloadModelButton->setEnabledButtons(0, !allModelsDownloaded && !allModelsDownloading && !cancellingDownload && !updatingTinygrad && fs.frogpilot_scene.online && parked);
downloadModelButton->setEnabledButtons(1, !allModelsDownloaded && !modelDownloading && !cancellingDownload && !updatingTinygrad && fs.frogpilot_scene.online && parked);
downloadModelButton->setEnabledButtons(0, !allModelsDownloaded && !allModelsDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked);
downloadModelButton->setEnabledButtons(1, !allModelsDownloaded && !modelDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked);
downloadModelButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : "Not parked") : tr("Offline..."));
updateTinygradButton->setEnabled(!modelDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked && tinygradUpdate);
updateTinygradButton->setValue(tinygradUpdate ? tr("Update available!") : tr("Up to date!"));
started = s.scene.started;
updateToggles();
@@ -444,32 +689,27 @@ void FrogPilotModelPanel::updateState(const UIState &s, const FrogPilotUIState &
if (allModelsDownloading || modelDownloading) {
QString progress = QString::fromStdString(params_memory.get("ModelDownloadProgress"));
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|missing|offline", QRegularExpression::CaseInsensitiveOption));
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption));
if (progress != "Downloading...") {
downloadModelButton->setValue(progress);
}
if (progress == "All models downloaded!" || progress == "Downloaded!" && !allModelsDownloading || downloadFailed) {
if (progress == "All models downloaded!" && allModelsDownloading || progress == "Downloaded!" && modelDownloading || downloadFailed) {
finalizingDownload = true;
QTimer::singleShot(2500, [progress, this]() {
QTimer::singleShot(2500, [this, progress]() {
allModelsDownloaded = progress == "All models downloaded!";
allModelsDownloading = false;
cancellingDownload = false;
finalizingDownload = false;
modelDownloading = false;
noModelsDownloaded = false;
QStringList downloadableModels = availableModelNames;
for (const QString &modelKey : modelFileToNameMap.keys()) {
QString modelName = modelFileToNameMap.value(modelKey);
if (modelDir.exists(modelKey + ".thneed") || hasAllTinygradFiles(modelDir, modelKey)) {
downloadableModels.removeAll(modelName);
}
}
allModelsDownloaded = downloadableModels.isEmpty();
params_memory.remove("CancelModelDownload");
params_memory.remove("DownloadAllModels");
params_memory.remove("ModelDownloadProgress");
params_memory.remove("ModelToDownload");
downloadModelButton->setEnabled(true);
downloadModelButton->setValue("");
@@ -479,58 +719,20 @@ void FrogPilotModelPanel::updateState(const UIState &s, const FrogPilotUIState &
downloadModelButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : "Not parked") : tr("Offline..."));
}
if (updatingTinygrad) {
QString progress = QString::fromStdString(params_memory.get("ModelDownloadProgress"));
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|missing|offline", QRegularExpression::CaseInsensitiveOption));
if (progress != "Downloading...") {
updateTinygradButton->setValue(progress);
}
if (progress == "Updated!" && updatingTinygrad || downloadFailed) {
finalizingDownload = true;
QTimer::singleShot(2500, [progress, this]() {
modelDownloading = !params_memory.get("ModelDownloadProgress").empty();
if (modelDownloading) {
downloadModelButton->setText(1, tr("CANCEL"));
downloadModelButton->setValue("Downloading...");
downloadModelButton->setVisibleButton(0, false);
} else {
cancellingDownload = false;
}
tinygradUpdate = params.getBool("TinygradUpdateAvailable");
finalizingDownload = false;
updatingTinygrad = false;
updateTinygradButton->setEnabled(tinygradUpdate);
updateTinygradButton->setText(0, tr("UPDATE"));
updateTinygradButton->setValue(tinygradUpdate ? tr("Update available!") : tr("Up to date!"));
});
}
}
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
downloadModelButton->setText(0, modelDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
downloadModelButton->setText(1, allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD ALL"));
downloadModelButton->setEnabledButtons(0, !allModelsDownloaded && !allModelsDownloading && !cancellingDownload && !finalizingDownload && !updatingTinygrad && fs.frogpilot_scene.online && parked);
downloadModelButton->setEnabledButtons(1, !allModelsDownloaded && !modelDownloading && !cancellingDownload && !finalizingDownload && !updatingTinygrad && fs.frogpilot_scene.online && parked);
downloadModelButton->setEnabledButtons(0, !allModelsDownloaded && !allModelsDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked);
downloadModelButton->setEnabledButtons(1, !allModelsDownloaded && !modelDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked);
downloadModelButton->setVisibleButton(0, !allModelsDownloading);
downloadModelButton->setVisibleButton(1, !modelDownloading);
updateTinygradButton->setEnabled(!modelDownloading && !cancellingDownload && !cancellingDownload && !finalizingDownload && fs.frogpilot_scene.online && parked && tinygradUpdate);
started = s.scene.started;
parent->keepScreenOn = allModelsDownloading || modelDownloading || updatingTinygrad;
parent->keepScreenOn = allModelsDownloading || modelDownloading;
}
void FrogPilotModelPanel::updateModelLabels(FrogPilotListWidget *labelsList) {
@@ -561,16 +763,16 @@ void FrogPilotModelPanel::updateToggles() {
if (key == "ManageBlacklistedModels" || key == "ManageScores") {
setVisible &= params.getBool("ModelRandomizer");
}
else if (key == "SelectModel") {
} else if (key == "SelectModel") {
setVisible &= !params.getBool("ModelRandomizer");
} else if (key == "RecoveryPower") {
setVisible &= (tuningLevel == 3); // Only visible in developer tuning level
} else if (key == "StopDistance") {
setVisible &= (tuningLevel == 3); // Only visible in developer tuning level
}
toggle->setVisible(setVisible);
}
openDescriptions(forceOpenDescriptions, toggles);
update();
}
+6
View File
@@ -20,6 +20,8 @@ private:
void updateModelLabels(FrogPilotListWidget *labelsList);
void updateState(const UIState &s, const FrogPilotUIState &fs);
void updateToggles();
bool isModelInstalled(const QString &key) const;
QMap<QString, QString> getDeletableModelDisplayNames();
bool allModelsDownloaded;
bool allModelsDownloading;
@@ -55,8 +57,12 @@ private:
QMap<QString, QString> modelFileToNameMap;
QMap<QString, QString> modelFileToNameMapProcessed;
QMap<QString, QString> modelReleasedDates;
QMap<QString, QString> modelSeriesMap;
QString currentModel;
QStringList availableModelNames;
QStringList availableModelSeries;
};
+65
View File
@@ -66,6 +66,11 @@ void deleteThemeAsset(QDir &directory, const QString &subFolder, const QString &
}
void downloadThemeAsset(const QString &input, const std::string &paramKey, const QString &assetParam, Params &params, Params &params_memory) {
if (paramKey == "BootLogoToDownload") {
params_memory.put(paramKey, input.trimmed().toStdString());
return;
}
QString output = input;
int tilde = output.indexOf("~");
if (tilde >= 0) {
@@ -228,6 +233,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
const std::vector<std::tuple<QString, QString, QString, QString>> themeToggles {
{"PersonalizeOpenpilot", tr("Custom Themes"), tr("<b>The overall look and feel of openpilot.</b> Use the \"Theme Maker\" in \"The Pond\" to create and share your own themes!"), "../../frogpilot/assets/toggle_icons/icon_frog.png"},
{"BootLogo", tr("Boot Logo"), tr("<b>The boot logo shown while the device starts.</b>"), ""},
{"CustomColors", tr("Color Scheme"), tr("<b>The color scheme used throughout openpilot.</b> Use the \"Theme Maker\" in \"The Pond\" to create and share your own themes!"), ""},
{"CustomDistanceIcons", tr("Distance Button"), tr("<b>The distance button icons shown on the driving screen.</b> Use the \"Theme Maker\" in \"The Pond\" to create and share your own themes!"), ""},
{"CustomIcons", tr("Icon Pack"), tr("<b>The icon style used across openpilot.</b> Use the \"Theme Maker\" in \"The Pond\" to create and share your own themes!"), ""},
@@ -252,6 +258,57 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
themesLayout->setCurrentWidget(customThemesPanel);
});
themeToggle = personalizeOpenpilotToggle;
} else if (param == "BootLogo") {
manageBootLogosButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
QObject::connect(manageBootLogosButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
// Show all downloaded boot logos, including the currently selected one.
QStringList bootLogos = getThemeList(true, QDir(bootLogosDirectory.path()), "", "BootLogo", params);
if (id == 0) {
QString bootLogoToDelete = MultiOptionDialog::getSelection(tr("Select a boot logo to delete"), bootLogos, "", this);
if (!bootLogoToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Delete the \"%1\" boot logo?").arg(bootLogoToDelete), tr("Delete"), this)) {
bootLogosDownloaded = false;
deleteThemeAsset(bootLogosDirectory, "", "DownloadableBootLogos", bootLogoToDelete, params);
}
} else if (id == 1) {
if (bootLogoDownloading) {
cancellingDownload = true;
params_memory.putBool("CancelThemeDownload", true);
QTimer::singleShot(2500, [this]() {
bootLogoDownloading = false;
cancellingDownload = false;
themeDownloading = false;
params_memory.putBool("CancelThemeDownload", false);
});
} else {
QStringList downloadableBootLogos = QString::fromStdString(params.get("DownloadableBootLogos")).split(",");
bootLogoToDownload = MultiOptionDialog::getSelection(tr("Select a boot logo to download"), downloadableBootLogos, "", this);
if (!bootLogoToDownload.isEmpty()) {
manageBootLogosButton->setValue(storeThemeName(bootLogoToDownload, "BootLogo", params));
bootLogoDownloading = true;
themeDownloading = true;
params_memory.put("ThemeDownloadProgress", "Downloading...");
downloadThemeAsset(bootLogoToDownload, "BootLogoToDownload", "DownloadableBootLogos", params, params_memory);
downloadStatusLabel->setText("Downloading...");
}
}
} else if (id == 2) {
QString bootLogoToSelect = MultiOptionDialog::getSelection(tr("Select a boot logo"), bootLogos, getThemeName("BootLogo", params), this);
if (!bootLogoToSelect.isEmpty()) {
manageBootLogosButton->setValue(storeThemeName(bootLogoToSelect, "BootLogo", params));
}
}
});
manageBootLogosButton->setValue(getThemeName(param.toStdString(), params));
themeToggle = manageBootLogosButton;
} else if (param == "CustomColors") {
manageCustomColorsButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
QObject::connect(manageCustomColorsButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
@@ -704,6 +761,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
}
void FrogPilotThemesPanel::showEvent(QShowEvent *event) {
bootLogosDownloaded = params.get("DownloadableBootLogos").empty();
colorsDownloaded = params.get("DownloadableColors").empty();
distanceIconsDownloaded = params.get("DownloadableDistanceIcons").empty();
iconsDownloaded = params.get("DownloadableIcons").empty();
@@ -756,6 +814,7 @@ void FrogPilotThemesPanel::updateState(const UIState &s, const FrogPilotUIState
finalizingDownload = true;
QTimer::singleShot(2500, [this]() {
bootLogoDownloading = false;
colorDownloading = false;
distanceIconDownloading = false;
finalizingDownload = false;
@@ -765,6 +824,7 @@ void FrogPilotThemesPanel::updateState(const UIState &s, const FrogPilotUIState
themeDownloading = false;
wheelDownloading = false;
bootLogosDownloaded = params.get("DownloadableBootLogos").empty();
colorsDownloaded = params.get("DownloadableColors").empty();
distanceIconsDownloaded = params.get("DownloadableDistanceIcons").empty();
iconsDownloaded = params.get("DownloadableIcons").empty();
@@ -782,6 +842,11 @@ void FrogPilotThemesPanel::updateState(const UIState &s, const FrogPilotUIState
bool parked = !s.scene.started || fs.frogpilot_scene.parked || fs.frogpilot_toggles.value("frogs_go_moo").toBool();
manageBootLogosButton->setText(1, bootLogoDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
manageBootLogosButton->setEnabledButtons(0, !themeDownloading);
manageBootLogosButton->setEnabledButtons(1, fs.frogpilot_scene.online && (!themeDownloading || bootLogoDownloading) && !cancellingDownload && !finalizingDownload && !bootLogosDownloaded && parked);
manageBootLogosButton->setEnabledButtons(2, !themeDownloading);
manageCustomColorsButton->setText(1, colorDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
manageCustomColorsButton->setEnabledButtons(0, !themeDownloading);
manageCustomColorsButton->setEnabledButtons(1, fs.frogpilot_scene.online && (!themeDownloading || colorDownloading) && !cancellingDownload && !finalizingDownload && !colorsDownloaded && parked);
+6 -1
View File
@@ -19,6 +19,8 @@ private:
void updateToggles();
bool cancellingDownload;
bool bootLogoDownloading = false;
bool bootLogosDownloaded = false;
bool colorDownloading;
bool colorsDownloaded;
bool distanceIconDownloading;
@@ -40,10 +42,11 @@ private:
std::map<QString, AbstractControl*> toggles;
QSet<QString> customThemeKeys = {"CustomColors", "CustomDistanceIcons", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon"};
QSet<QString> customThemeKeys = {"BootLogo", "CustomColors", "CustomDistanceIcons", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon"};
QSet<QString> parentKeys;
FrogPilotButtonsControl *manageBootLogosButton;
FrogPilotButtonsControl *manageCustomColorsButton;
FrogPilotButtonsControl *manageCustomIconsButton;
FrogPilotButtonsControl *manageCustomSignalsButton;
@@ -55,11 +58,13 @@ private:
LabelControl *downloadStatusLabel;
QDir bootLogosDirectory{"/data/themes/bootlogos/"};
QDir themePacksDirectory{"/data/themes/theme_packs/"};
QDir wheelsDirectory{"/data/themes/steering_wheels/"};
QJsonObject frogpilotToggleLevels;
QString bootLogoToDownload;
QString colorSchemeToDownload;
QString distanceIconPackToDownload;
QString iconPackToDownload;
@@ -1,6 +1,9 @@
#include <QRegularExpression>
#include <QTextStream>
#include <chrono>
#include <thread>
#include "frogpilot/ui/qt/offroad/vehicle_settings.h"
QStringList getCarNames(const QString &carMake, QMap<QString, QString> &carModels) {
@@ -168,7 +171,10 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
std::vector<std::tuple<QString, QString, QString, QString>> vehicleToggles {
{"GMToggles", tr("General Motors Settings"), tr("<b>FrogPilot features for General Motors vehicles.</b>"), ""},
{"ExperimentalGMTune", tr("FrogsGoMoo's Experimental Tune"), tr("<b>Experimental GM tune by FrogsGoMoo</b> that attempts to smoothen stopping and takeoff control. Use at your own risk!"), ""},
{"GMPedalLongitudinal", tr("Use Pedal for Longitudinal Control"), tr("<b>Use the pedal interceptor for longitudinal control</b> instead of camera ACC/Redneck when available."), ""},
{"LongPitch", tr("Smooth Pedal Response on Hills"), tr("<b>Smoothen acceleration and braking</b> when driving downhill/uphill."), ""},
{"RedPanda", tr("Red Panda"), tr("<b>Enable Red Panda behavior</b> for GM (alternate safety config and bus numbering). Requires a reboot to take effect."), ""},
{"RemoteStartBootsComma", tr("Remote Start Boots Comma"), tr("<b>Use GM C9 SystemPowerMode</b> for ignition detection. Toggle requires a panda firmware update and a reboot to take effect."), ""},
{"VoltSNG", tr("Stop-and-Go Hack"), tr("<b>Force stop-and-go</b> on the 2017 Chevy Volt."), ""},
{"HKGToggles", tr("Hyundai/Kia/Genesis Settings"), tr("<b>FrogPilot features for Genesis, Hyundai, and Kia vehicles.</b>"), ""},
@@ -188,6 +194,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
{"PedalSupport", tr("comma Pedal Support"), tr("<b>Does your vehicle support the \"comma pedal\"?</b>"), ""},
{"OpenpilotLongitudinal", tr("openpilot Longitudinal Support"), tr("<b>Can openpilot control the vehicle's acceleration and braking?</b>"), ""},
{"RadarSupport", tr("Radar Support"), tr("<b>Does openpilot use the vehicle's radar data</b> alongside the device's camera for tracking lead vehicles?"), ""},
{"SASCMSupport", tr("SASCM Support"), tr("<b>Does your vehicle support \"SASCMs\"?</b>"), ""},
{"SDSUSupport", tr("SDSU Support"), tr("<b>Does your vehicle support \"SDSUs\"?</b>"), ""},
{"SNGSupport", tr("Stop-and-Go Support"), tr("<b>Does your vehicle support stop-and-go driving?</b>"), ""}
};
@@ -298,6 +305,25 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
});
}
ParamControl *remoteStartToggle = static_cast<ParamControl*>(toggles["RemoteStartBootsComma"]);
QObject::connect(remoteStartToggle, &ToggleControl::toggleFlipped, [parent, remoteStartToggle, this](bool state) {
const QString prompt = tr("Remote Start requires a Panda firmware update. Flash the Panda now?");
if (!FrogPilotConfirmationDialog::yesorno(prompt, this)) {
params.putBool("RemoteStartBootsComma", !state);
remoteStartToggle->refresh();
return;
}
std::thread([parent, this]() {
parent->keepScreenOn = true;
params_memory.putBool("FlashPanda", true);
while (params_memory.getBool("FlashPanda")) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
Hardware::reboot();
}).detach();
});
openDescriptions(forceOpenDescriptions, toggles);
QObject::connect(uiState(), &UIState::offroadTransition, [selectMakeButton, selectModelButton, this]() {
@@ -341,6 +367,7 @@ void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
QStringList detected;
if (hasPedal) detected << "comma Pedal";
if (parent->hasSASCM) detected << "SASCM";
if (parent->hasSDSU) detected << "SDSU";
if (parent->hasZSS) detected << "ZSS";
static_cast<LabelControl*>(toggles["HardwareDetected"])->setText(detected.isEmpty() ? tr("None") : detected.join(", "));
@@ -349,6 +376,7 @@ void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
static_cast<LabelControl*>(toggles["OpenpilotLongitudinal"])->setText(hasOpenpilotLongitudinal ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["PedalSupport"])->setText(parent->canUsePedal ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["RadarSupport"])->setText(parent->hasRadar ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["SASCMSupport"])->setText(parent->canUseSASCM ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["SDSUSupport"])->setText(parent->canUseSDSU ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["SNGSupport"])->setText(hasSNG ? tr("Yes") : tr("No"));
@@ -403,6 +431,10 @@ void FrogPilotVehiclesPanel::updateToggles() {
setVisible &= isHKGCanFd;
}
else if (key == "GMPedalLongitudinal") {
setVisible &= hasPedal;
}
else if (key == "VoltSNG") {
setVisible &= isVolt && !hasSNG;
}
+3 -2
View File
@@ -36,11 +36,11 @@ private:
std::map<QString, AbstractControl*> toggles;
QSet<QString> gmKeys = {"ExperimentalGMTune", "LongPitch", "VoltSNG"};
QSet<QString> gmKeys = {"ExperimentalGMTune", "GMPedalLongitudinal", "LongPitch", "RedPanda", "RemoteStartBootsComma", "VoltSNG"};
QSet<QString> hkgKeys = {"NewLongAPI", "TacoTuneHacks"};
QSet<QString> longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "SNGHack", "VoltSNG"};
QSet<QString> toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"};
QSet<QString> vehicleInfoKeys = {"BlindSpotSupport", "HardwareDetected", "OpenpilotLongitudinal", "PedalSupport", "RadarSupport", "SDSUSupport", "SNGSupport"};
QSet<QString> vehicleInfoKeys = {"BlindSpotSupport", "HardwareDetected", "OpenpilotLongitudinal", "PedalSupport", "RadarSupport", "SASCMSupport", "SDSUSupport", "SNGSupport"};
QSet<QString> parentKeys;
@@ -50,6 +50,7 @@ private:
ParamControl *forceFingerprint;
Params params;
Params params_memory{"/dev/shm/params"};
Params params_default{"/dev/shm/params_default"};
QJsonObject frogpilotToggleLevels;
@@ -214,6 +214,7 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) :
{13, tr("Longitudinal MPC Jerk: Acceleration")},
{14, tr("Longitudinal MPC Jerk: Danger Zone")},
{15, tr("Longitudinal MPC Jerk: Speed Control")},
{16, tr("Driving Model: Current")},
};
ButtonControl *metricToggle = new ButtonControl(title, tr("SELECT"), desc);
+107
View File
@@ -8,7 +8,114 @@ source "$BASEDIR/launch_env.sh"
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# --- BEGIN: auto-fix persist from squashfs -> ext4 (one-time, preserves identity) ---
function persist_convert_if_needed {
LOG="/tmp/persist_fix.log"
{
echo "[persist-fix] ----- start $(date) -----"
# Guard: if we've already converted on this device, skip
if [ -f /data/.persist_converted ] || [ -f /persist/.converted_to_ext4 ]; then
echo "[persist-fix] Marker exists; skipping."
return 0
fi
# Discover persist device
DEV="$(blkid -t LABEL=persist -o device 2>/dev/null || true)"
if [ -z "$DEV" ] && [ -e /dev/disk/by-partlabel/persist ]; then
DEV="$(realpath /dev/disk/by-partlabel/persist)"
fi
if [ -z "$DEV" ]; then
# Fallback commonly used path
if lsblk -no PATH,LABEL | grep -E "persist$" >/dev/null 2>&1; then
DEV="$(lsblk -no PATH,LABEL | awk '$2=="persist"{print $1; exit}')"
else
DEV="/dev/sda2"
fi
fi
echo "[persist-fix] Using device: ${DEV}"
# Detect device filesystem type
FSTYPE="$(blkid -o value -s TYPE "$DEV" 2>/dev/null || true)"
echo "[persist-fix] Detected $DEV fstype='$FSTYPE'"
UNSQS="$DIR/third_party/bin/unsquashfs"
# Detect current /persist mount status & fstype
CUR_MNT_TYPE="$(mount | awk '$3==\"/persist\"{print $5}' | head -n1)"
if mountpoint -q /persist; then
echo "[persist-fix] /persist currently mounted as type='${CUR_MNT_TYPE}'"
else
echo "[persist-fix] /persist is not a mountpoint (empty dir)."
fi
# Only proceed on NEW devices: either /persist is not mounted OR is squashfs,
# AND the underlying persist partition is squashfs (factory RO image).
if { ! mountpoint -q /persist || [ "${CUR_MNT_TYPE}" = "squashfs" ]; } && [ "${FSTYPE}" = "squashfs" ]; then
echo "[persist-fix] NEW device detected (squashfs persist). Converting to ext4."
# Stream identity files using unsquashfs -cat directly
for f in id_rsa id_rsa.pub color_cal dongle_id; do
if sudo "$UNSQS" -cat "$DEV" "comma/$f" > "/data/$f" 2>/dev/null; then
echo "[persist-fix] Preserved $f"
else
echo "[persist-fix] $f not found in squashfs (ok, may not exist)."
fi
done
# (Optional) raw backup of the original partition image (once)
if [ ! -f /data/persist_backup.img ]; then
echo "[persist-fix] Creating raw backup of ${DEV} to /data/persist_backup.img"
sudo dd if="$DEV" of=/data/persist_backup.img bs=1M status=none || true
sync
fi
# Reformat persist as ext4 and label it
echo "[persist-fix] Formatting ${DEV} as ext4..."
if ! sudo mkfs.ext4 -F "$DEV"; then
echo "[persist-fix][ERROR] mkfs.ext4 failed on ${DEV}. Aborting."
return 0
fi
sudo e2label "$DEV" persist || true
# Mount the new ext4 persist RW
if ! sudo mount -t ext4 -o rw,discard "$DEV" /persist; then
echo "[persist-fix][ERROR] Failed to mount new ext4 persist. Aborting."
return 0
fi
# Recreate expected structure and restore identity
sudo mkdir -p /persist/{comma,params,tracking}
sudo chmod 755 /persist /persist/{comma,params,tracking}
for f in id_rsa id_rsa.pub color_cal dongle_id; do
if [ -f "/data/$f" ]; then
sudo cp -p "/data/$f" "/persist/comma/$f"
fi
done
if [ -f /persist/comma/id_rsa ]; then sudo chmod 600 /persist/comma/id_rsa; fi
sudo chown -R comma:comma /persist/ /persist/comma /persist/params /persist/tracking || true
sudo touch /persist/tracking/.lock
# Seed minimal params (only if not present)
[ -f /persist/params/HasAcceptedTerms ] || echo -n 1 | sudo tee /persist/params/HasAcceptedTerms >/dev/null
[ -f /persist/params/AlwaysOnDM ] || echo -n 1 | sudo tee /persist/params/AlwaysOnDM >/dev/null
# Mark complete so we don't run again; reboot once
echo "ext4" | sudo tee /persist/.converted_to_ext4 >/dev/null
sudo touch /data/.persist_converted
sync
echo "[persist-fix] Conversion complete. Rebooting once to finalize..."
sudo reboot
else
echo "[persist-fix] Device is not NEW/squashfs (persist already ext4 or mounted). Skipping."
fi
echo "[persist-fix] ----- end $(date) -----"
} >>"$LOG" 2>&1
}
# --- END: auto-fix persist from squashfs -> ext4 ---
function agnos_init {
persist_convert_if_needed
# TODO: move this to agnos
sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta
+1 -1
View File
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="10.1"
export AGNOS_VERSION="10.1.1"
fi
export STAGING_ROOT="/data/safe_staging"
+16 -4
View File
@@ -82,6 +82,12 @@ 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
@@ -165,6 +171,7 @@ BO_ 481 ASCMSteeringButton: 7 K124_ASCM
SG_ DistanceButton : 22|1@0+ (1,0) [0|0] "" NEO
SG_ LKAButton : 23|1@0+ (1,0) [0|0] "" NEO
SG_ ACCAlwaysOne : 24|1@0+ (1,0) [0|1] "" XXX
SG_ ACCHiddenBit : 30|1@0+ (1,0) [0|1] "" XXX
SG_ ACCButtons : 46|3@0+ (1,0) [0|0] "" NEO
SG_ DriveModeButton : 39|1@0+ (1,0) [0|1] "" XXX
SG_ RollingCounter : 33|2@0+ (1,0) [0|3] "" NEO
@@ -192,10 +199,15 @@ BO_ 500 SportMode: 6 XXX
SG_ SportMode : 15|1@0+ (1,0) [0|1] "" XXX
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_ 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
BO_ 532 BRAKE_RELATED: 6 XXX
SG_ UserBrakePressure : 0|9@0+ (1,0) [0|511] "" XXX
@@ -222,7 +234,7 @@ BO_ 715 ASCMGasRegenCmd: 8 K124_ASCM
SG_ GasRegenCmdActive : 0|1@0+ (1,0) [0|0] "" NEO
SG_ RollingCounter : 7|2@0+ (1,0) [0|0] "" NEO
SG_ GasRegenAlwaysOne3 : 23|1@0+ (1,0) [0|1] "" NEO
SG_ GasRegenCmd : 22|12@0+ (1,0) [0|0] "" NEO
SG_ GasRegenCmd : 8|14@0+ (1,0) [0|0] "" NEO
BO_ 717 ASCM_2CD: 5 K124_ASCM
@@ -370,6 +382,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 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 ManualMode 1 "Active" 0 "Inactive"
+5
View File
@@ -13,6 +13,11 @@ a.out
dist/
pandacan.egg-info/
obj/
!board/obj/
!board/obj/bootstub.panda_remote.bin
!board/obj/bootstub.panda_h7_remote.bin
!board/obj/panda_remote.bin.signed
!board/obj/panda_h7_remote.bin.signed
examples/output.csv
.DS_Store
.vscode*
+17 -4
View File
@@ -33,6 +33,7 @@ extern bool can_loopback;
// Ignition detected from CAN meessages
bool ignition_can = false;
uint32_t ignition_can_cnt = 0U;
extern bool gm_remote_start_boots_comma;
#define ALL_CAN_SILENT 0xFF
#define ALL_CAN_LIVE 0
@@ -202,10 +203,22 @@ void ignition_can_hook(CANPacket_t *to_push) {
int len = GET_LEN(to_push);
// GM exception
if ((addr == 0x1F1) && (len == 8)) {
// SystemPowerMode (2=Run, 3=Crank Request)
ignition_can = (GET_BYTE(to_push, 0) & 0x2U) != 0U;
ignition_can_cnt = 0U;
#ifdef PANDA_GM_REMOTE_START_C9
if (true) {
#else
if (gm_remote_start_boots_comma) {
#endif
if ((addr == 0xC9) && (len == 8)) {
// Matches SystemPowerMode (1=Run, 0=Off)
ignition_can = (GET_BYTE(to_push, 6) & 0x10U) != 0U;
ignition_can_cnt = 0U;
}
} else {
if ((addr == 0x1F1) && (len == 8)) {
// SystemPowerMode (2=Run, 3=Crank Request)
ignition_can = (GET_BYTE(to_push, 0) & 0x2U) != 0U;
ignition_can_cnt = 0U;
}
}
// Tesla exception
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
const uint8_t gitversion[] = "DEV-87d2f6ab-DEBUG";
const uint8_t gitversion[] = "DEV-4d1efbb2-DEBUG";
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) {
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
+130 -66
View File
@@ -9,43 +9,55 @@ const SteeringLimits GM_STEERING_LIMITS = {
.type = TorqueDriverLimited,
};
const SteeringLimits GM_BOLT_2017_STEERING_LIMITS = {
.max_steer = 450,
.max_rate_up = 15,
.max_rate_down = 34,
.driver_torque_allowance = 78,
.driver_torque_factor = 6,
.max_rt_delta = 345,
.max_rt_interval = 200000,
.type = TorqueDriverLimited,
};
const LongitudinalLimits GM_ASCM_LONG_LIMITS = {
.max_gas = 3072,
.min_gas = 1404,
.inactive_gas = 1404,
.max_gas = 8191,
.min_gas = 5500,
.inactive_gas = 5500,
.max_brake = 400,
};
const LongitudinalLimits GM_CAM_LONG_LIMITS = {
.max_gas = 3400,
.min_gas = 1514,
.inactive_gas = 1554,
.max_gas = 8848,
.min_gas = 5610,
.inactive_gas = 5650,
.max_brake = 400,
};
const SteeringLimits *gm_steer_limits;
const LongitudinalLimits *gm_long_limits;
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 = 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
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
{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
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_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
{0x315, 2, 5}, {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
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
// TODO: do checksum and counter checks. Add correct timestep, 0.1s for now.
@@ -62,13 +74,18 @@ RxCheck gm_rx_checks[] = {
const uint16_t GM_PARAM_HW_CAM = 1;
const uint16_t GM_PARAM_HW_CAM_LONG = 2;
const uint16_t GM_PARAM_HW_SDGM = 4;
const uint16_t GM_PARAM_CC_LONG = 8;
const uint16_t GM_PARAM_HW_ASCM_LONG = 16;
const uint16_t GM_PARAM_NO_CAMERA = 32;
const uint16_t GM_PARAM_NO_ACC = 64;
const uint16_t GM_PARAM_PEDAL_LONG = 128; // TODO: this can be inferred
const uint16_t GM_PARAM_PEDAL_INTERCEPTOR = 256;
const uint16_t GM_PARAM_CC_LONG = 4;
const uint16_t GM_PARAM_HW_ASCM_LONG = 8;
const uint16_t GM_PARAM_NO_CAMERA = 16;
const uint16_t GM_PARAM_NO_ACC = 32;
const uint16_t GM_PARAM_PEDAL_LONG = 64; // TODO: this can be inferred
const uint16_t GM_PARAM_PEDAL_INTERCEPTOR = 128;
const uint16_t GM_PARAM_ASCM_INT = 256;
const uint16_t GM_PARAM_FORCE_BRAKE_C9 = 512;
const uint16_t GM_PARAM_HW_SDGM = 1024;
const uint16_t GM_PARAM_BOLT_2017 = 2048;
const uint16_t GM_PARAM_BOLT_2022_PEDAL = 4096;
const uint16_t GM_PARAM_REMOTE_START_BOOTS_COMMA = 8192;
enum {
GM_BTN_UNPRESS = 1,
@@ -90,30 +107,12 @@ bool gm_pedal_long = false;
bool gm_cc_long = false;
bool gm_skip_relay_check = false;
bool gm_force_ascm = false;
static void handle_gm_wheel_buttons(const CANPacket_t *to_push) {
int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4;
// enter controls on falling edge of set or rising edge of resume (avoids fault)
bool set = (button != GM_BTN_SET) && (cruise_button_prev == GM_BTN_SET);
bool res = (button == GM_BTN_RESUME) && (cruise_button_prev != GM_BTN_RESUME);
if (set || res) {
controls_allowed = true;
}
// exit controls on cancel press
if (button == GM_BTN_CANCEL) {
controls_allowed = false;
}
cruise_button_prev = button;
}
bool gm_bolt_2022_pedal = false;
bool gm_ascm_int = false;
bool gm_force_brake_c9 = false;
bool gm_remote_start_boots_comma = false;
static void gm_rx_hook(const CANPacket_t *to_push) {
if ((GET_BUS(to_push) == 2U) && (GET_ADDR(to_push) == 0x1E1) && (gm_hw == GM_SDGM)) {
// SDGM buttons are on bus 2
handle_gm_wheel_buttons(to_push);
}
if (GET_BUS(to_push) == 0U) {
int addr = GET_ADDR(to_push);
@@ -131,19 +130,34 @@ static void gm_rx_hook(const CANPacket_t *to_push) {
vehicle_moving = (left_rear_speed > GM_STANDSTILL_THRSLD) || (right_rear_speed > GM_STANDSTILL_THRSLD);
}
// ACC steering wheel buttons (GM_CAM and GM_SDGM are tied to the PCM)
if ((addr == 0x1E1) && (!gm_pcm_cruise || gm_cc_long) && (gm_hw != GM_SDGM)) {
handle_gm_wheel_buttons(to_push);
// ACC steering wheel buttons (GM_CAM is tied to the PCM)
if ((addr == 0x1E1) && (!gm_pcm_cruise || gm_cc_long)) {
int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4;
// enter controls on falling edge of set or rising edge of resume (avoids fault)
bool set = (button != GM_BTN_SET) && (cruise_button_prev == GM_BTN_SET);
bool res = (button == GM_BTN_RESUME) && (cruise_button_prev != GM_BTN_RESUME);
if (set || res) {
controls_allowed = true;
}
// exit controls on cancel press
if (button == GM_BTN_CANCEL) {
controls_allowed = false;
}
cruise_button_prev = button;
}
// Reference for brake pressed signals:
// https://github.com/commaai/openpilot/blob/master/selfdrive/car/gm/carstate.py
if ((addr == 0xBE) && (gm_hw == GM_ASCM)) {
// Prefer 0xC9 (ECMEngineStatus) when gm_force_brake_c9 is set, otherwise keep legacy behavior.
if ((addr == 0xC9) && gm_force_brake_c9) {
brake_pressed = GET_BIT(to_push, 40U) != 0U;
} else if ((addr == 0xBE) && ((gm_hw == GM_ASCM) || (gm_hw == GM_SDGM))) {
brake_pressed = GET_BYTE(to_push, 1) >= 8U;
}
if ((addr == 0xC9) && ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM))) {
brake_pressed = GET_BIT(to_push, 40U);
} else if ((addr == 0xC9) && (gm_hw == GM_CAM)) {
brake_pressed = GET_BIT(to_push, 40U) != 0U;
}
if (addr == 0xC9) {
@@ -162,6 +176,11 @@ 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 && gm_bolt_2022_pedal) {
cruise_engaged_prev = false;
}
// Cruise check for CC only cars
if ((addr == 0x3D1) && !gm_has_acc) {
bool cruise_engaged = (GET_BYTE(to_push, 4) >> 7) != 0U;
@@ -192,6 +211,19 @@ static void gm_rx_hook(const CANPacket_t *to_push) {
}
generic_rx_checks(stock_ecu_detected);
}
// Cruise check for ASCMActiveCruiseControlStatus on bus 2.
// Keep kaofui behavior for non-Bolt paths; Bolt pedal path keeps local tracking.
if ((GET_ADDR(to_push) == 0x370) && (GET_BUS(to_push) == 2U)) {
bool cruise_engaged = (GET_BYTE(to_push, 2) >> 7) != 0U; // ACCCmdActive
if (gm_bolt_2022_pedal) {
cruise_engaged_prev = cruise_engaged;
} else if (gm_pcm_cruise && gm_has_acc) {
pcm_cruise_check(cruise_engaged);
} else {
cruise_engaged_prev = cruise_engaged;
}
}
}
static bool gm_tx_hook(const CANPacket_t *to_send) {
@@ -214,7 +246,7 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
bool steer_req = GET_BIT(to_send, 3U);
if (steer_torque_cmd_checks(desired_torque, steer_req, GM_STEERING_LIMITS)) {
if (steer_torque_cmd_checks(desired_torque, steer_req, *gm_steer_limits)) {
tx = false;
}
}
@@ -229,7 +261,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, 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;
// Allow apply bit in pre-enabled and overriding states
@@ -246,8 +278,11 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U;
bool allowed_btn = (button == GM_BTN_CANCEL) && cruise_engaged_prev;
// For standard CC, allow spamming of SET / RESUME
if (gm_cc_long) {
if (gm_hw == GM_CAM && enable_gas_interceptor && gm_bolt_2022_pedal && button == GM_BTN_CANCEL) {
allowed_btn = true;
}
// For CC_LONG or PCM cruise vehicles, allow SET/RESUME when cruise is engaged
if (gm_cc_long || gm_pcm_cruise) {
allowed_btn |= cruise_engaged_prev && (button == GM_BTN_SET || button == GM_BTN_RESUME || button == GM_BTN_UNPRESS);
}
@@ -256,6 +291,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;
}
@@ -272,10 +323,20 @@ static int gm_fwd_hook(int bus_num, int addr) {
}
if (bus_num == 2) {
// block lkas message and acc messages if gm_cam_long, forward all others
bool is_lkas_msg = (addr == 0x180);
bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370);
bool block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long);
bool block_msg = false;
if (gm_bolt_2022_pedal) {
// Block 0x370 only for experimental long without pedal interceptor
bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB);
if (gm_cam_long && !enable_gas_interceptor) {
is_acc_msg = is_acc_msg || (addr == 0x370);
}
block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long);
} else {
// block lkas message and acc messages if gm_cam_long, forward all others
bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370);
block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long);
}
if (!block_msg) {
bus_fwd = 0;
}
@@ -286,30 +347,35 @@ static int gm_fwd_hook(int bus_num, int addr) {
}
static safety_config gm_init(uint16_t param) {
if GET_FLAG(param, GM_PARAM_HW_CAM) {
gm_ascm_int = GET_FLAG(param, GM_PARAM_ASCM_INT);
if (GET_FLAG(param, GM_PARAM_HW_CAM)) {
gm_hw = GM_CAM;
} else if GET_FLAG(param, GM_PARAM_HW_SDGM) {
} else if (GET_FLAG(param, GM_PARAM_HW_SDGM)) {
gm_hw = GM_SDGM;
} else {
gm_hw = GM_ASCM;
}
gm_force_ascm = GET_FLAG(param, GM_PARAM_HW_ASCM_LONG);
gm_steer_limits = GET_FLAG(param, GM_PARAM_BOLT_2017) ? &GM_BOLT_2017_STEERING_LIMITS : &GM_STEERING_LIMITS;
if (gm_hw == GM_ASCM || gm_force_ascm) {
gm_long_limits = &GM_ASCM_LONG_LIMITS;
if (gm_hw == GM_ASCM || gm_force_ascm || gm_ascm_int) {
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 {
}
gm_pedal_long = GET_FLAG(param, GM_PARAM_PEDAL_LONG);
gm_cc_long = GET_FLAG(param, GM_PARAM_CC_LONG);
enable_gas_interceptor = GET_FLAG(param, GM_PARAM_PEDAL_INTERCEPTOR);
gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG) && !gm_cc_long;
gm_pcm_cruise = ((gm_hw == GM_CAM) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long) || (gm_hw == GM_SDGM);
gm_bolt_2022_pedal = GET_FLAG(param, GM_PARAM_BOLT_2022_PEDAL);
gm_pcm_cruise = (((gm_hw == GM_CAM) || (gm_hw == GM_SDGM)) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long);
gm_skip_relay_check = GET_FLAG(param, GM_PARAM_NO_CAMERA);
gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC);
enable_gas_interceptor = GET_FLAG(param, GM_PARAM_PEDAL_INTERCEPTOR);
gm_force_brake_c9 = GET_FLAG(param, GM_PARAM_FORCE_BRAKE_C9);
gm_remote_start_boots_comma = GET_FLAG(param, GM_PARAM_REMOTE_START_BOOTS_COMMA);
safety_config ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_ASCM_TX_MSGS);
if (gm_hw == GM_CAM) {
@@ -320,8 +386,6 @@ static safety_config gm_init(uint16_t param) {
} else {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS);
}
} else if (gm_hw == GM_SDGM) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_TX_MSGS);
}
return ret;
}
+31 -22
View File
@@ -231,13 +231,18 @@ class Panda:
FLAG_GM_HW_CAM = 1
FLAG_GM_HW_CAM_LONG = 2
FLAG_GM_HW_SDGM = 4
FLAG_GM_CC_LONG = 8
FLAG_GM_HW_ASCM_LONG = 16
FLAG_GM_NO_CAMERA = 32
FLAG_GM_NO_ACC = 64
FLAG_GM_PEDAL_LONG = 128 # TODO: This can be inferred
FLAG_GM_GAS_INTERCEPTOR = 256
FLAG_GM_CC_LONG = 4
FLAG_GM_HW_ASCM_LONG = 8
FLAG_GM_NO_CAMERA = 16
FLAG_GM_NO_ACC = 32
FLAG_GM_PEDAL_LONG = 64 # TODO: This can be inferred
FLAG_GM_GAS_INTERCEPTOR = 128
FLAG_GM_ASCM_INT = 256
FLAG_GM_FORCE_BRAKE_C9 = 512
FLAG_GM_HW_SDGM = 1024
FLAG_GM_BOLT_2017 = 2048
FLAG_GM_BOLT_2022_PEDAL = 4096
FLAG_GM_REMOTE_START_BOOTS_COMMA = 8192
FLAG_FORD_LONG_CONTROL = 1
FLAG_FORD_CANFD = 2
@@ -804,7 +809,8 @@ class Panda:
# The panda will NAK CAN writes when there is CAN congestion.
# libusb will try to send it again, with a max timeout.
# Timeout is in ms. If set to 0, the timeout is infinite.
CAN_SEND_TIMEOUT_MS = 10
CAN_SEND_TIMEOUT_MS = 5
CAN_MAX_RETRIES = 3
def can_reset_communications(self):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xc0, 0, 0, b'')
@@ -812,18 +818,18 @@ class Panda:
@ensure_can_packet_version
def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS):
snds = pack_can_buffer(arr)
while True:
try:
for tx in snds:
while True:
bs = self._handle.bulkWrite(3, tx, timeout=timeout)
tx = tx[bs:]
if len(tx) == 0:
break
logging.error("CAN: PARTIAL SEND MANY, RETRYING")
break
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
logging.error("CAN: BAD SEND MANY, RETRYING")
for tx in snds:
retries = 0
while len(tx) > 0:
bs = self._handle.bulkWrite(3, tx, timeout=timeout)
if bs == 0:
retries += 1
if retries > self.CAN_MAX_RETRIES:
logging.warning("CAN send: no progress after retries, dropping")
break
else:
retries = 0
tx = tx[bs:]
def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS):
self.can_send_many([[addr, None, dat, bus]], timeout=timeout)
@@ -831,13 +837,16 @@ class Panda:
@ensure_can_packet_version
def can_recv(self):
dat = bytearray()
while True:
for _ in range(self.CAN_MAX_RETRIES):
try:
dat = self._handle.bulkRead(1, 16384) # Max receive batch size + 2 extra reserve frames
break
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
logging.error("CAN: BAD RECV, RETRYING")
time.sleep(0.1)
time.sleep(0.01)
else:
logging.error("CAN: recv failed after retries")
return []
msgs, self.can_rx_overflow_buffer = unpack_can_buffer(self.can_rx_overflow_buffer + dat)
return msgs
+12 -1
View File
@@ -27,7 +27,10 @@ NACK = 0x1F
CHECKSUM_START = 0xAB
MIN_ACK_TIMEOUT_MS = 100
MAX_ACK_TIMEOUT_MS = 500 # like C++ SPI_ACK_TIMEOUT
DEFAULT_TIMEOUT_MS = 500 # default when timeout=0
MAX_XFER_RETRY_COUNT = 5
MAX_TIMEOUT_RETRIES = 5 # like C++
XFER_SIZE = 0x40*31
@@ -152,6 +155,8 @@ class PandaSpiHandle(BaseHandle):
return cksum
def _wait_for_ack(self, spi, ack_val: int, timeout: int, tx: int, length: int = 1) -> bytes:
# Original behavior preserved - timeout=0 means wait forever within this function
# The caller (_transfer) handles the overall timeout
timeout_s = max(MIN_ACK_TIMEOUT_MS, timeout) * 1e-3
start = time.monotonic()
@@ -225,10 +230,15 @@ class PandaSpiHandle(BaseHandle):
logging.debug("starting transfer: endpoint=%d, max_rx_len=%d", endpoint, max_rx_len)
logging.debug("==============================================")
# Fix timeout=0 infinite loop: default to DEFAULT_TIMEOUT_MS
if timeout == 0:
timeout = DEFAULT_TIMEOUT_MS
n = 0
start_time = time.monotonic()
exc = PandaSpiException()
while (timeout == 0) or (time.monotonic() - start_time) < timeout*1e-3:
# Use the timeout for the overall loop, matching original behavior but with timeout=0 fixed
while (time.monotonic() - start_time) < timeout * 1e-3:
n += 1
logging.debug("\ntry #%d", n)
with self.dev.acquire() as spi:
@@ -238,6 +248,7 @@ class PandaSpiHandle(BaseHandle):
exc = e
logging.debug("SPI transfer failed, retrying", exc_info=True)
logging.error("SPI transfer failed after %d tries, %.2fms", n, (time.monotonic() - start_time) * 1000)
raise exc
def get_protocol_version(self) -> bytes:
Binary file not shown.

Before

Width:  |  Height:  |  Size: 390 KiB

After

Width:  |  Height:  |  Size: 418 KiB

+130
View File
@@ -11,12 +11,14 @@ from openpilot.selfdrive.car.fingerprints import eliminate_incompatible_cars, al
from openpilot.selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
from openpilot.selfdrive.car.fw_versions import get_fw_versions_ordered, get_present_ecus, match_fw_to_car, set_obd_multiplexing
from openpilot.selfdrive.car.mock.values import CAR as MOCK
from openpilot.selfdrive.car.gm.values import CAR as GM_CAR, CanBus as GMCanBus
from openpilot.common.swaglog import cloudlog
import cereal.messaging as messaging
from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.system.version import get_build_metadata
FRAME_FINGERPRINT = 100 # 1s
SOURCE_BRANCH_FILE = "/data/media/0/starpilot_source_branch"
EventName = car.CarEvent.EventName
FrogPilotEventName = custom.FrogPilotCarEvent.EventName
@@ -188,6 +190,64 @@ def get_car_interface(CP, FPCP):
CarInterface, CarController, CarState = interfaces[CP.carFingerprint]
return CarInterface(CP, FPCP, CarController, CarState)
def get_cached_car_fingerprint(params: Params) -> str | None:
for key in ("CarParamsPersistent", "CarParamsCache", "CarParams"):
cp_bytes = params.get(key)
if cp_bytes is None:
continue
try:
with car.CarParams.from_bytes(cp_bytes) as cached_cp:
if cached_cp.carFingerprint:
return cached_cp.carFingerprint
except Exception:
continue
return None
def clear_stale_car_params(params: Params, candidate: str) -> None:
cached_fingerprint = get_cached_car_fingerprint(params)
if cached_fingerprint is None or cached_fingerprint == candidate:
return
stale_keys = (
"CarParams",
"CarParamsCache",
"CarParamsPersistent",
"FrogPilotCarParams",
"FrogPilotCarParamsPersistent",
"CarModelName",
)
for key in stale_keys:
params.remove(key)
cloudlog.warning("cleared stale car params after fingerprint change: %s -> %s", cached_fingerprint, candidate)
def migrate_legacy_bolt_candidate(candidate: str) -> str:
source_branch = ""
try:
with open(SOURCE_BRANCH_FILE, encoding="utf-8") as f:
source_branch = f.read().strip()
except OSError:
pass
migration_branch = source_branch or get_build_metadata().channel
replacements = {}
if migration_branch in {"TorqueTune", "TorquePedal"}:
replacements = {
"CHEVROLET_BOLT_EUV": GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
"CHEVROLET_BOLT_CC": GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
}
elif migration_branch in {"TotallyTune", "StarPilot-2017", "StarPilot 2017"}:
replacements = {
"CHEVROLET_BOLT_CC": GM_CAR.CHEVROLET_BOLT_CC_2017,
}
elif migration_branch in {"StarPilot"}:
replacements = {
"CHEVROLET_BOLT_CC": GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
}
normalized_candidate = candidate[4:] if candidate.startswith("CAR.") else candidate
return replacements.get(normalized_candidate, normalized_candidate)
def get_car(logcan, sendcan, experimental_long_allowed, params, num_pandas=1, frogpilot_toggles=None):
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
@@ -202,10 +262,80 @@ def get_car(logcan, sendcan, experimental_long_allowed, params, num_pandas=1, fr
params.put_nonblocking("CarMake", candidate.split('_')[0].title())
params.put_nonblocking("CarModel", candidate)
# Branch migration can leave legacy Bolt candidate names active in params/cache.
# Remap the selected candidate itself so fingerprint selection and params stay in sync.
migrated_candidate = migrate_legacy_bolt_candidate(candidate)
if candidate != migrated_candidate:
cloudlog.warning("legacy Bolt candidate migration: %s -> %s", candidate, migrated_candidate)
candidate = migrated_candidate
params.put_nonblocking("CarMake", candidate.split('_')[0].title())
params.put_nonblocking("CarModel", candidate)
params.remove("CarModelName")
# VIN-based Bolt year mapping (selfdrive-only, bolt variants only)
if not frogpilot_toggles.force_fingerprint and is_valid_vin(vin):
bolt_variants = {
"CHEVROLET_BOLT_EUV",
"CHEVROLET_BOLT_CC",
"CAR.CHEVROLET_BOLT_EUV",
"CAR.CHEVROLET_BOLT_CC",
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
GM_CAR.CHEVROLET_BOLT_CC_2017,
}
if candidate in bolt_variants:
year_code = vin[9:10]
year_map = {
"H": GM_CAR.CHEVROLET_BOLT_CC_2017, # 2017
"J": GM_CAR.CHEVROLET_BOLT_CC_2019_2021, # 2018
"K": GM_CAR.CHEVROLET_BOLT_CC_2019_2021, # 2019
"L": GM_CAR.CHEVROLET_BOLT_CC_2019_2021, # 2020
"M": GM_CAR.CHEVROLET_BOLT_CC_2019_2021, # 2021
"N": GM_CAR.CHEVROLET_BOLT_ACC_2022_2023, # 2022
"P": GM_CAR.CHEVROLET_BOLT_ACC_2022_2023, # 2023
}
if year_code in year_map:
vin_candidate = year_map[year_code]
if vin_candidate == GM_CAR.CHEVROLET_BOLT_ACC_2022_2023:
has_acc_msg = (
0x370 in fingerprints.get(GMCanBus.CAMERA, {}) or
0x370 in fingerprints.get(GMCanBus.POWERTRAIN, {})
)
has_pedal_msg = 0x201 in fingerprints.get(GMCanBus.POWERTRAIN, {})
if has_acc_msg:
vin_candidate = GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL if has_pedal_msg else GM_CAR.CHEVROLET_BOLT_ACC_2022_2023
else:
vin_candidate = GM_CAR.CHEVROLET_BOLT_CC_2022_2023
if candidate != vin_candidate:
prev_candidate = candidate
candidate = vin_candidate
params.put_nonblocking("CarMake", candidate.split('_')[0].title())
params.put_nonblocking("CarModel", candidate)
params.remove("CarModelName")
cloudlog.warning("VIN Bolt override: %s -> %s", prev_candidate, candidate)
# Always prefer live fingerprint naming for Bolt variants to avoid stale manual labels.
if candidate in {
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
GM_CAR.CHEVROLET_BOLT_CC_2017,
"CHEVROLET_BOLT_EUV",
"CHEVROLET_BOLT_CC",
"CAR.CHEVROLET_BOLT_EUV",
"CAR.CHEVROLET_BOLT_CC",
}:
params.remove("CarModelName")
if frogpilot_toggles.block_user:
candidate = MOCK.MOCK
sentry.capture_block()
clear_stale_car_params(params, candidate)
CarInterface, _, _ = interfaces[candidate]
CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed, frogpilot_toggles, docs=False)
FPCP = CarInterface.get_frogpilot_params(candidate, fingerprints, car_fw, CP, frogpilot_toggles)
+1 -1
View File
@@ -158,7 +158,7 @@ MIGRATION = {
"CADILLAC ESCALADE 2017": GM.CADILLAC_ESCALADE,
"CADILLAC ESCALADE ESV 2016": GM.CADILLAC_ESCALADE_ESV,
"CADILLAC ESCALADE ESV 2019": GM.CADILLAC_ESCALADE_ESV_2019,
"CHEVROLET BOLT EUV 2022": GM.CHEVROLET_BOLT_EUV,
"CHEVROLET BOLT EUV 2022": GM.CHEVROLET_BOLT_ACC_2022_2023,
"CHEVROLET SILVERADO 1500 2020": GM.CHEVROLET_SILVERADO,
"CHEVROLET EQUINOX 2019": GM.CHEVROLET_EQUINOX,
"CHEVROLET TRAILBLAZER 2021": GM.CHEVROLET_TRAILBLAZER,
+377 -51
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 openpilot.common.conversions import Conversions as CV
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 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 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, ASCM_INT, EV_CAR, CC_REGEN_PADDLE_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
@@ -22,7 +27,15 @@ 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
# 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
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
@@ -38,6 +51,11 @@ 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.
@@ -46,35 +64,126 @@ class CarController(CarControllerBase):
self.lka_icon_status_last = (False, False)
self.params = CarControllerParams(self.CP)
self.is_volt = self.CP.carFingerprint in (CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC)
self.pedal_scale = 1.0
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.malibu_cancel_phase = 0
self.malibu_cancel_last_ts = 0.0
self.malibu_cancel_frame = 0
self.malibu_button_phase = 0
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
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):
self.CS = CS
self.aego = CS.out.aEgo
actuators = CC.actuators
accel = brake_accel = actuators.accel
press_regen_paddle = False
kaofui_cars = SDGM_CAR | ASCM_INT | {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
CAR.CHEVROLET_VOLT_CC,
CAR.CHEVROLET_MALIBU_CC,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
volt_like = {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
CAR.CHEVROLET_VOLT_CC,
}
# 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
@@ -83,6 +192,129 @@ class CarController(CarControllerBase):
# Send CAN commands.
can_sends = []
paddle_sends = []
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
phase_map = gmcan.malibu_phase_map_for_acc(CS.cruise_buttons)
if phase_map and CS.steering_button_checksum in phase_map:
phase = (phase_map[CS.steering_button_checksum] + 1) % 4
self.malibu_cancel_phase = phase
self.malibu_button_phase = phase
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
# 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, self.CP))
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
# 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, self.CP))
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):
# 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, self.CP))
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
@@ -112,24 +344,35 @@ 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
@@ -141,21 +384,61 @@ class CarController(CarControllerBase):
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = int(min(-100 * frogpilot_toggles.stopAccel, self.params.MAX_BRAKE))
else:
# 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)))
if self.is_volt:
if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
volt_pitch_accel = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
else:
volt_pitch_accel = 0.0
aero_drag_accel = (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2) / self.mass
accel += aero_drag_accel + volt_pitch_accel
brake_accel = actuators.accel + aero_drag_accel + volt_pitch_accel * interp(CS.out.vEgo, BRAKE_PITCH_FACTOR_BP, BRAKE_PITCH_FACTOR_V)
accel = clip(accel, self.params.ACCEL_MIN, self.params.ACCEL_MAX)
brake_accel = clip(brake_accel, self.params.ACCEL_MIN, self.params.ACCEL_MAX)
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:
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)))
# Clamp within message-valid ranges to avoid ASCM faults from overshoot or rounding
self.apply_gas = int(round(clip(self.apply_gas, self.params.MAX_ACC_REGEN, self.params.MAX_GAS)))
self.apply_brake = int(round(clip(self.apply_brake, 0, self.params.MAX_BRAKE)))
if self.apply_brake > 0:
# Volt should never present positive torque alongside friction braking
self.apply_gas = self.params.INACTIVE_REGEN
else:
self.apply_gas = int(round(interp(accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
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
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
# 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 = 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:
# "Tap" the accelerator pedal to re-engage ACC
@@ -166,9 +449,18 @@ class CarController(CarControllerBase):
idx = (self.frame // 4) % 4
if self.CP.flags & GMFlags.CC_LONG.value:
if CC.longActive 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
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):
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
can_sends.append(gmcan.create_buttons_malibu(
self.packer_pt, CanBus.POWERTRAIN, CruiseButtons.DECEL_SET,
self.malibu_button_phase, CS.steering_button_prefix))
self.malibu_button_phase = (self.malibu_button_phase + 1) % 4
else:
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
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:
@@ -178,6 +470,8 @@ class CarController(CarControllerBase):
if self.CP.networkLocation == NetworkLocation.fwdCamera and self.CP.carFingerprint not in CC_ONLY_CAR:
at_full_stop = at_full_stop and stopping
friction_brake_bus = CanBus.POWERTRAIN
if self.CP.carFingerprint in SDGM_CAR:
friction_brake_bus = CanBus.CAMERA
if self.CP.autoResumeSng:
resume = actuators.longControlState != LongCtrlState.starting or CC.cruiseControl.resume
@@ -189,9 +483,10 @@ class CarController(CarControllerBase):
acc_engaged = CC.enabled
# 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,
include_always_one3=self.CP.carFingerprint in kaofui_cars))
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
@@ -202,32 +497,57 @@ class CarController(CarControllerBase):
accel += self.accel_g
# 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:
tt = self.frame * DT_CTRL
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))
send_adas = True
if self.CP.carFingerprint in kaofui_cars:
send_adas = (self.CP.networkLocation != NetworkLocation.fwdCamera) and (self.CP.carFingerprint not in SDGM_CAR)
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 send_adas:
tt = self.frame * DT_CTRL
if self.CP.carFingerprint in kaofui_cars:
time_and_headlights_step = 10
speed_and_accelerometer_step = 2
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))
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))
else:
time_and_headlights_step = 20
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))
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 == 0:
if self.CP.networkLocation == NetworkLocation.gateway and (self.frame % (self.params.ADAS_KEEPALIVE_STEP if self.CP.carFingerprint in kaofui_cars else self.params.ADAS_KEEPALIVE_STEP * 2)) == 0:
can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN)
# TODO: integrate this with the code block below?
stock_cc_active = CS.out.cruiseState.enabled or CS.pcm_acc_status != AccState.OFF
if self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
stock_cc_active = CS.out.cruiseState.enabled
if (
(self.CP.flags & GMFlags.PEDAL_LONG.value) # Always cancel stock CC when using pedal interceptor
or (self.CP.flags & GMFlags.CC_LONG.value and not CC.enabled) # Cancel stock CC if OP is not active
) and CS.out.cruiseState.enabled:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
self.last_button_frame = self.frame
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
) and stock_cc_active:
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
# Match 33 Hz cadence (every 3 frames) and align phase to the last seen checksum.
if self.malibu_cancel_frame % 3 == 0:
can_sends.append(gmcan.create_buttons_malibu_cancel(
CanBus.POWERTRAIN, self.malibu_cancel_phase, CS.steering_button_prefix))
self.malibu_cancel_phase = (self.malibu_cancel_phase + 1) % 4
self.malibu_cancel_frame += 1
else:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
self.last_button_frame = self.frame
cancel_bus = CanBus.CAMERA if self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL else CanBus.POWERTRAIN
can_sends.append(gmcan.create_buttons(self.packer_pt, cancel_bus, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
else:
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
@@ -238,7 +558,13 @@ class CarController(CarControllerBase):
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
if self.cancel_counter > CAMERA_CANCEL_DELAY_FRAMES:
self.last_button_frame = self.frame
if self.CP.carFingerprint in SDGM_CAR:
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
if self.malibu_cancel_frame % 3 == 0:
can_sends.append(gmcan.create_buttons_malibu_cancel(
CanBus.POWERTRAIN, self.malibu_cancel_phase, CS.steering_button_prefix))
self.malibu_cancel_phase = (self.malibu_cancel_phase + 1) % 4
self.malibu_cancel_frame += 1
elif self.CP.carFingerprint in SDGM_CAR and self.CP.carFingerprint not in (volt_like | {CAR.CHEVROLET_BLAZER, CAR.CHEVROLET_MALIBU_SDGM, CAR.CHEVROLET_TRAVERSE}):
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, CS.buttons_counter, CruiseButtons.CANCEL))
else:
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, CS.buttons_counter, CruiseButtons.CANCEL))
+86 -21
View File
@@ -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
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, ASCM_INT, CAR
TransmissionType = car.CarParams.TransmissionType
NetworkLocation = car.CarParams.NetworkLocation
@@ -26,6 +26,8 @@ class CarState(CarStateBase):
self.pt_lka_steering_cmd_counter = 0
self.cam_lka_steering_cmd_counter = 0
self.buttons_counter = 0
self.steering_button_checksum = 0
self.steering_button_prefix = 0x01
self.prev_distance_button = 0
self.distance_button = 0
@@ -36,13 +38,25 @@ class CarState(CarStateBase):
def update(self, pt_cp, cam_cp, loopback_cp, frogpilot_toggles):
ret = car.CarState.new_message()
fp_ret = custom.FrogPilotCarState.new_message()
volt_like = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC}
kaofui_state_cars = volt_like | SDGM_CAR | ASCM_INT | {
CAR.CHEVROLET_BLAZER,
CAR.CHEVROLET_MALIBU_SDGM,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
sdgm_non_volt = self.CP.carFingerprint in SDGM_CAR and \
self.CP.carFingerprint not in kaofui_state_cars
self.prev_cruise_buttons = self.cruise_buttons
self.prev_distance_button = self.distance_button
if self.CP.carFingerprint not in SDGM_CAR:
if not sdgm_non_volt:
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
self.distance_button = pt_cp.vl["ASCMSteeringButton"]["DistanceButton"]
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
self.steering_button_checksum = pt_cp.vl["ASCMSteeringButton"]["SteeringButtonChecksum"]
acc_always_one = pt_cp.vl["ASCMSteeringButton"]["ACCAlwaysOne"]
acc_hidden_bit = pt_cp.vl["ASCMSteeringButton"].get("ACCHiddenBit", 0)
self.steering_button_prefix = (int(acc_always_one) & 1) | ((int(acc_hidden_bit) & 1) << 6)
else:
self.cruise_buttons = cam_cp.vl["ASCMSteeringButton"]["ACCButtons"]
self.distance_button = cam_cp.vl["ASCMSteeringButton"]["DistanceButton"]
@@ -56,6 +70,13 @@ class CarState(CarStateBase):
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"]
# 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
if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value:
self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
self.cam_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
@@ -77,26 +98,42 @@ class CarState(CarStateBase):
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
if self.CP.carFingerprint in kaofui_state_cars:
ret.brake = pt_cp.vl.get("EBCMBrakePedalPosition", {}).get("BrakePedalPosition", 0) / 0xd0
else:
ret.brake = pt_cp.vl["EBCMBrakePedalPosition"]["BrakePedalPosition"] / 0xd0
else:
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"]
if self.CP.networkLocation == NetworkLocation.fwdCamera:
if self.CP.carFingerprint in kaofui_state_cars:
ret.brake = pt_cp.vl.get("ECMAcceleratorPos", {}).get("BrakePedalPos", 0)
else:
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"]
if self.CP.carFingerprint == CAR.CHEVROLET_BLAZER:
# Blazer can miss light taps on analog threshold; include digital brake switch.
ret.brakePressed = (pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0) or (ret.brake >= 0.7)
elif self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_CC:
# Malibu CC: keep strict opgm behavior using BrakePedalPos >= 8.
ret.brakePressed = ret.brake >= 8
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or (self.CP.networkLocation == NetworkLocation.fwdCamera):
ret.brakePressed = pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0
else:
# Some Volt 2016-17 have loose brake pedal push rod retainers which causes the ECM to believe
# that the brake is being intermittently pressed without user interaction.
# To avoid a cruise fault we need to use a conservative brake position threshold
# https://static.nhtsa.gov/odi/tsbs/2017/MC-10137629-9999.pdf
ret.brakePressed = ret.brake >= 8
analog_thresh = 0.15 if (self.CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value) else 8
ret.brakePressed = ret.brake >= analog_thresh
# 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)
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_ACC_2022_2023, CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, CAR.CHEVROLET_BOLT_CC_2022_2023) and self.CP.enableGasInterceptor))
if self.CP.enableGasInterceptor:
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
else:
ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254.
@@ -113,7 +150,7 @@ class CarState(CarStateBase):
ret.steerFaultTemporary = self.lkas_status == 2
ret.steerFaultPermanent = self.lkas_status == 3
if self.CP.carFingerprint not in SDGM_CAR:
if not sdgm_non_volt:
# 1 - open, 0 - closed
ret.doorOpen = (pt_cp.vl["BCMDoorBeltStatus"]["FrontLeftDoor"] == 1 or
pt_cp.vl["BCMDoorBeltStatus"]["FrontRightDoor"] == 1 or
@@ -149,20 +186,30 @@ class CarState(CarStateBase):
if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value:
if self.CP.carFingerprint not in CC_ONLY_CAR:
ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS
if self.CP.carFingerprint not in SDGM_CAR:
if self.CP.carFingerprint not in (SDGM_CAR | ASCM_INT):
ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0
else:
ret.stockAeb = False
# openpilot controls nonAdaptive when not pcmCruise
if self.CP.pcmCruise:
if self.CP.pcmCruise and self.CP.carFingerprint not in ASCM_INT:
ret.cruiseState.nonAdaptive = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCruiseState"] not in (2, 3)
if self.CP.carFingerprint in CC_ONLY_CAR:
ret.accFaulted = False
ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
if self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
try:
ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0
except:
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
else:
# Most CC paths use ECM first.
try:
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
except:
ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0
if self.CP.enableBsm:
if self.CP.carFingerprint not in SDGM_CAR:
if not sdgm_non_volt:
ret.leftBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1
ret.rightBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1
else:
@@ -171,7 +218,7 @@ class CarState(CarStateBase):
# FrogPilot CarState functions
self.lkas_previously_enabled = self.lkas_enabled
if self.CP.carFingerprint in SDGM_CAR:
if sdgm_non_volt:
self.lkas_enabled = cam_cp.vl["ASCMSteeringButton"]["LKAButton"]
else:
self.lkas_enabled = pt_cp.vl["ASCMSteeringButton"]["LKAButton"]
@@ -186,10 +233,18 @@ class CarState(CarStateBase):
def get_cam_can_parser(CP, FPCP):
messages = []
if CP.networkLocation == NetworkLocation.fwdCamera and not CP.flags & GMFlags.NO_CAMERA.value:
volt_like = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC}
kaofui_state_cars = volt_like | SDGM_CAR | ASCM_INT | {
CAR.CHEVROLET_BLAZER,
CAR.CHEVROLET_MALIBU_SDGM,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
sdgm_non_volt = CP.carFingerprint in SDGM_CAR and \
CP.carFingerprint not in kaofui_state_cars
messages += [
("ASCMLKASteeringCmd", 10),
]
if CP.carFingerprint in SDGM_CAR:
if sdgm_non_volt:
messages += [
("BCMTurnSignals", 1),
("BCMDoorBeltStatus", 10),
@@ -198,11 +253,11 @@ class CarState(CarStateBase):
]
if CP.enableBsm:
messages.append(("BCMBlindSpotMonitor", 10))
else:
elif CP.carFingerprint not in (SDGM_CAR | ASCM_INT):
messages += [
("AEBCmd", 10),
]
if CP.carFingerprint not in CC_ONLY_CAR:
if CP.carFingerprint not in CC_ONLY_CAR or CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
messages += [
("ASCMActiveCruiseControlStatus", 25),
]
@@ -222,15 +277,24 @@ class CarState(CarStateBase):
("SportMode", 0),
]
if CP.carFingerprint in SDGM_CAR:
volt_like = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC}
kaofui_state_cars = volt_like | SDGM_CAR | ASCM_INT | {
CAR.CHEVROLET_BLAZER,
CAR.CHEVROLET_MALIBU_SDGM,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
prndl2_rate = 10 if CP.carFingerprint in kaofui_state_cars else 40
sdgm_non_volt = CP.carFingerprint in SDGM_CAR and \
CP.carFingerprint not in kaofui_state_cars
if sdgm_non_volt:
messages += [
("ECMPRDNL2", 40),
("ECMPRDNL2", prndl2_rate),
("AcceleratorPedal2", 40),
("ECMEngineStatus", 80),
]
else:
messages += [
("ECMPRDNL2", 10),
("ECMPRDNL2", prndl2_rate),
("AcceleratorPedal2", 33),
("ECMEngineStatus", 100),
("BCMTurnSignals", 1),
@@ -251,8 +315,9 @@ class CarState(CarStateBase):
messages.append(("EBCMBrakePedalPosition", 100))
if CP.transmissionType == TransmissionType.direct:
regen_paddle_rate = 50 if CP.carFingerprint in kaofui_state_cars else 40
messages += [
("EBCMRegenPaddle", 50),
("EBCMRegenPaddle", regen_paddle_rate),
("EVDriveMode", 0),
]
+62 -17
View File
@@ -26,6 +26,20 @@ FINGERPRINTS = {
{
170: 8, 171: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1930: 7, 2017: 8, 2020: 8, 2025: 8, 2028: 8
}],
CAR.CHEVROLET_VOLT_ASCM: [
# Causes errors with normal OBD install
# {
# 189: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 528: 4, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 767: 4, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1922: 7, 1930: 7
# }
],
CAR.CHEVROLET_VOLT_CAMERA: [
# Volt Premier 2017 w/ flashed firmware, cam harness + pedal (no 0x170/0x171 on PT bus)
{
# 189: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 513: 6, 528: 4, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1922: 7
}],
CAR.GMC_ACADIA_ASCM: [
# Causes errors with normal OBD install
],
CAR.BUICK_LACROSSE: [{
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 510: 8, 528: 5, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 5, 707: 8, 753: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 872: 1, 882: 8, 890: 1, 892: 2, 893: 1, 894: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1914: 7, 1916: 7, 1918: 7, 1919: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
}],
@@ -52,6 +66,9 @@ FINGERPRINTS = {
CAR.CHEVROLET_MALIBU: [{
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1930: 7, 2016: 8, 2024: 8
}],
CAR.CHEVROLET_MALIBU_ASCM: [{
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1930: 7, 2016: 8, 2024: 8
}],
CAR.GMC_ACADIA: [{
190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 7, 368: 8, 381: 8, 384: 8, 386: 8, 388: 8, 393: 8, 398: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 458: 8, 460: 4, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 512: 3, 530: 8, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 568: 2, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 801: 8, 803: 8, 804: 3, 805: 8, 832: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1918: 7, 1919: 7, 1920: 7, 1930: 7
},
@@ -67,14 +84,34 @@ FINGERPRINTS = {
CAR.CADILLAC_ESCALADE_ESV_2019: [{
715: 8, 840: 5, 717: 5, 869: 4, 880: 6, 289: 8, 454: 8, 842: 5, 460: 5, 463: 3, 801: 8, 170: 8, 190: 6, 241: 6, 201: 8, 417: 7, 211: 2, 419: 1, 398: 8, 426: 7, 487: 8, 442: 8, 451: 8, 452: 8, 453: 6, 479: 3, 311: 8, 500: 6, 647: 6, 193: 8, 707: 8, 197: 8, 209: 7, 199: 4, 455: 7, 313: 8, 481: 7, 485: 8, 489: 8, 249: 8, 393: 7, 407: 7, 413: 8, 422: 4, 431: 8, 501: 8, 499: 3, 810: 8, 508: 8, 381: 8, 462: 4, 532: 6, 562: 8, 386: 8, 761: 7, 573: 1, 554: 3, 719: 5, 560: 8, 1279: 4, 388: 8, 288: 5, 1005: 6, 497: 8, 844: 8, 961: 8, 967: 4, 977: 8, 979: 8, 985: 5, 1001: 8, 1017: 8, 1019: 2, 1020: 8, 1217: 8, 510: 8, 866: 4, 304: 1, 969: 8, 384: 4, 1033: 7, 1009: 8, 1034: 7, 1296: 4, 1930: 7, 1105: 5, 1013: 5, 1225: 7, 1919: 7, 320: 3, 534: 2, 352: 5, 298: 8, 1223: 2, 1233: 8, 608: 8, 1265: 8, 609: 6, 1267: 1, 1417: 8, 610: 6, 1906: 7, 611: 6, 612: 8, 613: 8, 208: 8, 564: 5, 309: 8, 1221: 5, 1280: 4, 1249: 8, 1907: 7, 1257: 6, 1300: 8, 1920: 7, 563: 5, 1322: 6, 1323: 4, 1328: 4, 1917: 7, 328: 1, 1912: 7, 1914: 7, 804: 3, 1918: 7
}],
CAR.CHEVROLET_BOLT_EUV: [{
CAR.CHEVROLET_BOLT_ACC_2022_2023: [{
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
}],
CAR.CHEVROLET_BOLT_CC: [
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL: [{
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
}],
CAR.CHEVROLET_BOLT_CC_2022_2023: [{
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
}],
CAR.CHEVROLET_BOLT_CC_2017: [
# Bolt Premier w/o ACC 2017
{
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
},
# Bolt EV Premier 2017
{
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
},
# Bolt EV Premier 2017 w Pedal
{ # pylint: disable=duplicate-key
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 512: 6, 513: 6, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8 # pylint: disable=duplicate-key # noqa: F601
},
# Bolt EV Premier 2017 2 w Pedal
{
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
}],
CAR.CHEVROLET_BOLT_CC_2019_2021: [
# Chevy Bolt EV 2019-2021
# Bolt Premier no ACC 2018 + Pedal
{
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 577: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1616: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 2020: 8, 2023: 8, 2028: 8, 2031: 8
@@ -95,18 +132,6 @@ FINGERPRINTS = {
{
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 368: 3, 381: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 512: 6, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 568: 2, 569: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 872: 1, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1037: 5, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
},
# Bolt EV Premier 2017
{
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
},
# Bolt EV Premier 2017 w Pedal
{ # pylint: disable=duplicate-key
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 512: 6, 513: 6, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8 # pylint: disable=duplicate-key # noqa: F601
},
# Bolt EV Premier 2017 2 w Pedal
{
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
},
# Bolt EV Premier no ACC 2023
{
170: 8, 188: 8, 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 308: 4, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 398: 8, 407: 7, 417: 8, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 8, 567: 5, 568: 2, 569: 3, 573: 1, 577: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 711: 6, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1010: 8, 1013: 6, 1015: 1, 1017: 8, 1019: 2, 1020: 8, 1037: 5, 1105: 5, 1187: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1601: 8, 1616: 8, 1618: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 1930: 7, 2016: 8, 2020: 8, 2023: 8, 2024: 8, 2028: 8, 2031: 8
@@ -178,31 +203,51 @@ FINGERPRINTS = {
CAR.CADILLAC_XT4: [
# Cadillac XT4 w/ ACC 2023
{
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 761: 7, 806: 1, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1517: 8, 1601: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 8, 1924: 8, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2016: 8, 2017: 8, 2018: 8, 2020: 8, 2021: 8, 2024: 8, 2026: 8
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 761: 7, 767: 4, 806: 1, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1517: 8, 1601: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 8, 1924: 8, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2016: 8, 2017: 8, 2018: 8, 2020: 8, 2021: 8, 2024: 8, 2026: 8
}],
CAR.CADILLAC_XT5_CC: [
# TRain's 2017 XT5
{
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 647: 3, 707: 8, 717: 5, 723: 2, 753: 5, 761: 7, 800: 6, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1914: 7, 1919: 7, 1920: 7
}],
CAR.CADILLAC_XT6: [
#{}
],
CAR.CHEVROLET_BLAZER: [{
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 289: 8, 298: 8, 304: 3, 309: 8, 313: 8, 322: 7, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 532: 6, 560: 8, 562: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 767: 4, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1296: 4
}],
CAR.CHEVROLET_TRAVERSE: [
# Chevy Traverse w/ ACC 2023
{
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 573: 1, 577: 8, 578: 8, 579: 8, 587: 8, 603: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 723: 4, 730: 4, 753: 5, 761: 7, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1346: 8, 1347: 8, 1355: 8, 1362: 8, 1417: 8, 1512: 8, 1514: 8, 1601: 8, 1602: 8, 1603: 7, 1609: 8, 1611: 8, 1613: 8, 1618: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 7, 1927: 8, 1930: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2004: 8, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2024: 8, 2026: 8
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 573: 1, 577: 8, 578: 8, 579: 8, 587: 8, 603: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 723: 4, 730: 4, 753: 5, 761: 7, 767: 4, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1346: 8, 1347: 8, 1355: 8, 1362: 8, 1417: 8, 1512: 8, 1514: 8, 1601: 8, 1602: 8, 1603: 7, 1609: 8, 1611: 8, 1613: 8, 1618: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 7, 1927: 8, 1930: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2004: 8, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2024: 8, 2026: 8
}],
CAR.CHEVROLET_MALIBU_SDGM: [
# Chevy Malibu w/ SDGM Harness 2019
{
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 5, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 707: 8, 715: 8, 717: 5, 761: 7, 767: 4, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 882: 8, 890: 1, 892: 2, 893: 2, 894: 1, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1353: 8, 1355: 8, 1611: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1843: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1916: 7, 1920: 8, 1927: 8, 1930: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2002: 8, 2004: 8, 2017: 8, 2018: 8, 2020: 8
}],
CAR.BUICK_BABYENCLAVE: [
# Buick Baby Enclave w/ ACC 2020-23
{
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 394: 7, 398: 8, 401: 8, 405: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 450: 4, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 456: 8, 457: 6, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 723: 4, 730: 4, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 872: 1, 880: 6, 882: 8, 890: 1, 892: 2, 893: 2, 894: 1, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1201: 3, 1217: 8, 1218: 3, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1514: 8, 1517: 8, 1601: 8, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1914: 7, 1916: 7, 1919: 7, 1927: 7, 1930: 7, 2018: 8, 2020: 8, 2021: 8, 2028: 8
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 394: 7, 398: 8, 401: 8, 405: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 450: 4, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 456: 8, 457: 6, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 723: 4, 730: 4, 761: 7, 767: 4, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 872: 1, 880: 6, 882: 8, 890: 1, 892: 2, 893: 2, 894: 1, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1201: 3, 1217: 8, 1218: 3, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1514: 8, 1517: 8, 1601: 8, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1914: 7, 1916: 7, 1919: 7, 1927: 7, 1930: 7, 2018: 8, 2020: 8, 2021: 8, 2028: 8
}],
CAR.CHEVROLET_MALIBU_CC: [
{
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 328: 1, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 409: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 730: 4, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1037: 5, 1105: 5, 1187: 6, 1189: 1, 1195: 3, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7
}],
CAR.CHEVROLET_MALIBU_HYBRID_CC: [
{
193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 352: 5, 386: 8, 451: 8, 452: 8, 453: 6, 481: 7, 485: 8, 489: 8, 493: 8, 500: 6, 560: 8, 562: 8, 566: 6, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 717: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4
}],
CAR.CHEVROLET_TRAX: [
{
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7
}],
CAR.CHEVROLET_VOLT_2019: [
# Chevy Volt w/ ACC 2019
{
170: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 573: 1, 577: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 715: 8, 717: 5, 761: 7, 767: 4, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1268: 2, 1273: 3, 1275: 3, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1513: 8, 1516: 8, 1517: 8, 1601: 8, 1609: 8, 1611: 8, 1618: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1920: 8, 1922: 7, 1927: 7, 1930: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2004: 8, 2017: 8, 2018: 8, 2020: 8, 2021: 8, 2023: 8, 2025: 8, 2028: 8, 2031: 8
}],
}
FW_VERSIONS: dict[str, dict[tuple, list[bytes]]] = {
+123 -27
View File
@@ -6,6 +6,56 @@ from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.car import make_can_msg
from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CanBus
MALIBU_BUTTON_TABLE = {
0: [0x2FBC, 0x25DE, 0x15EE, 0x1FCC],
1: [0x55AE, 0x5F8C, 0x6F7C, 0x659E],
4: [0x2ACD, 0x20EF, 0x1ADD, 0x10FF],
5: [0x50BF, 0x5A9D, 0x60AF, 0x6A8D],
}
MALIBU_BUTTON_MAP = {
CruiseButtons.UNPRESS: 0,
CruiseButtons.RES_ACCEL: 1,
CruiseButtons.MAIN: 4,
CruiseButtons.CANCEL: 5,
}
def malibu_phase_map_for_button(button):
key = MALIBU_BUTTON_MAP.get(button, None)
if key is None or key not in MALIBU_BUTTON_TABLE:
return None
return {v: i for i, v in enumerate(MALIBU_BUTTON_TABLE[key])}
def malibu_phase_map_for_acc(acc_value):
seq = MALIBU_BUTTON_TABLE.get(acc_value)
if not seq:
return None
return {v: i for i, v in enumerate(seq)}
def create_buttons_malibu(packer, bus, button, phase, prefix=0x41):
key = MALIBU_BUTTON_MAP.get(button, None)
if key is None or key not in MALIBU_BUTTON_TABLE:
# fallback to standard checksum for unsupported buttons
return create_buttons(packer, bus, 0, button)
values = {
"ACCButtons": button,
"RollingCounter": 0,
"ACCAlwaysOne": 1,
"DistanceButton": 0,
}
dat = packer.make_can_msg("ASCMSteeringButton", bus, values)[2]
data = bytearray(dat)
data[3] = prefix & 0xFF
seq = MALIBU_BUTTON_TABLE[key]
val = seq[phase % len(seq)]
data[5] = (val >> 8) & 0xFF
data[6] = val & 0xFF
return make_can_msg(0x1e1, bytes(data), bus)
def create_buttons(packer, bus, idx, button):
values = {
@@ -23,6 +73,17 @@ def create_buttons(packer, bus, idx, button):
values["SteeringButtonChecksum"] = checksum
return packer.make_can_msg("ASCMSteeringButton", bus, values)
def create_buttons_malibu_cancel(bus, phase, prefix=0x41):
# Malibu Hybrid CC cancel frames use a 4-value pattern in the last 2 bytes.
data = bytearray(7)
data[3] = prefix & 0xFF
data[4] = 0x00
cancel_bytes = (0x60, 0xAF, 0x65, 0x9E, 0x6A, 0x8D, 0x6F, 0x7C)
idx = ((phase + 2) % 4) * 2
data[5] = cancel_bytes[idx]
data[6] = cancel_bytes[idx + 1]
return make_can_msg(0x1e1, bytes(data), bus)
def create_pscm_status(packer, bus, pscm_status):
values = {s: pscm_status[s] for s in [
@@ -57,7 +118,7 @@ def create_adas_keepalive(bus):
return [make_can_msg(0x409, dat, bus), make_can_msg(0x40a, dat, bus)]
def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop):
def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop, include_always_one3=False):
values = {
"GasRegenCmdActive": enabled,
"RollingCounter": idx,
@@ -66,8 +127,9 @@ def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop):
"GasRegenFullStopActive": at_full_stop,
"GasRegenAlwaysOne": 1,
"GasRegenAlwaysOne2": 1,
"GasRegenAlwaysOne3": 1,
}
if include_always_one3:
values["GasRegenAlwaysOne3"] = 1
dat = packer.make_can_msg("ASCMGasRegenCmd", bus, values)[2]
values["GasRegenChecksum"] = (((0xff - dat[1]) & 0xff) << 16) | \
@@ -81,7 +143,7 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_s
mode = 0x1
# TODO: Understand this better. Volts and ICE Camera ACC cars are 0x1 when enabled with no brake
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_EUV,):
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023,):
mode = 0x9
if apply_brake > 0:
@@ -177,47 +239,81 @@ def create_lka_icon_command(bus, active, critical, steer):
dat = b"\x00\x00\x00"
return make_can_msg(0x104c006c, dat, bus)
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
def create_prndl2_command(packer, bus, press_regen_paddle, CP):
if CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, CAR.CHEVROLET_BOLT_CC_2022_2023):
prndl2_value = 5 if press_regen_paddle else 6
else:
_CV = CV.MS_TO_MPH
RATE_UP_MAX = 0.2
RATE_DOWN_MAX = 0.2
prndl2_value = 7 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)
accel = actuators.accel * _CV # m/s/s to mph/s
speedSetPoint = int(round(CS.out.cruiseState.speed * _CV))
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, frogpilot_toggles):
accel = actuators.accel
Vego = CS.out.vEgo
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
controller.apply_speed = 0
rate = 0.04
elif accel < 0:
elif DesiredSetPoint < speedSetPoint and speedSetPoint > CS.CP.minEnableSpeed * MS_CONVERT + 1:
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 accel > 0:
elif DesiredSetPoint > speedSetPoint:
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
if CS.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
phase_map = malibu_phase_map_for_button(cruiseBtn)
if phase_map:
msgs = [create_buttons_malibu(packer, CanBus.POWERTRAIN, cruiseBtn, controller.malibu_button_phase,
CS.steering_button_prefix)]
controller.malibu_button_phase = (controller.malibu_button_phase + 1) % 4
return msgs
idx = (CS.buttons_counter + 1) % 4 # Need to predict the next idx for '22-23 EUV
return [create_buttons(packer, CanBus.POWERTRAIN, idx, cruiseBtn)]
else:
+307 -78
View File
@@ -7,8 +7,8 @@ from panda import Panda
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car import create_button_events, get_safety_config
from openpilot.selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG
from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR, SDGM_CAR
from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LateralAccelFromTorqueCallbackType
from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR, SDGM_CAR, ASCM_INT, set_red_panda_canbus
from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LateralAccelFromTorqueCallbackType, get_friction_threshold
from openpilot.selfdrive.controls.lib.drive_helpers import get_friction
ButtonType = car.CarState.ButtonEvent.Type
@@ -26,15 +26,56 @@ CAM_MSG = 0x320 # AEBCmd
# TODO: Is this always linked to camera presence?
ACCELERATOR_POS_MSG = 0xbe
VOLT_LIKE_CARS = {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_CC,
CAR.CHEVROLET_VOLT_CAMERA,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_MALIBU,
CAR.CHEVROLET_MALIBU_ASCM,
CAR.CHEVROLET_MALIBU_SDGM,
CAR.CHEVROLET_MALIBU_CC,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
NON_LINEAR_TORQUE_PARAMS = {
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]
CAR.CHEVROLET_BOLT_ACC_2022_2023: {
"left": [2.6531724862969748, 1.1, 0.1919764879840985, 0.0],
"right": [2.7031724862969748, 1.0, 0.1469764879840985, 0.0],
},
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL: {
"left": [2.6531724862969748, 1.1, 0.1919764879840985, 0.0],
"right": [2.7031724862969748, 1.0, 0.1469764879840985, 0.0],
},
CAR.CHEVROLET_BOLT_CC_2022_2023: {
"left": [2.6531724862969748, 1.1, 0.1919764879840985, 0.0],
"right": [2.7031724862969748, 1.0, 0.1469764879840985, 0.0],
},
CAR.CHEVROLET_BOLT_CC_2019_2021: {
"left": [1.8, 1.1, 0.27, 0.0],
"right": [2.0, 1.0, 0.205, 0.0],
},
CAR.CHEVROLET_BOLT_CC_2017: {
"left": [2.15, 1.0, 0.21, 0.0],
"right": [2.15, 1.0, 0.21, 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],
},
}
class CarInterface(CarInterfaceBase):
def __init__(self, CP, FPCP, CarController, CarState):
super().__init__(CP, FPCP, CarController, CarState)
self.steer_offset = 0.0
@staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed):
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
@@ -47,7 +88,7 @@ class CarInterface(CarInterfaceBase):
return 0.10006696 * sigmoid * (v_ego + 3.12485927)
def get_steer_feedforward_function(self):
if self.CP.carFingerprint in (CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_CC):
if self.CP.carFingerprint in VOLT_LIKE_CARS:
return self.get_steer_feedforward_volt
else:
return CarInterfaceBase.get_steer_feedforward_default
@@ -60,10 +101,12 @@ 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"
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 = 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)
lataccel_values = np.arange(-5.0, 5.0, 0.01)
@@ -76,105 +119,234 @@ class CarInterface(CarInterfaceBase):
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
def torque_from_lateral_accel_siglin(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning):
return np.interp(lateral_acceleration, lataccel_values, torque_values)
return float(np.interp(lateral_acceleration, lataccel_values, torque_values) + self.steer_offset)
return torque_from_lateral_accel_siglin
else:
return self.torque_from_lateral_accel_linear
def torque_from_lateral_accel_linear(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning):
return self.torque_from_lateral_accel_linear(lateral_acceleration, torque_params) + self.steer_offset
return torque_from_lateral_accel_linear
def lateral_accel_from_torque(self) -> LateralAccelFromTorqueCallbackType:
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
def lateral_accel_from_torque_siglin(torque: float, torque_params: car.CarParams.LateralTorqueTuning):
return np.interp(torque, torque_values, lataccel_values)
return np.interp(torque - self.steer_offset, torque_values, lataccel_values)
return lateral_accel_from_torque_siglin
else:
return self.lateral_accel_from_torque_linear
def lateral_accel_from_torque_linear(torque: float, torque_params: car.CarParams.LateralTorqueTuning):
return self.lateral_accel_from_torque_linear(torque - self.steer_offset, torque_params)
return lateral_accel_from_torque_linear
def update(self, c: car.CarControl, can_strings: list[bytes], frogpilot_toggles) -> car.CarState:
self.steer_offset = float(getattr(frogpilot_toggles, "steer_offset", 0.0))
return super().update(c, can_strings, frogpilot_toggles)
@staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles):
ret.carName = "gm"
red_panda = getattr(frogpilot_toggles, "red_panda", False)
set_red_panda_canbus(red_panda)
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)]
if red_panda:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput), ret.safetyConfigs[0]]
gm_safety_cfg = ret.safetyConfigs[-1] if red_panda else ret.safetyConfigs[0]
ret.autoResumeSng = False
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN]
if PEDAL_MSG in fingerprint[0]:
ret.enableBsm = 0x142 in fingerprint.get(CanBus.POWERTRAIN, {})
def has_sascm(fingerprint):
return 0x2FF in fingerprint.get(CanBus.POWERTRAIN, {})
# Detect Beartech SASCM allows openpilot longitudinal control on SDGM and ASCM_INT vehicles
if has_sascm(fingerprint):
ret.flags |= GMFlags.SASCM.value
if PEDAL_MSG in fingerprint.get(CanBus.POWERTRAIN, {}):
ret.enableGasInterceptor = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR
if candidate == CAR.CHEVROLET_BOLT_ACC_2022_2023:
# Hard-block pedal interceptor for ACC fingerprinted Bolts
ret.enableGasInterceptor = False
gm_safety_cfg.safetyParam &= ~Panda.FLAG_GM_GAS_INTERCEPTOR
else:
# 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.]
kaofui_cars = SDGM_CAR | ASCM_INT | VOLT_LIKE_CARS | {CAR.CHEVROLET_MALIBU_HYBRID_CC}
ret.longitudinalTuning.kiBP = [5., 35.] if candidate in kaofui_cars else [5., 35., 60.]
if candidate in CAMERA_ACC_CAR:
ret.experimentalLongitudinalAvailable = candidate not in CC_ONLY_CAR
is_bolt_2022_2023_pedal = candidate == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and ret.enableGasInterceptor
kaofui_camera_cars = {
CAR.CHEVROLET_VOLT_CAMERA,
CAR.CHEVROLET_VOLT_CC,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
bolt_cc_camera_cars = {
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_BOLT_CC_2019_2021,
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
}
is_camera_acc = candidate in CAMERA_ACC_CAR and candidate not in kaofui_cars and \
(candidate not in CC_ONLY_CAR or candidate in bolt_cc_camera_cars)
if candidate in kaofui_camera_cars:
# Keep Volt/Malibu camera path functionally aligned with kaofui.
ret.experimentalLongitudinalAvailable = candidate not in (CC_ONLY_CAR | ASCM_INT | SDGM_CAR) or has_sascm(fingerprint)
ret.networkLocation = NetworkLocation.fwdCamera
ret.radarUnavailable = True # no radar
ret.radarUnavailable = 0x460 not in fingerprint.get(CanBus.OBSTACLE, {})
ret.pcmCruise = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
ret.minSteerSpeed = 10 * CV.KPH_TO_MS
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM
# Tuning for experimental long
ret.longitudinalTuning.kiV = [2.0, 1.5]
ret.vEgoStopping = 0.1
ret.vEgoStarting = 0.1
ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling
ret.longitudinalTuning.kiV = [0.5, 0.5]
ret.stoppingDecelRate = 1.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
ret.openpilotLongitudinalControl = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
elif candidate in SDGM_CAR:
ret.longitudinalTuning.kiV = [0., 0.] # TODO: tuning
ret.experimentalLongitudinalAvailable = False
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
elif is_camera_acc:
# TorqueTune camera-ACC behavior
ret.experimentalLongitudinalAvailable = (candidate not in CC_ONLY_CAR) and not ret.enableGasInterceptor
ret.networkLocation = NetworkLocation.fwdCamera
ret.pcmCruise = True
ret.radarUnavailable = True
ret.minEnableSpeed = -1. # engage speed is decided by ASCM
ret.minSteerSpeed = 30 * CV.MPH_TO_MS
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_SDGM
ret.radarUnavailable = True # no radar
ret.pcmCruise = not ret.enableGasInterceptor
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM
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.vEgoStopping = 0.1
ret.vEgoStarting = 0.1
ret.stoppingDecelRate = 1.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
ret.openpilotLongitudinalControl = True
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
elif candidate in SDGM_CAR:
# kaofui parity: SDGM cars require SASCM for experimental long
ret.experimentalLongitudinalAvailable = candidate not in (CC_ONLY_CAR | ASCM_INT | SDGM_CAR) or has_sascm(fingerprint)
ret.networkLocation = NetworkLocation.fwdCamera
ret.radarUnavailable = 0x460 not in fingerprint.get(CanBus.OBSTACLE, {})
ret.pcmCruise = True
ret.minEnableSpeed = -1. # engage speed is decided by pcm
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_SDGM
# Use C9 brake bit on Blazer and SDGM variants that lack 0xBE (ECMAcceleratorPos),
# so panda brake_pressed source matches carstate on light taps.
if candidate == CAR.CHEVROLET_BLAZER or ACCELERATOR_POS_MSG not in fingerprint.get(CanBus.POWERTRAIN, {}):
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_FORCE_BRAKE_C9
ret.flags |= GMFlags.FORCE_BRAKE_C9.value
# Tuning for experimental long
ret.longitudinalTuning.kiV = [0.5, 0.5] if candidate in kaofui_cars else [0.5, 0.5, 0.5]
ret.vEgoStopping = 0.1
ret.vEgoStarting = 0.1
ret.stoppingDecelRate = 1.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
ret.openpilotLongitudinalControl = True
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
if is_bolt_2022_2023_pedal:
ret.experimentalLongitudinalAvailable = False
ret.pcmCruise = False
elif candidate in ASCM_INT and has_sascm(fingerprint):
ret.experimentalLongitudinalAvailable = True
ret.networkLocation = NetworkLocation.fwdCamera
ret.radarUnavailable = 0x460 not in fingerprint.get(CanBus.OBSTACLE, {})
ret.pcmCruise = True
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_ASCM_INT
# Tuning for experimental long
ret.longitudinalTuning.kiV = [0.5, 0.5] if candidate in kaofui_cars else [0.5, 0.5, 0.5]
ret.vEgoStopping = 0.1
ret.vEgoStarting = 0.1
ret.stoppingDecelRate = 1.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
ret.openpilotLongitudinalControl = True
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
if is_bolt_2022_2023_pedal:
ret.experimentalLongitudinalAvailable = False
ret.pcmCruise = False
else: # ASCM, OBD-II harness
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
ret.networkLocation = NetworkLocation.gateway
ret.radarUnavailable = RADAR_HEADER_MSG not in fingerprint[CanBus.OBSTACLE] and not docs
ret.radarUnavailable = RADAR_HEADER_MSG not in fingerprint.get(CanBus.OBSTACLE, {}) and not docs
ret.pcmCruise = False # stock non-adaptive cruise control is kept off
# supports stop and go, but initial engage must (conservatively) be above 18mph
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
# Tuning
ret.longitudinalTuning.kiV = [2.4, 1.5]
ret.longitudinalTuning.kiV = [0.5, 0.5] if candidate in kaofui_cars else [0.5, 0.5, 0.5]
if candidate in kaofui_cars:
ret.stoppingDecelRate = 3
ret.vEgoStopping = 0.75
ret.vEgoStarting = 0.75
ret.stopAccel = -1.5
if ret.enableGasInterceptor:
# Need to set ASCM long limits when using pedal interceptor, instead of camera ACC long limits
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_ASCM_LONG
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_ASCM_LONG
if getattr(frogpilot_toggles, "remote_start_boots_comma", False):
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_REMOTE_START_BOOTS_COMMA
# Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below.
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.00]]
ret.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594
ret.steerActuatorDelay = 0.1 # Default delay, not measured yet
ret.longitudinalTuning.deadzoneBP = [0.]
ret.longitudinalTuning.deadzoneV = [0.]
ret.steerLimitTimer = 0.4
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
ret.longitudinalActuatorDelay = 0.5 # large delay to initially start braking
if candidate in (CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_CC):
if candidate in (CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_VOLT_CAMERA):
ret.minEnableSpeed = -1
ret.lateralTuning.pid.kpBP = [0., 40.]
ret.lateralTuning.pid.kpV = [0., 0.17]
ret.lateralTuning.pid.kiBP = [0.]
ret.lateralTuning.pid.kiV = [0.]
ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_volt()
if candidate == CAR.CHEVROLET_VOLT_2019 and not ret.openpilotLongitudinalControl:
ret.minEnableSpeed = -1
if candidate in VOLT_LIKE_CARS:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
ret.steerActuatorDelay = 0.2
if candidate == CAR.CHEVROLET_MALIBU_HYBRID_CC and ret.enableGasInterceptor:
ret.flags |= GMFlags.PEDAL_LONG.value
elif candidate == CAR.GMC_ACADIA:
ret.minEnableSpeed = -1. # engage speed is decided by pcm
@@ -199,15 +371,30 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_BOLT_CC):
elif candidate in (CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, CAR.CHEVROLET_BOLT_CC_2022_2023, CAR.CHEVROLET_BOLT_CC_2019_2021, CAR.CHEVROLET_BOLT_CC_2017):
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 candidate == CAR.CHEVROLET_BOLT_CC_2017:
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_BOLT_2017
if ret.enableGasInterceptor:
# ACC Bolts use pedal for full longitudinal control, not just sng
ret.flags |= GMFlags.PEDAL_LONG.value
elif candidate == CAR.CHEVROLET_SILVERADO:
# Enable pedal interceptor for ACC models when detected
if is_bolt_2022_2023_pedal:
ret.flags |= GMFlags.PEDAL_LONG.value
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_NO_ACC
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_BOLT_2022_PEDAL
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
# 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
@@ -230,22 +417,34 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.CADILLAC_XT6:
ret.steerActuatorDelay = 0.2
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.CADILLAC_XT4:
ret.steerActuatorDelay = 0.2
if not ret.openpilotLongitudinalControl:
ret.minEnableSpeed = -1. # engage speed is decided by pcm
ret.minSteerSpeed = 30 * CV.MPH_TO_MS
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.CADILLAC_XT5_CC:
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.CHEVROLET_TRAVERSE:
elif candidate in (CAR.CHEVROLET_TRAVERSE, CAR.CHEVROLET_BLAZER):
ret.steerActuatorDelay = 0.2
ret.minSteerSpeed = 10 * CV.KPH_TO_MS
if not ret.openpilotLongitudinalControl:
ret.minEnableSpeed = -1. # engage speed is decided by pcm
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if candidate == CAR.CHEVROLET_BLAZER:
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
elif candidate == CAR.BUICK_BABYENCLAVE:
ret.steerActuatorDelay = 0.2
ret.minSteerSpeed = 10 * CV.KPH_TO_MS
if not ret.openpilotLongitudinalControl:
ret.minEnableSpeed = -1. # engage speed is decided by pcm
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.CADILLAC_CT6_CC:
@@ -258,58 +457,82 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.CHEVROLET_TRAX:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if ret.enableGasInterceptor:
if ret.enableGasInterceptor and frogpilot_toggles.gm_pedal_longitudinal:
ret.networkLocation = NetworkLocation.fwdCamera
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM
ret.minEnableSpeed = -1
ret.pcmCruise = False
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
ret.stoppingControl = True
ret.autoResumeSng = True
if candidate in CC_ONLY_CAR:
if candidate in CC_ONLY_CAR or (candidate in CAMERA_ACC_CAR and ret.enableGasInterceptor): #pedal interceptor tuning
ret.flags |= GMFlags.PEDAL_LONG.value
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_PEDAL_LONG
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_PEDAL_LONG
# Note: Low speed, stop and go not tested. Should be fairly smooth on highway
ret.longitudinalTuning.kiBP = [0.0, 5., 35.]
ret.longitudinalTuning.kiV = [0.0, 0.35, 0.5]
ret.longitudinalTuning.kf = 0.15
ret.stoppingDecelRate = 0.8
if candidate in (CAR.CHEVROLET_MALIBU_CC, CAR.CHEVROLET_MALIBU_HYBRID_CC):
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
ret.minEnableSpeed = -1
ret.pcmCruise = False
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
else:
ret.longitudinalTuning.kiBP = [0., 3., 6., 35.]
ret.longitudinalTuning.kiV = [0.125, 0.175, 0.225, 0.33]
ret.longitudinalTuning.kfDEPRECATED = 0.25
ret.stoppingDecelRate = 0.8
else: # Pedal used for SNG, ACC for longitudinal control otherwise
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
ret.startingState = True
ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25
elif candidate in CC_ONLY_CAR:
if ret.enableGasInterceptor and candidate == CAR.CHEVROLET_MALIBU_HYBRID_CC:
ret.flags |= GMFlags.PEDAL_LONG.value
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_PEDAL_LONG
ret.longitudinalTuning.kiBP = [0.0, 5., 35.]
ret.longitudinalTuning.kiV = [0.0, 0.18, 0.25]
ret.longitudinalTuning.kfDEPRECATED = 0.15
ret.stoppingDecelRate = 0.8
ret.minEnableSpeed = -1
ret.pcmCruise = False
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
elif candidate in CC_ONLY_CAR and not ret.enableGasInterceptor:
ret.flags |= GMFlags.CC_LONG.value
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_CC_LONG
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_CC_LONG
ret.radarUnavailable = True
ret.experimentalLongitudinalAvailable = False
ret.minEnableSpeed = 24 * CV.MPH_TO_MS
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
ret.pcmCruise = False
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 not ret.enableGasInterceptor and candidate in CC_ONLY_CAR: #redneck tuning
if candidate == CAR.CHEVROLET_MALIBU_HYBRID_CC:
pass
else:
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
if candidate == CAR.CHEVROLET_MALIBU_CC:
ret.longitudinalTuning.kpV = [0., 20., 20.]
ret.longitudinalTuning.kiBP = [0.]
ret.longitudinalTuning.kiV = [0.1]
ret.stoppingDecelRate = 11.18 # == 25 mph/s (.04 rate)
if candidate in CC_ONLY_CAR:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_ACC
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_NO_ACC
# Exception for flashed cars, or cars whose camera was removed
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and CAM_MSG not in fingerprint[CanBus.CAMERA] and not candidate in SDGM_CAR:
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and CAM_MSG not in fingerprint.get(CanBus.CAMERA, {}) and not candidate in (SDGM_CAR | ASCM_INT):
ret.flags |= GMFlags.NO_CAMERA.value
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_CAMERA
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_NO_CAMERA
if ACCELERATOR_POS_MSG not in fingerprint[CanBus.POWERTRAIN]:
if ACCELERATOR_POS_MSG not in fingerprint.get(CanBus.POWERTRAIN, {}):
ret.flags |= GMFlags.NO_ACCELERATOR_POS_MSG.value
return ret
@@ -341,22 +564,28 @@ class CarInterface(CarInterfaceBase):
# TODO: verify 17 Volt can enable for the first time at a stop and allow for all GMs
below_min_enable_speed = ret.vEgo < self.CP.minEnableSpeed or self.CS.moving_backward
if below_min_enable_speed and not (ret.standstill and ret.brake >= 20 and
(self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.carFingerprint in SDGM_CAR)):
(self.CP.networkLocation == NetworkLocation.fwdCamera and
(self.CP.carFingerprint in VOLT_LIKE_CARS or self.CP.carFingerprint in {CAR.CHEVROLET_BLAZER, CAR.CHEVROLET_MALIBU_SDGM, CAR.CHEVROLET_TRAVERSE} or self.CP.carFingerprint not in SDGM_CAR))):
events.add(EventName.belowEngageSpeed)
if ret.cruiseState.standstill and not self.CP.autoResumeSng:
events.add(EventName.resumeRequired)
if ret.vEgo < self.CP.minSteerSpeed:
events.add(EventName.belowSteerSpeed)
if (self.CP.flags & GMFlags.CC_LONG.value) and ret.vEgo < self.CP.minEnableSpeed and ret.cruiseState.enabled:
events.add(EventName.speedTooLow)
if (self.CP.flags & GMFlags.CC_LONG.value) and ret.vEgo < self.CP.minEnableSpeed:
if ret.cruiseState.enabled or self.CS.out.cruiseState.enabled:
events.add(EventName.speedTooLow)
if (self.CP.flags & GMFlags.PEDAL_LONG.value) and \
self.CP.transmissionType == TransmissionType.direct and \
self.CP.carFingerprint != CAR.CHEVROLET_MALIBU_HYBRID_CC and \
not self.CS.single_pedal_mode and \
c.longActive:
events.add(FrogPilotEventName.pedalInterceptorNoBrake)
if self.CS.lkas_status == 3:
events.add(EventName.steerUnavailable)
ret.events = events.to_msg()
return ret, fp_ret
+228 -36
View File
@@ -33,41 +33,115 @@ 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):
self.STEER_MAX = CarControllerParams.STEER_MAX
self.STEER_STEP = CarControllerParams.STEER_STEP
self.INACTIVE_STEER_STEP = CarControllerParams.INACTIVE_STEER_STEP
self.STEER_DELTA_UP = CarControllerParams.STEER_DELTA_UP
self.STEER_DELTA_DOWN = CarControllerParams.STEER_DELTA_DOWN
self.STEER_DRIVER_ALLOWANCE = CarControllerParams.STEER_DRIVER_ALLOWANCE
self.STEER_DRIVER_MULTIPLIER = CarControllerParams.STEER_DRIVER_MULTIPLIER
self.STEER_DRIVER_FACTOR = CarControllerParams.STEER_DRIVER_FACTOR
if CP.carFingerprint == CAR.CHEVROLET_BOLT_CC_2017:
self.STEER_MAX = 450
self.STEER_DELTA_UP = 15
self.STEER_DELTA_DOWN = 34
self.STEER_DRIVER_ALLOWANCE = 78
self.STEER_DRIVER_MULTIPLIER = 6
self.STEER_DRIVER_FACTOR = 100
# Gas/brake lookups
self.ZERO_GAS = 2048 # Coasting
self.ZERO_GAS = 6150 # 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 = 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.
kaofui_cars = SDGM_CAR | ASCM_INT | {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
CAR.CHEVROLET_VOLT_CC,
CAR.CHEVROLET_MALIBU_CC,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
volt_like = {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
CAR.CHEVROLET_VOLT_CC,
}
elif CP.carFingerprint in SDGM_CAR:
self.MAX_GAS = 3400
self.MAX_ACC_REGEN = 1514
self.INACTIVE_REGEN = 1554
max_regen_acceleration = 0.
if CP.carFingerprint in kaofui_cars:
if (CP.carFingerprint in (CAMERA_ACC_CAR | SDGM_CAR) and
CP.carFingerprint not in CC_ONLY_CAR and
CP.carFingerprint != CAR.CHEVROLET_BOLT_ACC_2022_2023):
self.MAX_GAS = 8848
self.MAX_GAS_PLUS = 8848
self.MAX_ACC_REGEN = 5610
self.INACTIVE_REGEN = 5650
# Camera ACC vehicles have no regen while enabled.
# Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly
max_regen_acceleration = 0.
else:
self.MAX_GAS = 8191 # Safety limit, not ACC max. Stock ACC >8192 from standstill.
self.MAX_GAS_PLUS = 8191
self.MAX_ACC_REGEN = 5500 # 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,
# lower threshold removes some braking deadzone
max_regen_acceleration = -1. 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
if CP.carFingerprint in volt_like:
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, 0.]
else:
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, max_regen_acceleration]
else:
self.MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill.
self.MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen
self.INACTIVE_REGEN = 1404
# ICE has much less engine braking force compared to regen in EVs,
# lower threshold removes some braking deadzone
max_regen_acceleration = -1. if CP.carFingerprint in EV_CAR else -0.1
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
# 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
self.GAS_LOOKUP_BP = [max_regen_acceleration, 0., self.ACCEL_MAX]
elif CP.carFingerprint in SDGM_CAR:
self.MAX_GAS = 8191
self.MAX_GAS_PLUS = 8191
self.MAX_ACC_REGEN = 5500
self.INACTIVE_REGEN = 5500
max_regen_acceleration = 0.
self.BRAKE_SWITCH_MAX = 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
# 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
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, 0.]
self.max_regen_acceleration = max_regen_acceleration
self.GAS_LOOKUP_BP = [self.max_regen_acceleration, 0., self.ACCEL_MAX]
self.GAS_LOOKUP_BP_PLUS = [self.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, 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
@@ -76,10 +150,11 @@ 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)"
@@ -118,6 +193,16 @@ class CAR(Platforms):
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.45, tireStiffnessFactor=0.469, minEnableSpeed=-1),
dbc_dict=dbc_dict('gm_global_a_powertrain_volt', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')
)
CHEVROLET_VOLT_ASCM = GMPlatformConfig(
[GMCarDocs("Chevrolet Volt 2017-18 ASCM Harness", min_enable_speed=0, video_link="https://youtu.be/QeMCN_4TFfQ")],
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.45, tireStiffnessFactor=0.469, minEnableSpeed=-1),
dbc_dict=dbc_dict('gm_global_a_powertrain_volt', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')
)
CHEVROLET_VOLT_CAMERA = GMPlatformConfig(
[GMCarDocs("Chevrolet Volt 2017-18 Camera Harness", "Flashed camera-forward integration with ACC")],
CHEVROLET_VOLT.specs,
dbc_dict=dbc_dict('gm_global_a_powertrain_volt', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')
)
CADILLAC_ATS = GMASCMPlatformConfig(
[GMCarDocs("Cadillac ATS Premium Performance 2018")],
GMCarSpecs(mass=1601, wheelbase=2.78, steerRatio=15.3),
@@ -126,10 +211,19 @@ class CAR(Platforms):
[GMCarDocs("Chevrolet Malibu Premier 2017")],
GMCarSpecs(mass=1496, wheelbase=2.83, steerRatio=15.8, centerToFrontRatio=0.4),
)
CHEVROLET_MALIBU_ASCM = GMPlatformConfig(
[GMCarDocs("Chevrolet Malibu 2017-19 ASCM Harness")],
CHEVROLET_MALIBU.specs,
)
GMC_ACADIA = GMASCMPlatformConfig(
[GMCarDocs("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo")],
GMCarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4),
)
GMC_ACADIA_ASCM = GMPlatformConfig(
[GMCarDocs("GMC Acadia 2018 ASCM Harness", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo")],
GMCarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4),
dbc_dict=dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')
)
BUICK_LACROSSE = GMASCMPlatformConfig(
[GMCarDocs("Buick LaCrosse 2017-19", "Driver Confidence Package 2")],
GMCarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4),
@@ -150,10 +244,10 @@ class CAR(Platforms):
[GMCarDocs("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS")],
CADILLAC_ESCALADE_ESV.specs,
)
CHEVROLET_BOLT_EUV = GMPlatformConfig(
CHEVROLET_BOLT_ACC_2022_2023 = GMPlatformConfig(
[
GMCarDocs("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"),
GMCarDocs("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"),
GMCarDocs("Chevrolet Bolt ACC 2022-2023", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"),
GMCarDocs("Chevrolet Bolt EV ACC 2022-2023", "2LT Trim with Adaptive Cruise Control Package"),
],
GMCarSpecs(mass=1669, wheelbase=2.63779, steerRatio=16.8, centerToFrontRatio=0.4, tireStiffnessFactor=1.0),
)
@@ -162,7 +256,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=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(
[GMCarDocs("Chevrolet Equinox 2019-22")],
@@ -178,12 +272,26 @@ class CAR(Platforms):
[GMCarDocs("Chevrolet Volt 2017-18 - No-ACC", min_enable_speed=0)],
CHEVROLET_VOLT.specs,
)
CHEVROLET_BOLT_CC = GMPlatformConfig(
CHEVROLET_BOLT_CC_2019_2021 = GMPlatformConfig(
[GMCarDocs("Chevrolet Bolt EV 2019-2021 - No-ACC")],
CHEVROLET_BOLT_ACC_2022_2023.specs,
)
CHEVROLET_BOLT_ACC_2022_2023_PEDAL = GMPlatformConfig(
[
GMCarDocs("Chevrolet Bolt EUV 2022-23 - No-ACC"),
GMCarDocs("Chevrolet Bolt EV 2017-23 - No-ACC"),
GMCarDocs("Chevrolet Bolt EV 2022-2023 ACC w Pedal"),
],
CHEVROLET_BOLT_EUV.specs,
CHEVROLET_BOLT_ACC_2022_2023.specs,
)
CHEVROLET_BOLT_CC_2022_2023 = GMPlatformConfig(
[
GMCarDocs("Chevrolet Bolt EV 2022-2023 - No-ACC"),
GMCarDocs("Chevrolet Bolt EV 2022-2023 - No-ACC"),
],
CHEVROLET_BOLT_ACC_2022_2023.specs,
)
CHEVROLET_BOLT_CC_2017 = GMPlatformConfig(
[GMCarDocs("Chevrolet Bolt EV 2017 - No-ACC")],
CHEVROLET_BOLT_ACC_2022_2023.specs,
)
CHEVROLET_EQUINOX_CC = GMPlatformConfig(
[GMCarDocs("Chevrolet Equinox 2019-22 - No-ACC")],
@@ -217,22 +325,42 @@ class CAR(Platforms):
[GMCarDocs("Cadillac XT5 - No-ACC")],
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
)
CHEVROLET_BLAZER = GMPlatformConfig(
[GMCarDocs("Chevrolet Blazer 2019-2025", "Driver Assist Package")],
CarSpecs(mass=1850, wheelbase=3.10, steerRatio=17.9, centerToFrontRatio=0.4),
)
CHEVROLET_TRAVERSE = GMPlatformConfig(
[GMCarDocs("Chevrolet Traverse 2023", "Driver Assist Package")],
CarSpecs(mass=1955, wheelbase=3.07, steerRatio=17.9, centerToFrontRatio=0.4),
)
CHEVROLET_MALIBU_SDGM = GMPlatformConfig(
[GMCarDocs("Chevrolet Malibu 2019", "SDGM Harness (Optional SASCM)")],
CHEVROLET_MALIBU.specs,
)
BUICK_BABYENCLAVE = GMPlatformConfig(
[GMCarDocs("Buick Baby Enclave 2020-23", "Driver Assist Package")],
CarSpecs(mass=2050, wheelbase=2.86, steerRatio=16.0, centerToFrontRatio=0.5),
)
CHEVROLET_MALIBU_CC = GMPlatformConfig(
[GMCarDocs("Chevrolet Malibu 2023 - No-ACC")],
CarSpecs(mass=1450, wheelbase=2.8, steerRatio=18.25, centerToFrontRatio=0.4, tireStiffnessFactor=0.997),
)
CHEVROLET_MALIBU_HYBRID_CC = GMPlatformConfig(
[GMCarDocs("Chevrolet Malibu Hybrid 2017 - No-ACC")],
CarSpecs(mass=1450, wheelbase=2.8, steerRatio=15.8, centerToFrontRatio=0.4),
)
CHEVROLET_TRAX = GMPlatformConfig(
[GMCarDocs("Chevrolet TRAX 2024")],
CarSpecs(mass=1365, wheelbase=2.7, steerRatio=16.4, centerToFrontRatio=0.4),
)
CHEVROLET_VOLT_2019 = GMPlatformConfig(
[GMCarDocs("Chevrolet Volt 2019")],
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=15.7, centerToFrontRatio=0.45),
)
CADILLAC_XT6 = GMPlatformConfig(
[GMCarDocs("Cadillac XT6 2020", "Driver Assist Package")],
GMCarSpecs(mass=2050, wheelbase=2.86, steerRatio=16.5, centerToFrontRatio=0.4),
)
class CruiseButtons:
@@ -257,11 +385,29 @@ class CanBus:
LOOPBACK = 128
DROPPED = 192
def set_red_panda_canbus(enabled: bool) -> None:
if enabled:
CanBus.POWERTRAIN = 4
CanBus.OBSTACLE = 5
CanBus.CAMERA = 6
CanBus.CHASSIS = 6
CanBus.LOOPBACK = 132
CanBus.DROPPED = 196
else:
CanBus.POWERTRAIN = 0
CanBus.OBSTACLE = 1
CanBus.CAMERA = 2
CanBus.CHASSIS = 2
CanBus.LOOPBACK = 128
CanBus.DROPPED = 192
class GMFlags(IntFlag):
PEDAL_LONG = 1
CC_LONG = 2
NO_CAMERA = 4
NO_ACCELERATOR_POS_MSG = 8
FORCE_BRAKE_C9 = 16
SASCM = 32
# In a Data Module, an identifier is a string used to recognize an object,
@@ -313,16 +459,62 @@ FW_QUERY_CONFIG = FwQueryConfig(
extra_ecus=[(Ecu.fwdCamera, 0x24b, None)],
)
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'))
EV_CAR = {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
CAR.CHEVROLET_VOLT_CC,
CAR.CHEVROLET_BOLT_ACC_2022_2023,
CAR.CHEVROLET_BOLT_CC_2019_2021,
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
CC_ONLY_CAR = {
CAR.CHEVROLET_VOLT_CC,
CAR.CHEVROLET_BOLT_CC_2019_2021,
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
CAR.CHEVROLET_BOLT_CC_2017,
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,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
CC_REGEN_PADDLE_CAR = {
CAR.CHEVROLET_BOLT_CC_2019_2021,
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
CAR.CHEVROLET_BOLT_CC_2017,
}
# 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.CADILLAC_XT6, CAR.CHEVROLET_TRAVERSE, CAR.CHEVROLET_BLAZER, CAR.CHEVROLET_MALIBU_SDGM, CAR.BUICK_BABYENCLAVE, CAR.CHEVROLET_VOLT_2019}
ASCM_INT = {CAR.CHEVROLET_VOLT_ASCM, CAR.GMC_ACADIA_ASCM, CAR.CHEVROLET_MALIBU_ASCM}
# We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness)
CAMERA_ACC_CAR = {CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_EQUINOX, CAR.CHEVROLET_TRAILBLAZER, CAR.CHEVROLET_TRAX}
CAMERA_ACC_CAR.update({CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_EQUINOX_CC, CAR.GMC_YUKON_CC, CAR.CADILLAC_CT6_CC, CAR.CHEVROLET_TRAILBLAZER_CC, CAR.CADILLAC_XT5_CC, CAR.CHEVROLET_MALIBU_CC})
CAMERA_ACC_CAR = {CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_EQUINOX, CAR.CHEVROLET_TRAILBLAZER, CAR.CHEVROLET_TRAX, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_BLAZER}
CAMERA_ACC_CAR.update({
CAR.CHEVROLET_VOLT_CC,
CAR.CHEVROLET_BOLT_CC_2019_2021,
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_EQUINOX_CC,
CAR.GMC_YUKON_CC,
CAR.CADILLAC_CT6_CC,
CAR.CHEVROLET_TRAILBLAZER_CC,
CAR.CADILLAC_XT5_CC,
CAR.CHEVROLET_MALIBU_CC,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
})
# CAMERA_ACC_CAR.update(CC_ONLY_CAR)
STEER_THRESHOLD = 1.0
+34 -8
View File
@@ -1,3 +1,4 @@
import math
from collections import namedtuple
from cereal import car
@@ -5,10 +6,12 @@ from openpilot.common.numpy_fast import clip, interp
from openpilot.common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import create_gas_interceptor_command
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.selfdrive.car.honda import hondacan
from openpilot.selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams
from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.controls.lib.drive_helpers import rate_limit
from openpilot.selfdrive.controls.lib.pid import PIDController
VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState
@@ -125,6 +128,10 @@ class CarController(CarControllerBase):
self.gas = 0.0
self.brake = 0.0
self.last_steer = 0.0
self.pitch = 0.0
self.gasonly_pid = PIDController(k_p=([0,], [0,]),
k_i=([0., 5., 35.], [1.2, 0.8, 0.5]),
rate=1 / DT_CTRL / 2)
def update(self, CC, CS, now_nanos, frogpilot_toggles):
actuators = CC.actuators
@@ -133,6 +140,9 @@ class CarController(CarControllerBase):
hud_v_cruise = hud_control.setSpeed / conversion if hud_control.speedVisible else 255
pcm_cancel_cmd = CC.cruiseControl.cancel
if len(CC.orientationNED) == 3:
self.pitch = CC.orientationNED[1]
if CC.longActive:
accel = actuators.accel
gas, brake = compute_gas_brake(actuators.accel, CS.out.vEgo, self.CP.carFingerprint)
@@ -173,8 +183,11 @@ class CarController(CarControllerBase):
CS.CP.openpilotLongitudinalControl))
# wind brake from air resistance decel at high speed
wind_brake = interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15])
wind_brake_ms2 = interp(CS.out.vEgo, [0.0, 13.4, 22.4, 31.3, 40.2], [0.000, 0.049, 0.136, 0.267, 0.441]) # in m/s2 units
hill_brake = math.sin(self.pitch) * ACCELERATION_DUE_TO_GRAVITY
# all of this is only relevant for HONDA NIDEC
wind_brake = interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15]) # not in m/s2 units
max_accel = interp(CS.out.vEgo, self.params.NIDEC_MAX_ACCEL_BP, self.params.NIDEC_MAX_ACCEL_V)
# TODO this 1.44 is just to maintain previous behavior
pcm_speed_BP = [-wind_brake,
@@ -217,12 +230,25 @@ class CarController(CarControllerBase):
if self.CP.carFingerprint in HONDA_BOSCH:
self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, self.params.BOSCH_ACCEL_MAX)
self.gas = interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V)
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
gas_pedal_force = self.accel # radarless does not need a pid
elif (actuators.longControlState == LongCtrlState.pid) and not CS.out.gasPressed: # perform a gas-only pid
gas_error = self.accel - CS.out.aEgo
self.gasonly_pid.neg_limit = self.params.BOSCH_ACCEL_MIN
self.gasonly_pid.pos_limit = self.params.BOSCH_ACCEL_MAX
gas_pedal_force = self.gasonly_pid.update(gas_error, speed=CS.out.vEgo, feedforward=self.accel)
gas_pedal_force += wind_brake_ms2 + hill_brake
else:
gas_pedal_force = self.accel
self.gasonly_pid.reset()
gas_pedal_force += wind_brake_ms2 + hill_brake
self.gas = interp(gas_pedal_force, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V)
stopping = actuators.longControlState == LongCtrlState.stopping
self.stopping_counter = self.stopping_counter + 1 if stopping else 0
can_sends.extend(hondacan.create_acc_commands(self.packer, self.CAN, CC.enabled, CC.longActive, self.accel, self.gas,
self.stopping_counter, self.CP.carFingerprint))
self.stopping_counter, self.CP.carFingerprint, accel + wind_brake_ms2 + hill_brake))
else:
apply_brake = clip(self.brake_last - wind_brake, 0.0, 1.0)
apply_brake = int(clip(apply_brake * self.params.NIDEC_BRAKE_MAX, 0, self.params.NIDEC_BRAKE_MAX - 1))
@@ -260,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
+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)
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
+7 -4
View File
@@ -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
+19 -12
View File
@@ -37,7 +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.3
FRICTION_THRESHOLD = 0.12
def get_friction_threshold(v_ego):
# Interpolate friction threshold
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])
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')
@@ -52,6 +57,7 @@ GEAR_SHIFTER_MAP: dict[str, car.CarState.GearShifter] = {
'D': GearShifter.drive, 'DRIVE': GearShifter.drive,
'S': GearShifter.sport, 'SPORT': GearShifter.sport,
'L': GearShifter.low, 'LOW': GearShifter.low,
'L2': GearShifter.low, 'L3': GearShifter.low,
'B': GearShifter.brake, 'BRAKE': GearShifter.brake,
}
@@ -146,14 +152,22 @@ class CarInterfaceBase(ABC):
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles)
trailer_load_kg = getattr(frogpilot_toggles, "trailer_load_kg", 0)
# Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
if not ret.notCar:
ret.mass = ret.mass + trailer_load_kg
ret.mass = ret.mass + STD_CARGO_KG
# Set params dependent on values set by the car interface
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront, ret.tireStiffnessFactor)
# FrogPilot variables
toggles_to_check = ("force_torque_controller", "nnff", "nnff_lite")
if ret.steerControlType != car.CarParams.SteerControlType.angle and any(getattr(frogpilot_toggles, toggle, False) for toggle in toggles_to_check):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
return ret
@classmethod
@@ -173,6 +187,7 @@ class CarInterfaceBase(ABC):
elif platform in GMCAR:
fp_ret.canUsePedal = True
fp_ret.canUseSASCM = True
elif platform in HondaCAR:
if candidate == HondaCAR.HONDA_CLARITY:
@@ -216,14 +231,6 @@ class CarInterfaceBase(ABC):
fp_ret.canUsePedal = not CP.autoResumeSng
fp_ret.canUseSDSU = not CP.enableDsu and candidate not in UNSUPPORTED_DSU_CAR and candidate not in TSS2_CAR
if CP.steerControlType != car.CarParams.SteerControlType.angle:
if CP.lateralTuning.which() == "pid" and (frogpilot_toggles.force_torque_controller or frogpilot_toggles.nnff or frogpilot_toggles.nnff_lite):
CarInterfaceBase.configure_torque_tune(candidate, fp_ret.lateralTuning)
elif CP.lateralTuning.which() == "torque":
CarInterfaceBase.configure_torque_tune(candidate, fp_ret.lateralTuning)
else:
fp_ret.lateralTuning.init("pid")
fp_ret.openpilotLongitudinalControlDisabled = frogpilot_toggles.disable_openpilot_long
return fp_ret
@@ -285,7 +292,7 @@ class CarInterfaceBase(ABC):
ret.vEgoStopping = 0.5
ret.vEgoStarting = 0.5
ret.stoppingControl = True
ret.longitudinalTuning.kf = 1.
ret.longitudinalTuning.kfDEPRECATED = 1.
ret.longitudinalTuning.kpBP = [0.]
ret.longitudinalTuning.kpV = [0.]
ret.longitudinalTuning.kiBP = [0.]
@@ -301,9 +308,9 @@ class CarInterfaceBase(ABC):
tune.init('torque')
tune.torque.useSteeringAngle = use_steering_angle
tune.torque.kf = 1.0
tune.torque.kp = 1.0
tune.torque.kp = 0.6
tune.torque.ki = 0.3
tune.torque.kd = 0.0
tune.torque.friction = params['FRICTION']
tune.torque.latAccelFactor = params['LAT_ACCEL_FACTOR']
tune.torque.latAccelOffset = 0.0
+4 -2
View File
@@ -43,8 +43,10 @@ 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.05]
"CHEVROLET_MALIBU_CC" = [1.85, 1.85, 0.075]
"CADILLAC_XT6" = [1.33, 1.9, 0.16]
"CHEVROLET_BOLT_ACC_2022_2023" = [2.0, 2.0, 0.09]
"CHEVROLET_BLAZER" = [1.33, 1.33, 0.18]
"CHEVROLET_MALIBU_CC" = [1.58, 1.8422651988094612, 0.205]
"CHEVROLET_SILVERADO" = [1.9, 1.9, 0.112]
"CHEVROLET_TRAILBLAZER" = [1.33, 1.9, 0.16]
"CHEVROLET_TRAVERSE" = [1.33, 1.33, 0.18]
+1
View File
@@ -5,6 +5,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"AUDI_A3_MK3" = [1.5122414863077502, 1.7443517531719404, 0.15194151892450905]
"AUDI_Q3_MK2" = [1.4439223359448605, 1.2254955789112076, 0.1413798895978097]
"CHEVROLET_VOLT" = [1.5961527626411784, 1.8422651988094612, 0.1572393918005158]
"CHEVROLET_MALIBU_HYBRID_CC" = [1.5961527626411784, 1.8422651988094612, 0.1572393918005158]
"CHRYSLER_PACIFICA_2018" = [2.07140, 1.3366521181047952, 0.13776367250652022]
"CHRYSLER_PACIFICA_2020" = [1.86206, 1.509076559398423, 0.14328246159386085]
"CHRYSLER_PACIFICA_2017_HYBRID" = [1.79422, 1.06831764583744, 0.116237]
+10 -1
View File
@@ -56,9 +56,18 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"CADILLAC_ESCALADE_ESV" = "CHEVROLET_VOLT"
"CADILLAC_ATS" = "CHEVROLET_VOLT"
"CHEVROLET_MALIBU" = "CHEVROLET_VOLT"
"CHEVROLET_MALIBU_ASCM" = "CHEVROLET_VOLT"
"CHEVROLET_MALIBU_SDGM" = "CHEVROLET_VOLT"
"HOLDEN_ASTRA" = "CHEVROLET_VOLT"
"CHEVROLET_VOLT_CC" = "CHEVROLET_VOLT"
"CHEVROLET_BOLT_CC" = "CHEVROLET_BOLT_EUV"
"CHEVROLET_VOLT_CAMERA" = "CHEVROLET_VOLT"
"CHEVROLET_VOLT_ASCM" = "CHEVROLET_VOLT"
"GMC_ACADIA_ASCM" = "GMC_ACADIA"
"CHEVROLET_VOLT_2019" = "CHEVROLET_VOLT"
"CHEVROLET_BOLT_CC_2019_2021" = "CHEVROLET_BOLT_ACC_2022_2023"
"CHEVROLET_BOLT_ACC_2022_2023_PEDAL" = "CHEVROLET_BOLT_ACC_2022_2023"
"CHEVROLET_BOLT_CC_2022_2023" = "CHEVROLET_BOLT_ACC_2022_2023"
"CHEVROLET_BOLT_CC_2017" = "CHEVROLET_BOLT_ACC_2022_2023"
"CHEVROLET_EQUINOX_CC" = "CHEVROLET_EQUINOX"
"CHEVROLET_SUBURBAN" = "CHEVROLET_SILVERADO"
"CHEVROLET_SUBURBAN_CC" = "CHEVROLET_SILVERADO"
+1 -1
View File
@@ -53,7 +53,7 @@ def get_long_tune(CP, params):
kiBP = [2., 5.]
kiV = [0.5, 0.25]
return PIDController(0.0, (kiBP, kiV), k_f=1.0,
return PIDController(0.0, (kiBP, kiV),
pos_limit=params.ACCEL_MAX, neg_limit=params.ACCEL_MIN,
rate=1 / (DT_CTRL * 3))
+48 -17
View File
@@ -29,10 +29,12 @@ from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, S
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel
from openpilot.frogpilot.tinygrad_modeld.tinygrad_modeld import LAT_SMOOTH_SECONDS
from openpilot.system.hardware import HARDWARE
from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles, params_memory
from openpilot.frogpilot.controls.lib.neural_network_feedforward import LatControlNNFF
SOFT_DISABLE_TIME = 3 # seconds
LDW_MIN_SPEED = 31 * CV.MPH_TO_MS
@@ -135,11 +137,11 @@ class Controls:
self.LaC: LatControl
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
self.LaC = LatControlAngle(self.CP, self.CI)
elif self.FPCP.lateralTuning.which() == 'pid':
self.LaC = LatControlPID(self.CP, self.CI)
elif self.FPCP.lateralTuning.which() == 'torque':
self.LaC = LatControlTorque(self.CP, self.FPCP, self.CI)
self.LaC = LatControlAngle(self.CP, self.CI, DT_CTRL)
elif self.CP.lateralTuning.which() == 'pid':
self.LaC = LatControlPID(self.CP, self.CI, DT_CTRL)
elif self.CP.lateralTuning.which() == 'torque':
self.LaC = LatControlTorque(self.CP, self.CI, DT_CTRL)
self.initialized = False
self.state = State.disabled
@@ -197,13 +199,19 @@ class Controls:
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_events = Events(frogpilot=True)
self.frogpilot_toggles = get_frogpilot_toggles()
if self.CP.lateralTuning.which() == "torque" and (self.frogpilot_toggles.nnff or self.frogpilot_toggles.nnff_lite):
self.LaC = LatControlNNFF(self.CP, self.CI, DT_CTRL)
self.frogpilot_toggles.is_metric = self.is_metric
def set_initial_state(self):
if REPLAY:
controls_state = self.params.get("ReplayControlsState")
@@ -613,14 +621,36 @@ class Controls:
self.curvature = -self.VM.calc_curvature(steer_angle_without_offset, CS.vEgo, lp.roll)
# Update Torque Params
if self.FPCP.lateralTuning.which() == 'torque':
if self.CP.lateralTuning.which() == 'torque':
torque_params = self.sm['liveTorqueParameters']
if self.sm.all_checks(['liveTorqueParameters']) and (torque_params.useParams or self.frogpilot_toggles.force_auto_tune):
self.LaC.update_live_torque_params(torque_params.latAccelFactorFiltered, torque_params.latAccelOffsetFiltered,
torque_params.frictionCoefficientFiltered)
if self.sm.updated['liveDelay'] and (self.frogpilot_toggles.nnff or self.frogpilot_toggles.nnff_lite):
self.LaC.nnff.update_live_delay(self.sm['liveDelay'].lateralDelay)
allow_lat_accel_learning = self.CP.carName in ['toyota', 'hyundai']
allow_friction_learning = (allow_lat_accel_learning or self.CP.carName in ['gm'])
use_live_params = self.sm.all_checks(['liveTorqueParameters']) and (torque_params.useParams or self.frogpilot_toggles.force_auto_tune)
# Defaults pulled from manual tuning values
lat_accel_factor = self.params.get_float("SteerLatAccel")
friction = self.params.get_float("SteerFriction")
lat_accel_offset = self.CP.lateralTuning.torque.latAccelOffset
# Apply user overrides first
if self.frogpilot_toggles.use_custom_latAccelFactor:
lat_accel_factor = self.frogpilot_toggles.latAccelFactor
if self.frogpilot_toggles.use_custom_friction:
friction = self.frogpilot_toggles.friction
# Layer in live values only for parameters the platform allows to learn and only when not overridden
if use_live_params:
if allow_lat_accel_learning and not self.frogpilot_toggles.use_custom_latAccelFactor:
lat_accel_factor = torque_params.latAccelFactorFiltered
lat_accel_offset = torque_params.latAccelOffsetFiltered
if allow_friction_learning and not self.frogpilot_toggles.use_custom_friction:
friction = torque_params.frictionCoefficientFiltered
self.LaC.update_live_torque_params(lat_accel_factor, lat_accel_offset, friction)
if self.sm.updated['liveDelay'] and hasattr(self.LaC, "update_live_delay"):
self.LaC.update_live_delay(self.sm['liveDelay'].lateralDelay)
long_plan = self.sm['longitudinalPlan']
model_v2 = self.sm['modelV2']
@@ -671,11 +701,12 @@ class Controls:
# Reset desired curvature to current to avoid violating the limits on engage
new_desired_curvature = model_v2.action.desiredCurvature if CC.latActive else self.curvature
self.desired_curvature, curvature_limited = clip_curvature(CS.vEgo, self.desired_curvature, new_desired_curvature, lp.roll)
lat_delay = self.sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS
actuators.curvature = self.desired_curvature
steer, steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp,
self.steer_limited_by_safety, self.desired_curvature,
curvature_limited,
curvature_limited, lat_delay,
self.sm['liveLocationKalman'],
self.sm['modelV2'],
self.frogpilot_toggles)
@@ -715,8 +746,8 @@ class Controls:
desired_lateral_accel = model_v2.action.desiredCurvature * (clipped_speed**2)
undershooting = abs(desired_lateral_accel) / abs(1e-3 + actual_lateral_accel) > 1.2
turning = abs(desired_lateral_accel) > 1.0
# TODO: lac.saturated includes speed and other checks, should be pulled out
if undershooting and turning and lac_log.saturated:
commanded_torque_at_max = abs(lac_log.output) > 0.99
if undershooting and turning and (lac_log.saturated or commanded_torque_at_max):
if self.frogpilot_toggles.goat_scream_alert:
self.frogpilot_events.add(FrogPilotEventName.goatSteerSaturated)
else:
@@ -764,7 +795,7 @@ class Controls:
if self.frogpilot_toggles.conditional_experimental_mode or self.frogpilot_toggles.slc_fallback_experimental_mode:
self.experimental_mode = self.sm['frogpilotPlan'].experimentalMode
if hasattr(self.LaC, "pid"):
if hasattr(self.LaC, "pid") and self.CP.lateralTuning.which() != "pid":
self.LaC.pid._k_p = self.frogpilot_toggles.steerKp
# Update FrogPilot variables
@@ -888,7 +919,7 @@ class Controls:
controlsState.experimentalMode = self.experimental_mode
controlsState.personality = self.personality
lat_tuning = self.FPCP.lateralTuning.which()
lat_tuning = self.CP.lateralTuning.which()
if self.joystick_mode:
controlsState.lateralControlState.debugState = lac_log
elif self.CP.steerControlType == car.CarParams.SteerControlType.angle:
+2 -2
View File
@@ -189,7 +189,7 @@ def smooth_value(val, prev_val, tau, dt=DT_MDL):
alpha = 1 - np.exp(-dt/tau) if tau > 0 else 1
return alpha * val + (1 - alpha) * prev_val
def clip_curvature(v_ego, prev_curvature, new_curvature, roll):
def clip_curvature(v_ego, prev_curvature, new_curvature, roll) -> tuple[float, bool]:
# This function respects ISO lateral jerk and acceleration limits + a max curvature
v_ego = max(v_ego, MIN_SPEED)
max_curvature_rate = MAX_LATERAL_JERK / (v_ego ** 2) # inexact calculation, check https://github.com/commaai/openpilot/pull/24755
@@ -225,7 +225,7 @@ def get_speed_error(modelV2: log.ModelDataV2, v_ego: float) -> float:
return 0.0
def get_accel_from_plan(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0.05):
def get_accel_from_plan_tomb_raider(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0.05):
if len(speeds) == len(t_idxs):
v_now = speeds[0]
a_now = accels[0]
+4 -4
View File
@@ -357,8 +357,8 @@ def forcing_stop_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMas
def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
holiday_messages = {
"new_years": "Happy New Year! 🎉",
"valentines": "Happy Valentine's Day! ❤️",
"st_patricks": "Happy St. Patrick's Day! 🍀",
"valentines_day": "Happy Valentine's Day! ❤️",
"st_patricks_day": "Happy St. Patrick's Day! 🍀",
"world_frog_day": "Happy World Frog Day! 🐸",
"april_fools": "Happy April Fool's Day! 🤡",
"easter_week": "Happy Easter! 🐰",
@@ -372,7 +372,7 @@ def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster,
}
return Alert(
holiday_messages.get(frogpilot_toggles.current_holiday_theme),
holiday_messages.get(frogpilot_toggles.current_holiday_theme, "Happy Holidays!"),
"",
AlertStatus.normal, AlertSize.small,
Priority.LOWEST, VisualAlert.none, FrogPilotAudibleAlert.startup, 5.)
@@ -982,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("Reverse Gear"),
ET.USER_DISABLE: ImmediateDisableAlert("Wrong Gear"),
ET.NO_ENTRY: NoEntryAlert("Reverse Gear"),
},

Some files were not shown because too many files have changed in this diff Show More