Compare commits

...

76 Commits

Author SHA1 Message Date
firestar5683 3d1a99f2af User adjustable offsets 2026-02-07 23:33:16 -06:00
firestar5683 a4d61232c6 Integrator Smooth On Handoff 2026-02-06 15:48:45 -06:00
firestar5683 5881c17c59 Lights 2026-02-05 22:40:42 -06:00
firestar5683 d5e971c32b New Models 2026-02-05 15:07:42 -06:00
firestar5683 3024ed42f5 Revert "merry christmas"
This reverts commit 8a9d0e5e6a.
2026-02-05 13:33:58 -06:00
firestar5683 09a073c242 Increase Fault Resilience 2026-02-05 13:29:00 -06:00
firestarsdog 231ef85bed Stats 2026-02-02 01:05:58 -05:00
firestarsdog 740668c973 Stats 2026-02-01 22:47:02 -06:00
firestar5683 b343c6aa98 Revert "Mac Update"
This reverts commit c32a5fda39.
2026-01-19 11:44:30 -06:00
firestarsdog cc1a0e4697 Add SASCM to vehicle settings detection/stats 2026-01-19 10:37:27 -06:00
firestar5683 c32a5fda39 Mac Update 2026-01-18 22:21:59 -06:00
firestarsdog af14724e78 Stats 2026-01-18 22:17:50 -06:00
firestar5683 c85a34f324 Update carcontroller.py 2026-01-15 22:52:25 -06:00
firestar5683 70260c0ed2 fix redneck v2 2026-01-14 13:58:43 -06:00
firestar5683 0110bec1af Remove lat smooth seconds 2026-01-14 13:50:35 -06:00
firestar5683 94d881211c frogpilot migration 2026-01-12 22:12:07 -06:00
firestar5683 cea5b9cada More defaults 2026-01-12 22:04:34 -06:00
firestar5683 4269bb2309 Update defaults 2026-01-12 22:01:01 -06:00
firestar5683 5cc94240cd Big Mac 2026-01-11 16:58:51 -06:00
firestar5683 201458c3d6 Try Higher Friction 2026-01-10 14:05:27 -06:00
firestar5683 1af7a5ba65 Autotune Off 2026-01-09 22:42:42 -06:00
firestar5683 fb3d1a1392 merry christmas 2025-12-24 22:05:56 -06:00
firestar5683 d650757ff9 ds2 2025-12-24 21:30:15 -06:00
firestar5683 91e273e489 Try friction adjustment 2025-12-16 14:39:42 -06:00
firestar5683 d620145cdc Update frogpilot_tracking.py 2025-12-15 00:55:16 -06:00
firestar5683 23a5bedabc minsteer speed 2025-12-14 14:46:42 -06:00
firestar5683 ea45cbf382 Update Percentages 2025-12-13 19:14:46 -06:00
firestar5683 a1f7b451a6 Trailer Load Gas Tuning 2025-12-12 10:52:53 -06:00
firestar5683 1fabfd04bd Live Friction 2025-12-11 16:54:55 -06:00
firestar5683 8630693f7a Zero error 2025-12-11 15:13:40 -06:00
firestar5683 6df0333380 Updates
Update latcontrol_torque.py

interp friction threshold
2025-12-09 20:01:30 -06:00
firestar5683 8708ab2496 LattyBoi2.0 2025-12-03 23:30:46 -06:00
firestar5683 01ed483036 Latty Boi
Revert "Latty Boi"

This reverts commit af687e501cc4bdcda7840453d06595d7ea674148.

Reapply "Latty Boi"

This reverts commit ff5566d4439f5e4997fe83f4f21d0be62b75d75d.
2025-12-02 20:31:00 -06:00
firestar5683 23fb048ba6 Recovery Power 2025-12-02 17:18:59 -06:00
firestar5683 bee7628900 New planplus 2025-12-02 12:11:19 -06:00
Woohyun Rho 9b84afc5a8 Update 2025-11-28 17:14:08 -06:00
firestar5683 5f94a5b9e0 New Lateral Changes 2025-11-18 20:49:21 -06:00
firestar5683 41d69c31e7 Update 2025-11-15 14:47:04 -06:00
firestar5683 f6c89beebd Revert "Upstream Lateral"
This reverts commit 62c1345b2a.
2025-11-10 23:09:15 -06:00
firestar5683 62c1345b2a Upstream Lateral
Revert "Upstream Lateral"

This reverts commit 20f7d6631bb152860781b46533bb0e96223be132.

Reapply "Upstream Lateral"

This reverts commit 2a7a563e219a1b688529d341919fc377fed5038e.

Update latcontrol_torque.py

more lateral

Update ui
2025-11-07 00:14:57 -06:00
firestar5683 97bfbfbb0d Update carcontroller.py 2025-10-25 16:22:22 -05:00
firestar5683 67c9a8b52e Update 2025-10-25 15:46:56 -05:00
firestar5683 2f253174e9 Medium Fanta 2025-10-22 22:27:22 -05:00
firestar5683 5a39fa4ac1 Update interfaces.py 2025-10-20 13:50:27 -05:00
firestar5683 8a54eb5733 no nnff 2025-10-19 18:00:23 -05:00
firestar5683 a1edc6d6e7 Scene Complexity 2025-10-18 14:24:54 -05:00
firestar5683 05ded9d10b Update interface.py 2025-10-17 18:13:42 -05:00
firestar5683 d387c7934f CEM 2025-10-17 17:40:21 -05:00
firestar5683 4c9d393e37 lite 2025-10-17 17:14:41 -05:00
firestar5683 c14680edcb Modify torque tuning parameters in interfaces.py
Adjusted torque tuning parameters for improved performance.
2025-10-17 17:02:13 -05:00
firestar5683 95ea985218 Hurts Donut 2025-10-16 23:57:42 -05:00
firestar5683 376aeff9e3 Smoothy Boi 2025-10-16 23:40:06 -05:00
firestar5683 680c3acaf0 lat3 2025-10-15 22:21:08 -05:00
firestar5683 9cc2c20cdc Honda? 2025-10-12 00:30:14 -05:00
firestar5683 8c81eadf5d oopsie doopsie 2025-10-12 00:12:03 -05:00
firestar5683 88a29c67d0 Revert "Humanlanechanges fix"
This reverts commit 5e88e5beff.
2025-10-12 00:11:50 -05:00
firestar5683 6fe8f8b722 Revert "Duh"
This reverts commit b5b942a108.
2025-10-12 00:11:48 -05:00
firestarsdog b5b942a108 Duh 2025-10-11 20:56:35 -04:00
niknak6 5e88e5beff Humanlanechanges fix 2025-10-11 19:37:40 -04:00
firestar5683 8f27c4937c New Lateral Changes 2025-10-10 20:59:30 -05:00
firestar5683 c95f8fa8fc Fix Standard 2025-10-10 19:25:57 -05:00
firestar5683 8b55472a90 error? 2025-10-09 13:29:53 -05:00
firestar5683 9bcfeb1cf3 Honda2 2025-10-09 13:29:53 -05:00
firestar5683 93713aaacf honda 2025-10-08 22:38:53 -05:00
firestar5683 602641856d Fix New Devices 2025-10-08 22:20:55 -05:00
firestar5683 1f0d2092a3 No positive P-response for long control if user-selected parameter set 2025-10-08 07:44:53 -05:00
firestar5683 587dc140b7 Automatic updates 2025-10-04 16:03:30 -05:00
firestar5683 63ee52b4aa Steer Alerts 2025-10-04 01:48:56 -05:00
firestar5683 eb19b336e2 Update frogpilot_acceleration.py 2025-10-03 22:36:15 -05:00
firestar5683 06f95d0dcd SteerAlerts
Revert "SteerAlerts"

This reverts commit cbaba399c5b9caac5a8faf3917f0929d747a0acc.

Update controlsd.py
2025-10-03 22:14:06 -05:00
firestar5683 cd7f2f8bc6 Donut DM 2025-10-03 20:49:04 -05:00
firestar5683 82af86e04c Update gm_global_a_powertrain_generated.dbc 2025-10-03 16:41:03 -05:00
firestar5683 6934f375d5 Update 2025-10-03 00:43:23 -05:00
firestar5683 09afc383fd firehose 2025-09-30 20:47:05 -05:00
firestar5683 19f7685744 Update 2025-09-30 14:31:30 -05:00
firestar5683 6477ba6a5e Dom
Revert "Dom"

This reverts commit eb2218e733.

Dom
2025-09-30 13:06:51 -05:00
131 changed files with 8909 additions and 2826 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 {
+16
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,6 +241,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"AutomaticallyDownloadModels", PERSISTENT},
{"AutomaticUpdates", PERSISTENT},
{"AvailableModelNames", PERSISTENT},
{"AvailableModelSeries", PERSISTENT},
{"AvailableModels", PERSISTENT},
{"BigMap", PERSISTENT},
{"BlacklistedModels", PERSISTENT},
@@ -320,6 +322,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DynamicPedalsOnUI", PERSISTENT},
{"EngageVolume", PERSISTENT},
{"ExperimentalGMTune", PERSISTENT},
{"EVTuning", PERSISTENT},
{"Fahrenheit", PERSISTENT},
{"FavoriteDestinations", PERSISTENT | DONT_LOG},
{"FlashPanda", CLEAR_ON_MANAGER_START},
@@ -345,6 +348,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 +402,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 +458,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"RandomThemes", PERSISTENT},
{"RefuseVolume", PERSISTENT},
{"RelaxedFollow", PERSISTENT},
{"RelaxedFollowHigh", PERSISTENT},
{"RelaxedJerkAcceleration", PERSISTENT},
{"RelaxedJerkDanger", PERSISTENT},
{"RelaxedJerkDeceleration", PERSISTENT},
@@ -508,6 +518,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 +535,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 +551,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 +590,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:
+33 -38
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,87 @@ 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):
try:
destination.parent.mkdir(parents=True, exist_ok=True)
total_size = get_remote_file_size(url, session)
total_size = get_remote_file_size(url)
if total_size == 0:
if not url.endswith(".gif"):
handle_error(None, "Download invalid...", "Download invalid...", download_param, progress_param)
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}%")
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...")
temp_file_path.rename(destination)
except Exception as exception:
handle_request_error(exception, destination, download_param, progress_param)
except Exception as error:
handle_request_error(error, destination, download_param, progress_param, params_memory)
def get_remote_file_size(url, session):
def get_remote_file_size(url):
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:
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):
remote_file_size = get_remote_file_size(url)
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"}]}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 912 KiB

After

Width:  |  Height:  |  Size: 503 KiB

+11 -11
View File
@@ -96,9 +96,9 @@ class ThemeManager:
def download_theme(self, theme_component, theme_name, asset_param, frogpilot_toggles):
self.downloading_theme = True
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
@@ -122,16 +122,16 @@ class ThemeManager:
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)
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, params_memory)
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
delete_file(theme_path)
handle_error(None, "Download cancelled...", "Download cancelled...", asset_param, DOWNLOAD_PROGRESS_PARAM)
handle_error(None, "Download cancelled...", "Download cancelled...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_theme = False
return
if verify_download(theme_path, theme_url, self.session):
if verify_download(theme_path, theme_url):
print(f"Theme {theme_name} downloaded and verified successfully from GitHub!")
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
@@ -149,7 +149,7 @@ class ThemeManager:
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)
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):
@@ -252,7 +252,7 @@ class ThemeManager:
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
@@ -332,9 +332,9 @@ class ThemeManager:
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)
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, params_memory)
if verify_download(theme_path, theme_url, self.session):
if verify_download(theme_path, theme_url):
print(f"Theme {theme_name} downloaded and verified successfully from GitLab!")
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
@@ -350,7 +350,7 @@ class ThemeManager:
self.update_themes(frogpilot_toggles)
return True
handle_error(None, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM)
handle_error(None, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
self.downloading_theme = False
return False
@@ -563,7 +563,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
+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():
+3 -3
View File
@@ -296,12 +296,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
+153 -98
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,15 @@ 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"),
("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 +144,22 @@ 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"),
("BorderMetrics", "1", 3, "0"),
("CalibratedLateralAcceleration", str(DEFAULT_LATERAL_ACCELERATION), 2, str(DEFAULT_LATERAL_ACCELERATION)),
("CalibrationProgress", "0", 3, "0"),
("CameraView", "3", 2, "0"),
@@ -157,16 +171,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 +198,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 +207,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 +217,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 +236,7 @@ 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"),
("GithubSshKeys", "", 0, ""),
("GithubUsername", "", 0, ""),
("GoatScream", "0", 1, "0"),
@@ -235,10 +250,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 +261,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 +276,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 +318,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 +336,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 +367,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 +386,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 +425,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 +433,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 +451,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 +548,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 +560,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,6 +617,7 @@ 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
@@ -602,6 +626,13 @@ class FrogPilotVariables:
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")
ev_vehicle = toggle.car_make == "gm" and toggle.car_model != "CHEVROLET_VOLT" and CP.carFingerprint in GM_EV_CAR 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)
toggle.ev_tuning = params.get_bool("EVTuning") if advanced_longitudinal_tuning and tuning_level >= level["EVTuning"] else ev_vehicle
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 +640,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 +659,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 +699,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)
@@ -814,33 +852,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"))
@@ -906,7 +960,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"))
@@ -955,5 +1008,7 @@ class FrogPilotVariables:
toggle.volt_sng = toggle.car_model == "CHEVROLET_VOLT" 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,73 @@
#!/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.0, 1.0, 1.0]
A_CRUISE_MAX_VALS_SPORT_EV = [1.25, 1.25, 1.25, 1.25, 1.5, 1.5, 2.0]
A_CRUISE_MAX_VALS_ECO_GAS = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2]
A_CRUISE_MAX_VALS_SPORT_GAS = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6]
def get_max_accel_eco(v_ego):
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):
cruise_vals = A_CRUISE_MAX_VALS_ECO_EV if ev_tuning else 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):
cruise_vals = A_CRUISE_MAX_VALS_SPORT_EV if ev_tuning else A_CRUISE_MAX_VALS_SPORT_GAS
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals))
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
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,22 +79,23 @@ 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
if sm["frogpilotCarState"].trafficModeEnabled:
self.max_accel = get_max_accel(v_ego)
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)
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)
else:
self.max_accel = get_max_allowed_accel(v_ego)
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)
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)
elif frogpilot_toggles.acceleration_profile == 3:
self.max_accel = get_max_allowed_accel(v_ego)
else:
@@ -64,9 +105,7 @@ class FrogPilotAcceleration:
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 +118,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;
};
+25 -5
View File
@@ -269,6 +269,7 @@ 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;
@@ -277,20 +278,26 @@ void FrogPilotSettingsWindow::updateVariables() {
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 +322,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 +401,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", "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;
};
@@ -168,6 +168,7 @@ 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."), ""},
{"VoltSNG", tr("Stop-and-Go Hack"), tr("<b>Force stop-and-go</b> on the 2017 Chevy Volt."), ""},
@@ -188,6 +189,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>"), ""}
};
@@ -341,6 +343,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 +352,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 +407,10 @@ void FrogPilotVehiclesPanel::updateToggles() {
setVisible &= isHKGCanFd;
}
else if (key == "GMPedalLongitudinal") {
setVisible &= hasPedal;
}
else if (key == "VoltSNG") {
setVisible &= isVolt && !hasSNG;
}
+2 -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", "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;
@@ -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"
+1 -1
View File
@@ -222,7 +222,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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+19 -15
View File
@@ -804,7 +804,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 +813,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 +832,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

+1 -1
View File
@@ -166,7 +166,7 @@ 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))
if self.CP.enableGasInterceptor:
+29 -9
View File
@@ -8,7 +8,7 @@ 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.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
@@ -35,6 +35,10 @@ NON_LINEAR_TORQUE_PARAMS = {
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
@@ -76,20 +80,28 @@ 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):
@@ -97,6 +109,11 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)]
ret.autoResumeSng = False
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN]
# Detect Beartech SASCM allows openpilot longitudinal control on SDGM and ASCM_INT vehicles
if 0x2FF in fingerprint[0]:
ret.flags |= GMFlags.SASCM.value
if PEDAL_MSG in fingerprint[0]:
ret.enableGasInterceptor = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR
@@ -162,6 +179,8 @@ class CarInterface(CarInterfaceBase):
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
@@ -258,7 +277,7 @@ 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
ret.minEnableSpeed = -1
@@ -273,7 +292,7 @@ class CarInterface(CarInterfaceBase):
# 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.longitudinalTuning.kfDEPRECATED = 0.15
ret.stoppingDecelRate = 0.8
else: # Pedal used for SNG, ACC for longitudinal control otherwise
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
@@ -348,8 +367,9 @@ class CarInterface(CarInterfaceBase):
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 \
+2
View File
@@ -262,6 +262,8 @@ class GMFlags(IntFlag):
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,
+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.09
def get_friction_threshold(v_ego):
# Interpolate friction threshold from 0.09 at 50 mph to 0.15 at 75 mph
from openpilot.common.numpy_fast import interp
return interp(v_ego, [1 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.12, 0.25])
TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.toml')
TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.toml')
@@ -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
+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))
+47 -16
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
@@ -204,6 +206,12 @@ class Controls:
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]
+9 -11
View File
@@ -1,33 +1,31 @@
import numpy as np
from abc import abstractmethod, ABC
from openpilot.common.realtime import DT_CTRL
MIN_LATERAL_CONTROL_SPEED = 0.3 # m/s
class LatControl(ABC):
def __init__(self, CP, CI):
self.sat_count_rate = 1.0 * DT_CTRL
def __init__(self, CP, CI, dt):
self.dt = dt
self.sat_limit = CP.steerLimitTimer
self.sat_count = 0.
self.sat_time = 0.
self.sat_check_min_speed = 10.
# we define the steer torque scale as [-1.0...1.0]
self.steer_max = 1.0
@abstractmethod
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, llk, model_data, frogpilot_toggles):
def update(self, active: bool, CS, VM, params, steer_limited_by_safety: bool, desired_curvature: float, curvature_limited: bool, lat_delay: float, llk, model_data, frogpilot_toggles):
pass
def reset(self):
self.sat_count = 0.
self.sat_time = 0.
def _check_saturation(self, saturated, CS, steer_limited_by_safety, curvature_limited):
# Saturated only if control output is not being limited by car torque/angle rate limits
if (saturated or curvature_limited) and CS.vEgo > self.sat_check_min_speed and not steer_limited_by_safety and not CS.steeringPressed:
self.sat_count += self.sat_count_rate
self.sat_time += self.dt
else:
self.sat_count -= self.sat_count_rate
self.sat_count = np.clip(self.sat_count, 0.0, self.sat_limit)
return self.sat_count > (self.sat_limit - 1e-3)
self.sat_time -= self.dt
self.sat_time = np.clip(self.sat_time, 0.0, self.sat_limit)
return self.sat_time > (self.sat_limit - 1e-3)
+3 -3
View File
@@ -8,12 +8,12 @@ STEER_ANGLE_SATURATION_THRESHOLD = 2.5 # Degrees
class LatControlAngle(LatControl):
def __init__(self, CP, CI):
super().__init__(CP, CI)
def __init__(self, CP, CI, dt):
super().__init__(CP, CI, dt)
self.sat_check_min_speed = 5.
self.use_steer_limited_by_safety = CP.carName == "tesla"
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, llk, model_data, frogpilot_toggles):
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay, llk, model_data, frogpilot_toggles):
angle_log = log.ControlsState.LateralAngleState.new_message()
if not active:
+19 -7
View File
@@ -1,19 +1,23 @@
import math
from cereal import log
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
from openpilot.selfdrive.controls.lib.pid import PIDController
class LatControlPID(LatControl):
def __init__(self, CP, CI):
super().__init__(CP, CI)
def __init__(self, CP, CI, dt):
super().__init__(CP, CI, dt)
self.steer_release_i_decay = 0.8
self.prev_steering_pressed = False
self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
(CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
k_f=CP.lateralTuning.pid.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max)
pos_limit=self.steer_max, neg_limit=-self.steer_max)
self.ff_factor = CP.lateralTuning.pid.kf
self.get_steer_feedforward = CI.get_steer_feedforward_function()
self.low_speed_reset_threshold = max(CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED)
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, llk, model_data, frogpilot_toggles):
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.LateralPIDState.new_message()
pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
pid_log.steeringRateDeg = float(CS.steeringRateDeg)
@@ -27,11 +31,17 @@ class LatControlPID(LatControl):
if not active:
output_torque = 0.0
pid_log.active = False
self.pid.reset()
else:
if self.prev_steering_pressed and not CS.steeringPressed:
self.pid.i *= self.steer_release_i_decay
# offset does not contribute to resistive torque
ff = self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo)
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
ff = self.ff_factor * self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo)
if CS.vEgo < self.low_speed_reset_threshold:
self.pid.reset()
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < self.low_speed_reset_threshold
output_torque = self.pid.update(error,
feedforward=ff,
@@ -45,4 +55,6 @@ class LatControlPID(LatControl):
pid_log.output = float(output_torque)
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
self.prev_steering_pressed = CS.steeringPressed
return output_torque, angle_steers_des, pid_log
+79 -54
View File
@@ -1,45 +1,60 @@
import math
import numpy as np
from collections import deque
from cereal import log
from openpilot.selfdrive.controls.lib.drive_helpers import get_friction
from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD, get_friction_threshold
from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED, get_friction
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
from openpilot.selfdrive.controls.lib.pid import PIDController
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.frogpilot.controls.lib.neural_network_feedforward import LOW_SPEED_Y_NN, NeuralNetworkFeedforward
# At higher speeds (25+mph) we can assume:
# Lateral acceleration achieved by a specific car correlates to
# torque applied to the steering rack. It does not correlate to
# wheel slip, or to speed.
# This controller applies torque to achieve desired lateral
# accelerations. To compensate for the low speed effects we
# use a LOW_SPEED_FACTOR in the error. Additionally, there is
# friction in the steering wheel that needs to be overcome to
# move it at all, this is compensated for too.
# accelerations. To compensate for the low speed effects the
# proportional gain is increased at low speeds by the PID controller.
# Additionally, there is friction in the steering wheel that needs
# to be overcome to move it at all, this is compensated for too.
KP = 0.6
KI = 0.3
INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30]
KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP]
LOW_SPEED_X = [0, 10, 20, 30]
LOW_SPEED_Y = [15, 13, 10, 5]
LOW_SPEED_Y = [12, 10.5, 8, 5]
MAX_LAT_JERK_UP = 2.5 # m/s^3
LP_FILTER_CUTOFF_HZ = 1.2
JERK_LOOKAHEAD_SECONDS = 0.19
JERK_GAIN = 0.22
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
VERSION = 2
class LatControlTorque(LatControl):
def __init__(self, CP, FPCP, CI):
super().__init__(CP, CI)
self.torque_params = FPCP.lateralTuning.torque
def __init__(self, CP, CI, dt):
super().__init__(CP, CI, dt)
self.steer_release_i_decay = 0.8
self.prev_steering_pressed = False
self.torque_params = CP.lateralTuning.torque
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
self.lateral_accel_from_torque = CI.lateral_accel_from_torque()
self.pid = PIDController(self.torque_params.kp, self.torque_params.ki,
k_f=self.torque_params.kf)
self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, rate=1/self.dt)
self.update_limits()
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
# FrogPilot variables
self.nnff = NeuralNetworkFeedforward(CP, self)
self.nnff_loaded = self.nnff.lat_torque_nn_model != None
self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt)
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
self.lookahead_frames = int(JERK_LOOKAHEAD_SECONDS / self.dt)
self.jerk_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt)
self.previous_measurement = 0.0
self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * (MAX_LAT_JERK_UP - 0.5)), self.dt)
self.low_speed_reset_threshold = max(CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED)
def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction):
self.torque_params.latAccelFactor = latAccelFactor
@@ -51,50 +66,57 @@ class LatControlTorque(LatControl):
self.pid.set_limits(self.lateral_accel_from_torque(self.steer_max, self.torque_params),
self.lateral_accel_from_torque(-self.steer_max, self.torque_params))
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, llk, model_data, frogpilot_toggles):
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()
pid_log.version = VERSION
if not active:
output_torque = 0.0
pid_log.active = False
self.pid.reset()
self.previous_measurement = 0.0
self.measurement_rate_filter.x = 0.0
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
else:
actual_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
if self.prev_steering_pressed and not CS.steeringPressed:
self.pid.i *= self.steer_release_i_decay
measured_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
desired_lateral_accel = desired_curvature * CS.vEgo ** 2
actual_lateral_accel = actual_curvature * CS.vEgo ** 2
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2
low_speed_factor = np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y_NN if frogpilot_toggles.nnff else 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
delay_frames = int(np.clip(lat_delay / self.dt, 1, self.lat_accel_request_buffer_len))
expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames]
future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2
self.lat_accel_request_buffer.append(future_desired_lateral_accel)
raw_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / max(lat_delay, self.dt)
raw_lateral_jerk = np.clip(raw_lateral_jerk, -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
desired_lateral_jerk = np.clip(self.jerk_filter.update(raw_lateral_jerk), -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
setpoint = expected_lateral_accel + desired_lateral_jerk * lat_delay
if self.nnff_loaded and frogpilot_toggles.nnff or frogpilot_toggles.nnff_lite:
pid_log, ff = self.nnff.compute_nnff(
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
)
measurement = measured_curvature * CS.vEgo ** 2
measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt)
measurement_rate = np.clip(measurement_rate, -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
self.previous_measurement = measurement
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)
else:
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
pid_log.error = float(setpoint - measurement)
ff = gravity_adjusted_lateral_accel
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
ff -= self.torque_params.latAccelOffset
ff += get_friction(desired_lateral_accel - actual_lateral_accel, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)
low_speed_factor = (np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y) / max(CS.vEgo, MIN_SPEED)) ** 2
current_kp = np.interp(CS.vEgo, self.pid._k_p[0], self.pid._k_p[1])
error = setpoint - measurement
error_with_lsf = error * (1 + low_speed_factor / max(current_kp, 1e-3))
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
output_lataccel = self.pid.update(pid_log.error,
feedforward=ff,
speed=CS.vEgo,
freeze_integrator=freeze_integrator)
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
pid_log.error = float(error_with_lsf)
ff = gravity_adjusted_future_lateral_accel
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
ff -= self.torque_params.latAccelOffset
ff += get_friction(error_with_lsf + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, get_friction_threshold(CS.vEgo), self.torque_params)
if CS.vEgo < self.low_speed_reset_threshold:
self.pid.reset()
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < self.low_speed_reset_threshold
output_lataccel = self.pid.update(pid_log.error, error_rate=-measurement_rate, speed=CS.vEgo, feedforward=ff, freeze_integrator=freeze_integrator)
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
pid_log.active = True
pid_log.p = float(self.pid.p)
@@ -102,9 +124,12 @@ class LatControlTorque(LatControl):
pid_log.d = float(self.pid.d)
pid_log.f = float(self.pid.f)
pid_log.output = float(-output_torque) # TODO: log lat accel?
pid_log.actualLateralAccel = float(actual_lateral_accel)
pid_log.desiredLateralAccel = float(desired_lateral_accel)
pid_log.actualLateralAccel = float(measurement)
pid_log.desiredLateralAccel = float(setpoint)
pid_log.desiredLateralJerk = float(desired_lateral_jerk)
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
self.prev_steering_pressed = CS.steeringPressed
# TODO left is positive in this convention
return -output_torque, 0.0, pid_log
+78 -5
View File
@@ -4,6 +4,8 @@ from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, apply_deadzone
from openpilot.selfdrive.controls.lib.pid import PIDController
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.selfdrive.car.gm.values import CarControllerParams
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
@@ -85,17 +87,51 @@ def long_control_state_trans_old_long(CP, active, long_control_state, v_ego, v_t
return long_control_state
class LongControl:
def __init__(self, CP):
self.CP = CP
self.long_control_state = LongCtrlState.off
self.experimental_mode = False
pos_p_limit = 0.0 # if params("NoPositivePResponse") else None # put parameter-based control here
self.pid = PIDController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
k_f=CP.longitudinalTuning.kf, rate=1 / DT_CTRL)
rate=1 / DT_CTRL, pos_p_limit=pos_p_limit)
# Preserve legacy behaviour when no feedforward gain is provided (default of 0.0)
kf = getattr(CP.longitudinalTuning, 'kfDEPRECATED', 0.0)
self.feedforward_gain = kf if kf != 0.0 else 1.0
self.v_pid = 0.0
self._mode_setup()
self.last_output_accel = 0.0
def update_mpc_mode(self, experimental_mode):
new_mode = 'blended' if experimental_mode else 'acc'
if self.transitioning and self.prev_mode == 'blended' and self.current_mode == 'acc':
self.mode_transition_timer = 0.0
if new_mode != self.current_mode:
self.prev_mode = self.current_mode
self.transitioning = True
self.mode_transition_timer = 0.0
self.mode_transition_filter.x = self.last_output_accel
self.current_mode = new_mode
if self.transitioning:
self.mode_transition_timer += DT_CTRL
if self.mode_transition_timer >= self.mode_transition_duration:
self.transitioning = False
def _mode_setup(self):
self.prev_mode = 'acc'
self.current_mode = 'acc'
self.mode_transition_filter = FirstOrderFilter(0.0, 0.5, DT_CTRL)
self.mode_transition_timer = 0.0
self.mode_transition_duration = 1.0
self.transitioning = False
def reset(self):
self.pid.reset()
@@ -124,8 +160,44 @@ class LongControl:
else: # LongCtrlState.pid
error = a_target - CS.aEgo
output_accel = self.pid.update(error, speed=CS.vEgo,
feedforward=a_target)
self.update_mpc_mode(self.experimental_mode)
feedforward = a_target * self.feedforward_gain
raw_output_accel = self.pid.update(error, speed=CS.vEgo, feedforward=feedforward)
if self.transitioning and self.prev_mode == 'acc' and self.current_mode == 'blended':
if raw_output_accel < 0 and raw_output_accel < self.last_output_accel:
progress = min(1.0, self.mode_transition_timer / self.mode_transition_duration)
# Soften transition at low urgency, but keep sharp for high decel
# 20% smoother for chill decel (lower exponent)
urgency = abs(raw_output_accel / CarControllerParams.ACCEL_MIN)
urgency_smooth = min(1.0, urgency ** 0.4) # 20% smoother for chill decel
blend_factor = 1.0 - (1.0 - progress) * (1.0 - urgency_smooth)
output_accel = self.last_output_accel + (raw_output_accel - self.last_output_accel) * blend_factor
else:
output_accel = raw_output_accel
else:
output_accel = raw_output_accel
if self.long_control_state == LongCtrlState.pid:
# Smooth acceleration and deceleration with urgency-based rate limiting
base_rate = 1.0
if output_accel < self.last_output_accel: # Deceleration requested
decel_needed = self.last_output_accel - output_accel
# Use a safe default for ACCEL_MIN if not available, to prevent division by zero
max_decel = abs(CarControllerParams.ACCEL_MIN) if CarControllerParams.ACCEL_MIN != 0 else 4.0
urgency = min(1.0, decel_needed / max_decel)
# Adjust rate based on urgency (1.0 m/s^3 for low urgency, up to 4.0 m/s^3 for high urgency)
max_rate = 1.0 + 3.0 * urgency
else:
max_rate = base_rate # Acceleration is always smooth
max_accel_change = max_rate * DT_CTRL
output_accel = clip(output_accel,
self.last_output_accel - max_accel_change,
self.last_output_accel + max_accel_change)
self.last_output_accel = clip(output_accel, accel_limits[0], accel_limits[1])
return self.last_output_accel
@@ -187,8 +259,9 @@ class LongControl:
error = self.v_pid - CS.vEgo
error_deadzone = apply_deadzone(error, deadzone)
feedforward = a_target * self.feedforward_gain
output_accel = self.pid.update(error_deadzone, speed=CS.vEgo,
feedforward=a_target,
feedforward=feedforward,
freeze_integrator=freeze_integrator)
self.last_output_accel = clip(output_accel, accel_limits[0], accel_limits[1])
@@ -3,11 +3,13 @@ import os
import time
import numpy as np
from cereal import log
from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX
from openpilot.common.numpy_fast import clip, interp
from openpilot.common.realtime import DT_MDL
from openpilot.common.swaglog import cloudlog
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.conversions import Conversions as CV
# WARNING: imports outside of constants will not trigger a rebuild
from openpilot.selfdrive.modeld.constants import index_function
from openpilot.selfdrive.car.interfaces import ACCEL_MIN
if __name__ == '__main__': # generating code
from openpilot.third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver
@@ -30,12 +32,54 @@ COST_E_DIM = 5
COST_DIM = COST_E_DIM + 1
CONSTR_DIM = 4
X_EGO_OBSTACLE_COST = 3.
# ===== VOACC SPEED-BASED TUNING PARAMETERS =====
# City: Emergency-responsive | Highway: Rubber-banding prevention
# Speed ranges: [0-35, 35-55, 55-70, 70+ mph]
# SPEED BREAKPOINTS (mph)
SPEED_BREAKPOINTS = [0, 35, 55, 70] # 4 ranges: 0-35, 35-55, 55-70, 70+
# ===== CHANGE THESE VALUES FOR DIFFERENT SPEEDS =====
# RESPONSIVENESS TO LEAD CARS (Lower = More responsive, Higher = More stable)
# [City Emergency, Urban Hwy, Rural Hwy, High Speed]
X_EGO_OBSTACLE_COSTS = [3.0, 3.0, 2.5, 2.0] # Less aggressive at low speeds, closer to original
# JERK CONTROL (Lower = More jerky/responsive, Higher = Smoother/conservative)
# [City Emergency, Urban Hwy, Rural Hwy, High Speed]
J_EGO_COSTS = [5.0, 4.75, 4.5, 4.0] # Reverted to original 5.0 at low speeds
# ACCELERATION CHANGE PENALTIES (Lower = More responsive, Higher = Smoother)
# [City Emergency, Urban Hwy, Rural Hwy, High Speed]
A_CHANGE_COSTS = [200, 195, 180, 170] # Reverted to original 200 at low speeds
# SMOOTHING FILTERS - Speed-adaptive for optimal responsiveness
# Lower = More responsive, Higher = Smoother
LEAD_FILTER_TIME_LOW = 0.8 # Under 40 mph: Fast response for city emergency braking
LEAD_FILTER_TIME_HIGH = 1.2 # Over 40 mph: Faster response to prevent highway gaps
SPEED_FILTER_THRESHOLD = 40 * CV.MPH_TO_MS # 40 mph threshold
# DISTANCE ADAPTATION STRENGTH (How much penalties increase when close to lead)
# [City, Urban Hwy, Rural Hwy, High Speed]
DIST_ADAPTS = [0.04, 0.06, 0.06, 0.05] # Balanced across speeds
# ===== END TUNING PARAMETERS =====
# Function to get parameter value based on current speed
def get_speed_based_param(speed_mph, param_array):
"""Get parameter value based on current speed using smooth interpolation"""
return np.interp(speed_mph, SPEED_BREAKPOINTS, param_array)
# Current active values (set based on speed)
X_EGO_OBSTACLE_COST = 2.75
J_EGO_COST = 5.5
A_CHANGE_COST = 250.0
LEAD_FILTER_TIME = 2.0
DIST_ADAPT = 0.06
X_EGO_COST = 0.
V_EGO_COST = 0.
A_EGO_COST = 0.
J_EGO_COST = 5.0
A_CHANGE_COST = 200.
DANGER_ZONE_COST = 100.
CRASH_DISTANCE = .25
LEAD_DANGER_FACTOR = 0.75
@@ -55,9 +99,6 @@ T_IDXS = np.array(T_IDXS_LST)
FCW_IDXS = T_IDXS < 5.0
T_DIFFS = np.diff(T_IDXS, prepend=[0.])
COMFORT_BRAKE = 2.5
STOP_DISTANCE = 6.0
CRUISE_MIN_ACCEL = -1.2
CRUISE_MAX_ACCEL = 1.6
def get_jerk_factor(aggressive_jerk_acceleration=0.5, aggressive_jerk_danger=0.5, aggressive_jerk_speed=0.5,
standard_jerk_acceleration=1.0, standard_jerk_danger=1.0, standard_jerk_speed=1.0,
@@ -107,7 +148,11 @@ def get_stopped_equivalence_factor(v_lead):
return (v_lead**2) / (2 * COMFORT_BRAKE)
def get_safe_obstacle_distance(v_ego, t_follow):
return (v_ego**2) / (2 * COMFORT_BRAKE) + t_follow * v_ego + STOP_DISTANCE
from openpilot.common.params import Params
params = Params()
stop_str = params.get("StopDistance", encoding="utf8")
stop_distance = float(stop_str) if stop_str else 6.0
return (v_ego**2) / (2 * COMFORT_BRAKE) + t_follow * v_ego + stop_distance
def desired_follow_distance(v_ego, v_lead, t_follow=None):
if t_follow is None:
@@ -188,11 +233,12 @@ def gen_long_ocp():
# from an obstacle at every timestep. This obstacle can be a lead car
# or other object. In e2e mode we can use x_position targets as a cost
# instead.
accel_change = a_ego - prev_a
costs = [((x_obstacle - x_ego) - (desired_dist_comfort)) / (v_ego + 10.),
x_ego,
v_ego,
a_ego,
a_ego - prev_a,
accel_change,
j_ego]
ocp.model.cost_y_expr = vertcat(*costs)
ocp.model.cost_y_expr_e = vertcat(*costs[:-1])
@@ -250,8 +296,23 @@ class LongitudinalMpc:
self.mode = mode
self.dt = dt
self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N)
self.reset()
self.source = SOURCES[2]
# Initialize smoothing filters with default time constants
self.current_filter_time = LEAD_FILTER_TIME_LOW
self.lead_a_filter = FirstOrderFilter(0.0, self.current_filter_time, self.dt)
self.lead_v_filter = FirstOrderFilter(0.0, self.current_filter_time, self.dt)
# Slew-limited filter factor to avoid abrupt 0.50↔1.00 jumps
self.filter_time_factor = 1.0
self.slew_per_sec = 1.0
# Instance variables to avoid global modifications
self.current_x_ego_cost = X_EGO_OBSTACLE_COSTS[0]
self.current_j_ego_cost = J_EGO_COSTS[0]
self.current_a_change_cost = A_CHANGE_COSTS[0]
self.current_dist_adapt = DIST_ADAPTS[0]
# Initialize acceleration limits to prevent AttributeError
self.cruise_min_a = ACCEL_MIN
self.max_a = 1.2 # Default max acceleration
self.reset()
def reset(self):
# self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N)
@@ -298,10 +359,71 @@ class LongitudinalMpc:
for i in range(N):
self.solver.cost_set(i, 'Zl', Zl)
def set_weights(self, acceleration_jerk=1.0, danger_jerk=1.0, speed_jerk=1.0, prev_accel_constraint=True, personality=log.LongitudinalPersonality.standard):
def set_weights(self, acceleration_jerk=1.0, danger_jerk=1.0, speed_jerk=1.0, prev_accel_constraint=True,
personality=log.LongitudinalPersonality.standard, v_ego=0.0, lead_dist=50.0,
uncertainty=0.0, accel_reengage=False, panic_bypass=False):
# Update parameters based on current speed with interpolation for smooth scaling
speed_mph = v_ego * CV.MS_TO_MPH # Convert m/s to mph
# Use speed-based parameters for smooth scaling across all breakpoints
self.current_x_ego_cost = get_speed_based_param(speed_mph, X_EGO_OBSTACLE_COSTS)
self.current_j_ego_cost = get_speed_based_param(speed_mph, J_EGO_COSTS)
self.current_a_change_cost = get_speed_based_param(speed_mph, A_CHANGE_COSTS)
# For dist_adapt, start from 0.0 under low speeds while enabling full smooth transitions
dist_adapt_array = [0.0, DIST_ADAPTS[1], DIST_ADAPTS[2], DIST_ADAPTS[3]]
self.current_dist_adapt = get_speed_based_param(speed_mph, dist_adapt_array)
# Update filter time constants with interp and recreate filters if needed
if speed_mph < 47:
self.current_filter_time = 0.0
else:
self.current_filter_time = interp(speed_mph, [47, 65], [0.0, LEAD_FILTER_TIME_HIGH])
if abs(self.current_filter_time - getattr(self, 'prev_filter_time', 0)) > 0.1: # Only update if significant change
# Recreate filters with new time constant while preserving current values
current_a = self.lead_a_filter.x if hasattr(self.lead_a_filter, 'x') else 0.0
current_v = self.lead_v_filter.x if hasattr(self.lead_v_filter, 'x') else 0.0
self.lead_a_filter = FirstOrderFilter(current_a, self.current_filter_time, self.dt)
self.lead_v_filter = FirstOrderFilter(current_v, self.current_filter_time, self.dt)
self.prev_filter_time = self.current_filter_time
# Adaptive jerk factors for distance with interp scaling
dist_factor = 1.0 + self.current_dist_adapt * (20.0 / max(lead_dist, 5.0))
acceleration_jerk *= dist_factor
danger_jerk *= dist_factor
speed_jerk *= dist_factor
# Scene complexity adjustment based on model uncertainty
prev_filter_time_factor = getattr(self, 'prev_filter_time_factor', 1.0)
# Target factor from uncertainty
if uncertainty <= 0.45:
tgt_factor = 1.0
elif uncertainty >= 0.70:
tgt_factor = 0.0
else:
tgt_factor = float(np.interp(uncertainty, [0.45, 0.70], [1.0, 0.30]))
if accel_reengage:
tgt_factor = min(tgt_factor, 0.5)
# Hard bypass of smoothing when approaching fast or magnitude trips
if panic_bypass:
tgt_factor = 0.0
# Slew-limit changes to avoid step-wise filter jumps
max_step = self.slew_per_sec * self.dt
delta = np.clip(tgt_factor - self.filter_time_factor, -max_step, max_step)
self.filter_time_factor += float(delta)
filter_time_factor = float(self.filter_time_factor)
# When uncertainty is moderately elevated, allow accel but cap jerk by increasing jerk cost
if 0.45 <= uncertainty < 0.60:
scale = float(np.interp(uncertainty, [0.45, 0.60], [1.2, 1.5]))
speed_jerk *= scale
if self.mode == 'acc':
a_change_cost = acceleration_jerk if prev_accel_constraint else 0
cost_weights = [X_EGO_OBSTACLE_COST, X_EGO_COST, V_EGO_COST, A_EGO_COST, a_change_cost, speed_jerk]
cost_weights = [self.current_x_ego_cost, X_EGO_COST, V_EGO_COST, A_EGO_COST, a_change_cost, speed_jerk]
constraint_cost_weights = [LIMIT_COST, LIMIT_COST, LIMIT_COST, danger_jerk]
elif self.mode == 'blended':
a_change_cost = 40.0 if prev_accel_constraint else 0
@@ -311,6 +433,15 @@ class LongitudinalMpc:
raise NotImplementedError(f'Planner mode {self.mode} not recognized in planner cost set')
self.set_cost_weights(cost_weights, constraint_cost_weights)
# Adjust filter time constants for complex scenes
if abs(filter_time_factor - getattr(self, 'prev_filter_time_factor', 1.0)) > 0.05:
current_a = self.lead_a_filter.x if hasattr(self.lead_a_filter, 'x') else 0.0
current_v = self.lead_v_filter.x if hasattr(self.lead_v_filter, 'x') else 0.0
new_filter_time = self.current_filter_time * filter_time_factor
self.lead_a_filter = FirstOrderFilter(current_a, new_filter_time, self.dt)
self.lead_v_filter = FirstOrderFilter(current_v, new_filter_time, self.dt)
self.prev_filter_time_factor = filter_time_factor
def set_cur_state(self, v, a):
v_prev = self.x0[1]
self.x0[1] = v
@@ -320,16 +451,34 @@ class LongitudinalMpc:
self.solver.set(i, 'x', self.x0)
@staticmethod
def extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau):
a_lead_traj = a_lead * np.exp(-a_lead_tau * (T_IDXS**2)/2.)
v_lead_traj = np.clip(v_lead + np.cumsum(T_DIFFS * a_lead_traj), 0.0, 1e8)
x_lead_traj = x_lead + np.cumsum(T_DIFFS * v_lead_traj)
def extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau, v_ego=0.0):
speed_mph = v_ego * CV.MS_TO_MPH
bp = [0, 20, 35]
exp_weight = interp(speed_mph, bp, [1.0, 1.0, 0.0]) # Full exp at <20, blend to constant at 35
if exp_weight > 0:
# Exponential decay component
a_lead_traj_exp = a_lead * np.exp(-a_lead_tau * (T_IDXS**2)/2.)
v_lead_traj_exp = np.clip(v_lead + np.cumsum(T_DIFFS * a_lead_traj_exp), 0.0, 1e8)
x_lead_traj_exp = x_lead + np.cumsum(T_DIFFS * v_lead_traj_exp)
else:
x_lead_traj_exp = np.zeros_like(T_IDXS)
v_lead_traj_exp = np.zeros_like(T_IDXS)
# Constant acceleration component
v_lead_traj_const = np.clip(v_lead + a_lead * T_IDXS, 0.0, 1e8)
x_lead_traj_const = x_lead + v_lead * T_IDXS + 0.5 * a_lead * T_IDXS**2
# Blend based on weight
v_lead_traj = exp_weight * v_lead_traj_exp + (1 - exp_weight) * v_lead_traj_const
x_lead_traj = exp_weight * x_lead_traj_exp + (1 - exp_weight) * x_lead_traj_const
lead_xv = np.column_stack((x_lead_traj, v_lead_traj))
return lead_xv
def process_lead(self, lead):
def process_lead(self, lead, tracking_lead=True):
v_ego = self.x0[1]
if lead is not None and lead.status:
if lead is not None and lead.status and tracking_lead:
x_lead = lead.dRel
v_lead = lead.vLead
a_lead = lead.aLeadK
@@ -344,18 +493,29 @@ class LongitudinalMpc:
# MPC will not converge if immediate crash is expected
# Clip lead distance to what is still possible to brake for
min_x_lead = ((v_ego + v_lead)/2) * (v_ego - v_lead) / (-ACCEL_MIN * 2)
x_lead = np.clip(x_lead, min_x_lead, 1e8)
v_lead = np.clip(v_lead, 0.0, 1e8)
a_lead = np.clip(a_lead, -10., 5.)
lead_xv = self.extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau)
x_lead = clip(x_lead, min_x_lead, 1e8)
v_lead = clip(v_lead, 0.0, 1e8)
a_lead = clip(a_lead, -10., 5.)
# Apply smoothing filters with interp scaling
self.lead_a_filter.update(a_lead)
self.lead_v_filter.update(v_lead)
a_lead = self.lead_a_filter.x
v_lead = self.lead_v_filter.x
lead_xv = self.extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau, v_ego)
return lead_xv
def update(self, radarstate, v_cruise, x, v, a, j, t_follow, frogpilot_toggles, personality=log.LongitudinalPersonality.standard):
v_ego = self.x0[1]
self.status = radarstate.leadOne.status or radarstate.leadTwo.status
def set_accel_limits(self, min_a, max_a):
# TODO this sets a max accel limit, but the minimum limit is only for cruise decel
# needs refactor
self.cruise_min_a = min_a
self.max_a = max_a
lead_xv_0 = self.process_lead(radarstate.leadOne)
lead_xv_1 = self.process_lead(radarstate.leadTwo)
def update(self, lead_one, lead_two, v_cruise, x, v, a, j, t_follow, tracking_lead, personality=log.LongitudinalPersonality.standard):
v_ego = self.x0[1]
self.status = lead_one.status and tracking_lead or lead_two.status
lead_xv_0 = self.process_lead(lead_one, tracking_lead)
lead_xv_1 = self.process_lead(lead_two, v_ego)
# To estimate a safe distance from a moving lead, we calculate how much stopping
# distance that lead needs as a minimum. We can add that to the current distance
@@ -364,7 +524,8 @@ class LongitudinalMpc:
lead_1_obstacle = lead_xv_1[:,0] + get_stopped_equivalence_factor(lead_xv_1[:,1])
self.params[:,0] = ACCEL_MIN
self.params[:,1] = ACCEL_MAX
# negative accel constraint causes problems because negative speed is not allowed
self.params[:,1] = max(0.0, self.max_a)
# Update in ACC mode or ACC/e2e blend
if self.mode == 'acc':
@@ -372,9 +533,9 @@ class LongitudinalMpc:
# Fake an obstacle for cruise, this ensures smooth acceleration to set speed
# when the leads are no factor.
v_lower = v_ego + (T_IDXS * CRUISE_MIN_ACCEL * 1.05)
v_lower = v_ego + (T_IDXS * self.cruise_min_a * 1.05)
# TODO does this make sense when max_a is negative?
v_upper = v_ego + (T_IDXS * CRUISE_MAX_ACCEL * 1.05)
v_upper = v_ego + (T_IDXS * self.max_a * 1.05)
v_cruise_clipped = np.clip(v_cruise * np.ones(N+1),
v_lower,
v_upper)
@@ -415,8 +576,8 @@ class LongitudinalMpc:
self.params[:,4] = t_follow
self.run()
if (np.any(lead_xv_0[FCW_IDXS,0] - self.x_sol[FCW_IDXS,0] < CRASH_DISTANCE) and
radarstate.leadOne.modelProb > 0.9):
lead_probability = lead_one.modelProb
if (np.any(lead_xv_0[FCW_IDXS,0] - self.x_sol[FCW_IDXS,0] < CRASH_DISTANCE) and lead_probability > 0.9):
self.crash_cnt += 1
else:
self.crash_cnt = 0
@@ -462,11 +623,7 @@ class LongitudinalMpc:
self.prev_a = np.interp(T_IDXS + self.dt, T_IDXS, self.a_solution)
t = time.monotonic()
if self.solution_status != 0:
if t > self.last_cloudlog_t + 5.0:
self.last_cloudlog_t = t
cloudlog.warning(f"Long mpc reset, solution_status: {self.solution_status}")
self.reset()
# reset = 1
# print(f"long_mpc timings: total internal {self.solve_time:.2e}, external: {(time.monotonic() - t0):.2e} qp {self.time_qp_solution:.2e}, \
+323 -58
View File
@@ -1,6 +1,8 @@
#!/usr/bin/env python3
import math
import numpy as np
import time
from openpilot.common.numpy_fast import clip, interp
import cereal.messaging as messaging
from openpilot.common.conversions import Conversions as CV
@@ -11,25 +13,28 @@ from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX
from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, V_CRUISE_UNSET, CONTROL_N, get_accel_from_plan
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_UNSET, CONTROL_N, get_speed_error, get_accel_from_plan_tomb_raider
from openpilot.common.swaglog import cloudlog
from openpilot.frogpilot.common.frogpilot_variables import MINIMUM_LATERAL_ACCELERATION
LON_MPC_STEP = 0.2 # first step is 0.2s
A_CRUISE_MIN = -1.2
A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6]
A_CRUISE_MAX_BP = [0., 10.0, 25., 40.]
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
ALLOW_THROTTLE_THRESHOLD = 0.4
ALLOW_THROTTLE_THRESHOLD = 0.5
MIN_ALLOW_THROTTLE_SPEED = 2.5
# Uncertainty-based filter disable thresholds
UNCERT_SLOPE_TRIG = 0.12 # per second
UNCERT_MAG_TRIG = 0.50
# Lookup table for turns
_A_TOTAL_MAX_V = [1.7, 3.2]
_A_TOTAL_MAX_BP = [20., 40.]
def get_max_accel(v_ego):
return float(np.interp(v_ego, A_CRUISE_MAX_BP, A_CRUISE_MAX_VALS))
return interp(v_ego, A_CRUISE_MAX_BP, A_CRUISE_MAX_VALS)
def get_coast_accel(pitch):
return np.sin(pitch) * -5.65 - 0.3 # fitted from data using xx/projects/allow_throttle/compute_coast_accel.py
@@ -42,45 +47,114 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP):
"""
# FIXME: This function to calculate lateral accel is incorrect and should use the VehicleModel
# The lookup table for turns should also be updated if we do this
a_total_max = np.interp(v_ego, _A_TOTAL_MAX_BP, _A_TOTAL_MAX_V)
a_total_max = interp(v_ego, _A_TOTAL_MAX_BP, _A_TOTAL_MAX_V)
a_y = v_ego ** 2 * angle_steers * CV.DEG_TO_RAD / (CP.steerRatio * CP.wheelbase)
if abs(a_y) > MINIMUM_LATERAL_ACCELERATION:
a_x_allowed = math.sqrt(max(a_total_max ** 2 - a_y ** 2, 0.))
else:
a_x_allowed = a_target[1]
a_x_allowed = math.sqrt(max(a_total_max ** 2 - a_y ** 2, 0.))
return [a_target[0], min(a_target[1], a_x_allowed)]
def get_accel_from_plan_classic(CP, speeds, accels, vEgoStopping):
if len(speeds) == CONTROL_N:
v_target_now = interp(DT_MDL, CONTROL_N_T_IDX, speeds)
a_target_now = interp(DT_MDL, CONTROL_N_T_IDX, accels)
v_target = interp(CP.longitudinalActuatorDelay + DT_MDL, CONTROL_N_T_IDX, speeds)
if v_target != v_target_now:
a_target = 2 * (v_target - v_target_now) / CP.longitudinalActuatorDelay - a_target_now
else:
a_target = a_target_now
v_target_1sec = interp(CP.longitudinalActuatorDelay + DT_MDL + 1.0, CONTROL_N_T_IDX, speeds)
else:
v_target = 0.0
v_target_1sec = 0.0
a_target = 0.0
should_stop = (v_target < vEgoStopping and
v_target_1sec < vEgoStopping)
return a_target, should_stop
def get_accel_from_plan(speeds, accels, action_t=DT_MDL, vEgoStopping=0.05):
if len(speeds) == CONTROL_N:
v_now = speeds[0]
a_now = accels[0]
v_target = interp(action_t, CONTROL_N_T_IDX, speeds)
a_target = 2 * (v_target - v_now) / (action_t) - a_now
v_target_1sec = interp(action_t + 1.0, CONTROL_N_T_IDX, speeds)
else:
v_target = 0.0
v_target_1sec = 0.0
a_target = 0.0
should_stop = (v_target < vEgoStopping and
v_target_1sec < vEgoStopping)
return a_target, should_stop
class LongitudinalPlanner:
def __init__(self, CP, init_v=0.0, init_a=0.0, dt=DT_MDL):
self.CP = CP
self.mpc = LongitudinalMpc(dt=dt)
# TODO remove mpc modes when TR released
self.mpc.mode = 'acc'
self.fcw = False
self.dt = dt
self.allow_throttle = True
self.mode = 'acc'
self.generation = None
self.a_desired = init_a
self.v_desired_filter = FirstOrderFilter(init_v, 2.0, self.dt)
self.prev_accel_clip = [ACCEL_MIN, ACCEL_MAX]
self.output_a_target = 0.0
self.output_should_stop = False
self.v_model_error = 0.0
self.v_desired_trajectory = np.zeros(CONTROL_N)
self.a_desired_trajectory = np.zeros(CONTROL_N)
self.j_desired_trajectory = np.zeros(CONTROL_N)
self.solverExecutionTime = 0.0
# ---- Rubberband mitigation state ----
# Two uncertainty tracks (slow/fast) for asymmetric gating
self.uncert_slow = FirstOrderFilter(0.0, 1.6, self.dt) # ~lam=0.6
self.uncert_fast = FirstOrderFilter(0.0, 0.9, self.dt) # faster cool-down for accel decisions
# Lead stability tracking
self.prev_lead_dist = None
self.last_big_brake_t = 0.0
self.stable_lead = False
# Temporary accel nudge window
self.accel_nudge_until = 0.0
# Hysteresis gate + dwell for accel re-engage and smoothed lead distance
self.accel_gate = False
self._t_arm = 0.0
self._t_disarm = 0.0
self.lead_dist_f = None
# Uncertainty slope tracking
self._uncert_last = 0.0
self._uncert_last_t = None
@property
def mlsim(self):
return self.generation in ("v8", "v10", "v11", "v12")
def get_mpc_mode(self) -> str:
"""
Determine the desired MPC mode: if not ML-SIM, MPC should follow self.mode;
otherwise leave MPC.mode unchanged.
"""
# For non-ML-SIM generations, MPC mode tracks self.mode
if not self.mlsim:
return self.mode
# For ML-SIM (v8), preserve the existing MPC mode
return getattr(self.mpc, 'mode', 'acc')
@staticmethod
def parse_model(model_msg, v_ego, taco_tune):
def parse_model(model_msg, model_error, v_ego, taco_tune):
if (len(model_msg.position.x) == ModelConstants.IDX_N and
len(model_msg.velocity.x) == ModelConstants.IDX_N and
len(model_msg.acceleration.x) == ModelConstants.IDX_N):
x = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.position.x)
v = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.velocity.x)
x = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.position.x) - model_error * T_IDXS_MPC
v = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.velocity.x) - model_error
a = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.acceleration.x)
j = np.zeros(len(T_IDXS_MPC))
else:
@@ -90,7 +164,7 @@ class LongitudinalPlanner:
j = np.zeros(len(T_IDXS_MPC))
if taco_tune:
max_lat_accel = np.interp(v_ego, [5, 10, 20], [1.5, 2.0, 3.0])
max_lat_accel = interp(v_ego, [5, 10, 20], [1.5, 2.0, 3.0])
curvatures = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.orientationRate.z) / np.clip(v, 0.3, 100.0)
max_v = np.sqrt(max_lat_accel / (np.abs(curvatures) + 1e-3)) - 2.0
v = np.minimum(max_v, v)
@@ -101,17 +175,22 @@ class LongitudinalPlanner:
throttle_prob = 1.0
return x, v, a, j, throttle_prob
def update(self, sm, classic_longitudinal, frogpilot_toggles):
mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
if classic_longitudinal:
self.mpc.mode = mode
def update(self, tinygrad_model, sm, frogpilot_toggles):
self.generation = frogpilot_toggles.model_version
if tinygrad_model:
self.mpc.mode = 'acc'
self.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
else:
self.mpc.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
if not self.mlsim:
self.mpc.mode = self.mode
if len(sm['carControl'].orientationNED) == 3:
accel_coast = get_coast_accel(sm['carControl'].orientationNED[1])
else:
accel_coast = ACCEL_MAX
v_ego = sm['carState'].vEgo
v_ego = max(sm['carState'].vEgo, sm['carState'].vEgoCluster)
v_cruise = sm['frogpilotPlan'].vCruise
v_cruise_initialized = sm['controlsState'].vCruise != V_CRUISE_UNSET
@@ -126,36 +205,190 @@ class LongitudinalPlanner:
# No change cost when user is controlling the speed, or when standstill
prev_accel_constraint = not (reset_state or sm['carState'].standstill)
if mode == 'acc':
accel_clip = [sm['frogpilotPlan'].minAcceleration, sm['frogpilotPlan'].maxAcceleration]
if self.mpc.mode == 'acc':
accel_limits = [sm['frogpilotPlan'].minAcceleration, sm['frogpilotPlan'].maxAcceleration]
steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg
if not sm['frogpilotPlan'].cscControllingSpeed:
accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP)
accel_limits_turns = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_limits, self.CP)
else:
accel_clip = [ACCEL_MIN, ACCEL_MAX]
accel_limits = [ACCEL_MIN, ACCEL_MAX]
accel_limits_turns = [ACCEL_MIN, ACCEL_MAX]
if reset_state:
self.v_desired_filter.x = v_ego
# Clip aEgo to cruise limits to prevent large accelerations when becoming active
self.a_desired = np.clip(sm['carState'].aEgo, accel_clip[0], accel_clip[1])
self.a_desired = clip(sm['carState'].aEgo, accel_limits[0], accel_limits[1])
# Prevent divergence, smooth in current v_ego
self.v_desired_filter.x = max(0.0, self.v_desired_filter.update(v_ego))
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], v_ego, frogpilot_toggles.taco_tune)
# Compute model v_ego error
self.v_model_error = get_speed_error(sm['modelV2'], v_ego)
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], self.v_model_error, v_ego, frogpilot_toggles.taco_tune)
# Don't clip at low speeds since throttle_prob doesn't account for creep
self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED
self.allow_throttle &= not sm['frogpilotPlan'].disableThrottle
if not self.allow_throttle:
clipped_accel_coast = max(accel_coast, accel_clip[0])
clipped_accel_coast_interp = np.interp(v_ego, [MIN_ALLOW_THROTTLE_SPEED, MIN_ALLOW_THROTTLE_SPEED*2], [accel_clip[1], clipped_accel_coast])
accel_clip[1] = min(accel_clip[1], clipped_accel_coast_interp)
clipped_accel_coast = max(accel_coast, accel_limits_turns[0])
clipped_accel_coast_interp = interp(v_ego, [MIN_ALLOW_THROTTLE_SPEED, MIN_ALLOW_THROTTLE_SPEED*2], [accel_limits_turns[1], clipped_accel_coast])
accel_limits_turns[1] = min(accel_limits_turns[1], clipped_accel_coast_interp)
if force_slow_decel:
v_cruise = 0.0
# clip limits, cannot init MPC outside of bounds
accel_limits_turns[0] = min(accel_limits_turns[0], self.a_desired + 0.05)
accel_limits_turns[1] = max(accel_limits_turns[1], self.a_desired - 0.05)
self.mpc.set_weights(sm['frogpilotPlan'].accelerationJerk, sm['frogpilotPlan'].dangerJerk, sm['frogpilotPlan'].speedJerk, prev_accel_constraint, personality=sm['controlsState'].personality)
self.lead_one = sm['radarState'].leadOne
self.lead_two = sm['radarState'].leadTwo
lead_dist = self.lead_one.dRel if self.lead_one.status else 50.0
# Smooth lead distance (EMA) to avoid chatter in thresholds
alpha = max(0.02, min(0.15, 0.05 + 0.002 * v_ego))
if self.lead_dist_f is None:
self.lead_dist_f = float(lead_dist)
else:
self.lead_dist_f += alpha * (float(lead_dist) - self.lead_dist_f)
# Lead stability estimation and recent-brake timer
now_t = time.monotonic()
# relative speed (ego - lead) positive when closing
v_rel = (v_ego - self.lead_one.vLead) if self.lead_one.status else 0.0
if self.prev_lead_dist is None:
d_rel_dot = 0.0
else:
d_rel_dot = (lead_dist - self.prev_lead_dist) / max(self.dt, 1e-3)
self.prev_lead_dist = lead_dist
# Remember time of last non-trivial model brake risk
if 'raw_brake_max' in locals() and raw_brake_max is not None and raw_brake_max > 0.02:
self.last_big_brake_t = now_t
# Stable lead heuristic (short window, cheap to compute)
recently_braked = (now_t - self.last_big_brake_t) < 0.7
self.stable_lead = (
self.lead_one.status and
abs(v_rel) < 0.5 and
abs(d_rel_dot) < 0.5 and
not recently_braked
)
# Calculate scene uncertainty from model desire prediction entropy and disengage predictions
uncertainty = 0.0
if hasattr(sm['modelV2'], 'meta'):
# Desire prediction entropy (maneuver uncertainty), normalized to [0, 1]
desire_entropy = 0.0
if hasattr(sm['modelV2'].meta, 'desirePrediction'):
desire_probs = sm['modelV2'].meta.desirePrediction
if len(desire_probs) > 1:
probs = np.asarray(desire_probs, dtype=float)
total = float(np.sum(probs))
if total > 1e-6:
p = probs / total
entropy = -np.sum(p * np.log(p + 1e-10))
max_entropy = np.log(len(p))
desire_entropy = float(entropy / max(max_entropy, 1e-6)) # normalized entropy in [0,1]
else:
desire_entropy = 0.0 # guard against all-zero vector
# Disengage prediction risk (intervention likelihood)
disengage_risk = 0.0
raw_brake_max = -1.0
lam = -1.0
if hasattr(sm['modelV2'].meta, 'disengagePredictions'):
# Use brake press probabilities as primary risk indicator
brake_probs = sm['modelV2'].meta.disengagePredictions.brakePressProbs
if len(brake_probs) > 0:
# Exponentially decayed max over the full horizon
probs = np.asarray(brake_probs, dtype=float)
# Clip tiny brake blips so they don't inflate uncertainty
if float(np.max(probs)) < 0.015:
probs = probs * 0.5
raw_brake_max = float(np.max(probs))
# Time vector assuming model horizon step = DT_MDL
t = np.arange(len(probs), dtype=float) * DT_MDL
lam = 0.6 # decay rate per second (tunable: 0.50.9 typical)
weights = np.exp(-lam * t)
disengage_risk = float(np.max(probs * weights))
# Combined uncertainty metric (range roughly 0..2), with dual-track filtering
raw_uncertainty = desire_entropy + disengage_risk
# Update filters
self.uncert_slow.update(raw_uncertainty)
self.uncert_fast.update(raw_uncertainty)
# Use a more permissive track for accel decisions
uncertainty = self.uncert_slow.x
uncertainty_accel = min(self.uncert_slow.x, self.uncert_fast.x)
# --- Slope-based panic bypass ---
if self._uncert_last_t is None:
uncert_slope = 0.0
else:
dt_u = max(1e-3, now_t - self._uncert_last_t)
uncert_slope = (uncertainty - self._uncert_last) / dt_u
self._uncert_last = uncertainty
self._uncert_last_t = now_t
closing_fast = (self.lead_one.status and (v_ego - self.lead_one.vLead) > 0.5)
# Trigger if either slope is high or magnitude is high; require a valid lead and closing
panic_bypass = closing_fast and (uncert_slope > UNCERT_SLOPE_TRIG or uncertainty >= UNCERT_MAG_TRIG)
if panic_bypass:
try:
cloudlog.error(f"LON_SLOPE; slope={uncert_slope:.3f}/s; uncertainty={uncertainty:.3f}; v_ego={v_ego:.2f}; v_rel={(v_ego - self.lead_one.vLead) if self.lead_one.status else 0.0:.2f}; lead_dist={self.lead_dist_f if self.lead_dist_f is not None else -1:.2f}; trigger=True")
except Exception:
pass
# now_t defined earlier
over = uncertainty > 1.0
# Asymmetric accel release with hysteresis + dwell to prevent on/off pulsing
rise_dwell_s, fall_dwell_s = 0.6, 0.4
good = (
(self.a_desired > 0.0) and
self.stable_lead and
(uncertainty <= 0.425) and
(desire_entropy < 0.41)
)
# dwell timers for robust gating
if good and not self.accel_gate:
if now_t - self._t_arm >= rise_dwell_s:
self.accel_gate = True
else:
self._t_arm = now_t
if (not good) and self.accel_gate:
if now_t - self._t_disarm >= fall_dwell_s:
self.accel_gate = False
else:
self._t_disarm = now_t
if self.accel_gate:
# Ensure some positive headroom for MPC to exit coasting
accel_limits_turns[1] = max(accel_limits_turns[1], 0.2)
# Short self-canceling nudge to unstick (applied post-MPC)
if now_t > self.accel_nudge_until:
self.accel_nudge_until = now_t + 0.45
self.mpc.set_weights(sm['frogpilotPlan'].accelerationJerk,
sm['frogpilotPlan'].dangerJerk,
sm['frogpilotPlan'].speedJerk,
prev_accel_constraint,
personality=sm['controlsState'].personality,
v_ego=v_ego,
lead_dist=self.lead_dist_f if self.lead_dist_f is not None else lead_dist,
uncertainty=uncertainty,
accel_reengage=self.accel_gate,
panic_bypass=panic_bypass)
self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1])
self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, sm['frogpilotPlan'].tFollow, frogpilot_toggles, personality=sm['controlsState'].personality)
# After deciding the MPC mode via get_mpc_mode(), ensure MPC uses that mode when not mlsim
dec_mpc_mode = self.get_mpc_mode()
if not self.mlsim:
self.mpc.mode = dec_mpc_mode
self.mpc.update(self.lead_one, self.lead_two, v_cruise, x, v, a, j, sm['frogpilotPlan'].tFollow,
sm['frogpilotPlan'].trackingLead, personality=sm['controlsState'].personality)
self.a_desired_trajectory_full = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution)
self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution)
@@ -167,30 +400,41 @@ class LongitudinalPlanner:
if self.fcw:
cloudlog.info("FCW triggered")
# Safety checks for rubber-banding mitigation
max_jerk = np.max(np.abs(self.mpc.j_solution))
max_accel_change = np.max(np.abs(np.diff(self.mpc.a_solution)))
if max_jerk > 5.0: # m/s^3
cloudlog.warning(f"High jerk detected: {max_jerk:.2f} m/s^3")
if max_accel_change > 2.0: # m/s^2
cloudlog.warning(f"High acceleration change: {max_accel_change:.2f} m/s^2")
# Interpolate 0.05 seconds and save as starting point for next iteration
a_prev = self.a_desired
self.a_desired = float(np.interp(self.dt, CONTROL_N_T_IDX, self.a_desired_trajectory))
self.a_desired = float(interp(self.dt, CONTROL_N_T_IDX, self.a_desired_trajectory))
self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0
action_t = frogpilot_toggles.longitudinalActuatorDelay + DT_MDL
output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX,
action_t=action_t, vEgoStopping=frogpilot_toggles.vEgoStopping)
output_a_target_e2e = sm['modelV2'].action.desiredAcceleration
output_should_stop_e2e = sm['modelV2'].action.shouldStop
# Anticipatory pre-brake to avoid "coming in hot" when closing on a lead
if self.lead_one.status:
rel_v = max(0.0, v_ego - self.lead_one.vLead)
# dynamic time headway adds a small buffer when uncertainty is elevated
base_th = 1.6
th = base_th + 0.6 * max(0.0, uncertainty - 0.42)
desired_gap = th * v_ego
if (self.lead_dist_f is not None and self.lead_dist_f < desired_gap and rel_v > 0.5):
k_rel, k_unc = 0.04, 0.20
pre_brake = k_rel * rel_v + k_unc * max(0.0, uncertainty - 0.42)
pre_brake = min(pre_brake, 0.06)
self.a_desired = float(self.a_desired - pre_brake)
if mode == 'acc':
output_a_target = output_a_target_mpc
self.output_should_stop = output_should_stop_mpc
else:
output_a_target = min(output_a_target_mpc, output_a_target_e2e)
self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc
# Apply tiny feed-forward nudge when released and safe
if now_t < self.accel_nudge_until and self.a_desired > -0.1:
self.a_desired = float(min(self.a_desired + 0.12, get_max_accel(v_ego)))
for idx in range(2):
accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05)
self.output_a_target = np.clip(output_a_target, accel_clip[0], accel_clip[1])
self.prev_accel_clip = accel_clip
# Small deadzone around zero accel to kill micro-dithers
if -0.05 < self.a_desired < 0.05:
self.a_desired = 0.0
def publish(self, sm, pm):
def publish(self, classic_model, tinygrad_model, sm, pm, frogpilot_toggles):
plan_send = messaging.new_message('longitudinalPlan')
plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState'])
@@ -204,13 +448,34 @@ class LongitudinalPlanner:
longitudinalPlan.accels = self.a_desired_trajectory.tolist()
longitudinalPlan.jerks = self.j_desired_trajectory.tolist()
longitudinalPlan.hasLead = sm['radarState'].leadOne.status
longitudinalPlan.hasLead = self.lead_one.status
longitudinalPlan.longitudinalPlanSource = self.mpc.source
longitudinalPlan.fcw = self.fcw
longitudinalPlan.aTarget = float(self.output_a_target)
longitudinalPlan.shouldStop = bool(self.output_should_stop)
if classic_model:
a_target, should_stop = get_accel_from_plan_classic(self.CP, longitudinalPlan.speeds,
longitudinalPlan.accels, vEgoStopping=frogpilot_toggles.vEgoStopping)
elif tinygrad_model:
action_t = self.CP.longitudinalActuatorDelay + DT_MDL
output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan_tomb_raider(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX,
action_t=action_t, vEgoStopping=frogpilot_toggles.vEgoStopping)
output_a_target_e2e = sm['modelV2'].action.desiredAcceleration
output_should_stop_e2e = sm['modelV2'].action.shouldStop
# v9 uses a different longitudinal interface; keep MPC-only behavior even in blended mode
if self.mode == 'acc' or self.generation == 'v9':
a_target = output_a_target_mpc
should_stop = output_should_stop_mpc
else:
a_target = min(output_a_target_mpc, output_a_target_e2e)
should_stop = output_should_stop_e2e or output_should_stop_mpc
else:
action_t = self.CP.longitudinalActuatorDelay + DT_MDL
a_target, should_stop = get_accel_from_plan(longitudinalPlan.speeds, longitudinalPlan.accels,
action_t=action_t, vEgoStopping=frogpilot_toggles.vEgoStopping)
longitudinalPlan.aTarget = float(a_target)
longitudinalPlan.shouldStop = bool(should_stop) or sm['frogpilotPlan'].forcingStopLength < 1
longitudinalPlan.allowBrake = True
longitudinalPlan.allowThrottle = bool(self.allow_throttle)
longitudinalPlan.allowThrottle = self.allow_throttle
pm.send('longitudinalPlan', plan_send)
+6 -7
View File
@@ -2,11 +2,10 @@ import numpy as np
from numbers import Number
class PIDController:
def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
def __init__(self, k_p, k_i, k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100, pos_p_limit=None, neg_p_limit=None):
self._k_p = k_p
self._k_i = k_i
self._k_d = k_d
self.k_f = k_f # feedforward gain
if isinstance(self._k_p, Number):
self._k_p = [[0], [self._k_p]]
if isinstance(self._k_i, Number):
@@ -16,7 +15,7 @@ class PIDController:
self.set_limits(pos_limit, neg_limit)
self.i_rate = 1.0 / rate
self.i_dt = 1.0 / rate
self.speed = 0.0
self.reset()
@@ -46,12 +45,12 @@ class PIDController:
def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False):
self.speed = speed
self.p = float(error) * self.k_p
self.f = feedforward * self.k_f
self.d = error_rate * self.k_d
self.p = self.k_p * float(error)
self.d = self.k_d * error_rate
self.f = feedforward
if not freeze_integrator:
i = self.i + error * self.k_i * self.i_rate
i = self.i + self.k_i * self.i_dt * error
# Don't allow windup if already clipping
test_control = self.p + i + self.d + self.f
+2 -4
View File
@@ -37,13 +37,11 @@ def plannerd_thread():
# FrogPilot variables
frogpilot_toggles = get_frogpilot_toggles()
classic_longitudinal = frogpilot_toggles.classic_longitudinal
while True:
sm.update()
if sm.updated['modelV2']:
longitudinal_planner.update(sm, classic_longitudinal, frogpilot_toggles)
longitudinal_planner.publish(sm, pm)
longitudinal_planner.update(False, sm, frogpilot_toggles)
longitudinal_planner.publish(False, False, sm, pm, frogpilot_toggles)
publish_ui_plan(sm, pm, longitudinal_planner)
# Update FrogPilot variables
+23 -12
View File
@@ -16,9 +16,10 @@ from openpilot.common.swaglog import cloudlog
from openpilot.common.simple_kalman import KF1D
from openpilot.frogpilot.common.frogpilot_variables import THRESHOLD, get_frogpilot_toggles
from openpilot.selfdrive.controls.controlsd import LaneChangeDirection, LaneChangeState
# Default lead acceleration decay set to 50% at 1s
_LEAD_ACCEL_TAU = 1.5
_LEAD_ACCEL_TAU = 0.6
# radar tracks
SPEED, ACCEL = 0, 1 # Kalman filter states enum
@@ -84,7 +85,7 @@ class Track:
# Learn if constant acceleration
if abs(self.aLeadK) < 0.5:
self.aLeadTau.x = _LEAD_ACCEL_TAU
self.aLeadTau.x = min(max(self.aLeadTau.x, 1e-2) * 1.1, _LEAD_ACCEL_TAU)
else:
self.aLeadTau.update(0.0)
@@ -149,7 +150,15 @@ def laplacian_pdf(x: float, mu: float, b: float):
return math.exp(-abs(x-mu)/b)
def match_vision_to_track(v_ego: float, lead: capnp._DynamicStructReader, tracks: dict[int, Track]):
def match_vision_to_track(v_ego: float, lead: capnp._DynamicStructReader, model_data: capnp._DynamicStructReader, tracks: dict[int, Track], frogpilot_toggles: SimpleNamespace):
if model_data.meta.laneChangeState == LaneChangeState.laneChangeStarting and getattr(frogpilot_toggles, "human_lane_changes", False):
direction = model_data.meta.laneChangeDirection
if direction == LaneChangeDirection.left:
tracks = {k: v for k, v in tracks.items() if v.yRel > 0}
elif direction == LaneChangeDirection.right:
tracks = {k: v for k, v in tracks.items() if v.yRel < 0}
offset_vision_dist = lead.x[0] - RADAR_TO_CAMERA
def prob(c):
@@ -173,14 +182,16 @@ def match_vision_to_track(v_ego: float, lead: capnp._DynamicStructReader, tracks
def get_RadarState_from_vision(lead_msg: capnp._DynamicStructReader, v_ego: float, model_v_ego: float):
lead_v_rel_pred = lead_msg.v[0] - model_v_ego
prev_aLeadK = getattr(get_RadarState_from_vision, "prev_aLeadK", 0.0)
blended_aLeadK = 0.8 * float(lead_msg.a[0]) + 0.2 * prev_aLeadK
get_RadarState_from_vision.prev_aLeadK = blended_aLeadK
return {
"dRel": float(lead_msg.x[0] - RADAR_TO_CAMERA),
"yRel": float(-lead_msg.y[0]),
"vRel": float(lead_v_rel_pred),
"vLead": float(v_ego + lead_v_rel_pred),
"vLeadK": float(v_ego + lead_v_rel_pred),
"aLeadK": float(lead_msg.a[0]),
"vRel": float(lead_msg.v[0] - model_v_ego),
"vLead": float(v_ego + (lead_msg.v[0] - model_v_ego)),
"vLeadK": float(v_ego + (lead_msg.v[0] - model_v_ego)),
"aLeadK": blended_aLeadK,
"aLeadTau": 0.3,
"fcw": False,
"modelProb": float(lead_msg.prob),
@@ -192,11 +203,11 @@ def get_RadarState_from_vision(lead_msg: capnp._DynamicStructReader, v_ego: floa
def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capnp._DynamicStructReader,
model_v_ego: float, model_data: capnp._DynamicStructReader, standstill: bool,
frogpilot_toggles: SimpleNamespace, frogpilotPlan: capnp._DynamicStructReader,
frogpilotPlan: capnp._DynamicStructReader, frogpilot_toggles: SimpleNamespace,
low_speed_override: bool = True) -> dict[str, Any]:
# Determine leads, this is where the essential logic happens
if len(tracks) > 0 and ready and lead_msg.prob > frogpilot_toggles.lead_detection_probability:
track = match_vision_to_track(v_ego, lead_msg, tracks)
track = match_vision_to_track(v_ego, lead_msg, model_data, tracks, frogpilot_toggles)
else:
track = None
@@ -313,8 +324,8 @@ class RadarD:
model_v_ego = self.v_ego
leads_v3 = sm['modelV2'].leadsV3
if len(leads_v3) > 1:
self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, sm['modelV2'], sm['carState'].standstill, self.frogpilot_toggles, sm['frogpilotPlan'], low_speed_override=True)
self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, sm['modelV2'], sm['carState'].standstill, self.frogpilot_toggles, sm['frogpilotPlan'], low_speed_override=False)
self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, sm['modelV2'], sm['carState'].standstill, sm['frogpilotPlan'], self.frogpilot_toggles, low_speed_override=True)
self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, sm['modelV2'], sm['carState'].standstill, sm['frogpilotPlan'], self.frogpilot_toggles, low_speed_override=False)
if self.frogpilot_toggles.adjacent_lead_tracking and self.ready:
self.frogpilot_radar_state.leadLeft = get_adjacent_lead(self.tracks, sm['carState'].standstill, sm['modelV2'], left=True)
+1 -3
View File
@@ -187,8 +187,6 @@ def main():
# FrogPilot variables
frogpilot_toggles = get_frogpilot_toggles()
with custom.FrogPilotCarParams.from_bytes(params_reader.get("FrogPilotCarParams", block=True)) as msg:
FPCP = msg
while True:
sm.update()
@@ -240,7 +238,7 @@ def main():
0.2 <= liveParameters.stiffnessFactor <= 5.0,
min_sr <= liveParameters.steerRatio <= max_sr,
))
if CP.carFingerprint == "RAM_HD" or CP.carName == "subaru" and FPCP.lateralTuning.which() == "torque":
if CP.carFingerprint == "RAM_HD" or CP.carName == "subaru" and CP.lateralTuning.which() == "torque":
liveParameters.valid = True
liveParameters.steerRatioStd = float(P[States.STEER_RATIO].item())
liveParameters.stiffnessFactorStd = float(P[States.STIFFNESS].item())
+28 -20
View File
@@ -34,7 +34,8 @@ MIN_BUCKET_POINTS = np.array([100, 300, 500, 500, 500, 500, 300, 100])
MIN_ENGAGE_BUFFER = 2 # secs
VERSION = 1 # bump this to invalidate old parameter caches
ALLOWED_CARS = ['toyota', 'hyundai']
FULL_AUTO_CARS = ['toyota', 'hyundai']
FRICTION_ONLY_CARS = ['gm']
def slope2rot(slope):
@@ -52,7 +53,7 @@ class TorqueBuckets(PointBuckets):
class TorqueEstimator(ParameterEstimator):
def __init__(self, CP, FPCP, decimated=False):
def __init__(self, CP, decimated=False):
self.hist_len = int(HISTORY / DT_MDL)
self.lag = 0.0
if decimated:
@@ -72,11 +73,14 @@ class TorqueEstimator(ParameterEstimator):
self.offline_friction = 0.0
self.offline_latAccelFactor = 0.0
self.resets = 0.0
self.use_params = CP.carName in ALLOWED_CARS and FPCP.lateralTuning.which() == 'torque'
self.allow_lat_accel_learning = CP.carName in FULL_AUTO_CARS and CP.lateralTuning.which() == 'torque'
self.allow_friction_learning = (self.allow_lat_accel_learning or CP.carName in FRICTION_ONLY_CARS) \
and CP.lateralTuning.which() == 'torque'
self.use_params = self.allow_friction_learning
if FPCP.lateralTuning.which() == 'torque':
self.offline_friction = FPCP.lateralTuning.torque.friction
self.offline_latAccelFactor = FPCP.lateralTuning.torque.latAccelFactor
if CP.lateralTuning.which() == 'torque':
self.offline_friction = CP.lateralTuning.torque.friction
self.offline_latAccelFactor = CP.lateralTuning.torque.latAccelFactor
self.reset()
@@ -102,13 +106,13 @@ class TorqueEstimator(ParameterEstimator):
cache_ltp = log_evt.liveTorqueParameters
with car.CarParams.from_bytes(params_cache) as msg:
cache_CP = msg
if self.get_restore_key(cache_CP, FPCP, cache_ltp.version) == self.get_restore_key(CP, FPCP, VERSION):
if self.get_restore_key(cache_CP, cache_ltp.version) == self.get_restore_key(CP, VERSION):
if cache_ltp.liveValid:
initial_params = {
'latAccelFactor': cache_ltp.latAccelFactorFiltered,
'latAccelOffset': cache_ltp.latAccelOffsetFiltered,
'frictionCoefficient': cache_ltp.frictionCoefficientFiltered
}
if self.allow_lat_accel_learning:
initial_params['latAccelFactor'] = cache_ltp.latAccelFactorFiltered
initial_params['latAccelOffset'] = cache_ltp.latAccelOffsetFiltered
if self.allow_friction_learning:
initial_params['frictionCoefficient'] = cache_ltp.frictionCoefficientFiltered
initial_params['points'] = cache_ltp.points
self.decay = cache_ltp.decay
self.filtered_points.load_points(initial_params['points'])
@@ -121,12 +125,12 @@ class TorqueEstimator(ParameterEstimator):
for param in initial_params:
self.filtered_params[param] = FirstOrderFilter(initial_params[param], self.decay, DT_MDL)
def get_restore_key(self, CP, FPCP, version):
def get_restore_key(self, CP, version):
a, b = None, None
if FPCP.lateralTuning.which() == 'torque':
a = FPCP.lateralTuning.torque.friction
b = FPCP.lateralTuning.torque.latAccelFactor
return (CP.carFingerprint, FPCP.lateralTuning.which(), a, b, version)
if CP.lateralTuning.which() == 'torque':
a = CP.lateralTuning.torque.friction
b = CP.lateralTuning.torque.latAccelFactor
return (CP.carFingerprint, CP.lateralTuning.which(), a, b, version)
def reset(self):
self.resets += 1.0
@@ -155,6 +159,10 @@ class TorqueEstimator(ParameterEstimator):
def update_params(self, params):
self.decay = min(self.decay + DT_MDL, MAX_FILTER_DECAY)
for param, value in params.items():
if param.startswith('latAccel') and not self.allow_lat_accel_learning:
continue
if param == 'frictionCoefficient' and not self.allow_friction_learning:
continue
self.filtered_params[param].update(value)
self.filtered_params[param].update_alpha(self.decay)
@@ -227,14 +235,14 @@ def main(demo=False):
sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveLocationKalman', 'liveDelay', 'frogpilotPlan'], poll='liveLocationKalman')
params = Params()
with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP, custom.FrogPilotCarParams.from_bytes(params.get("FrogPilotCarParams", block=True)) as FPCP:
estimator = TorqueEstimator(CP, FPCP)
with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP:
estimator = TorqueEstimator(CP)
# FrogPilot variables
frogpilot_toggles = get_frogpilot_toggles()
if not frogpilot_toggles.liveValid:
estimator = TorqueEstimator(CP, FPCP, decimated=True)
estimator = TorqueEstimator(CP, decimated=True)
while True:
sm.update()
+6 -14
View File
@@ -33,12 +33,10 @@ class DRIVER_MONITOR_SETTINGS:
self._SG_THRESHOLD = 0.9
self._BLINK_THRESHOLD = 0.865
self._EE_THRESH11 = 0.25
self._EE_THRESH12 = 7.5
self._EE_THRESH11 = 0.6
self._EE_THRESH12 = 15.0
self._EE_MAX_OFFSET1 = 0.06
self._EE_MIN_OFFSET1 = 0.025
self._EE_THRESH21 = 0.01
self._EE_THRESH22 = 0.35
self._POSE_PITCH_THRESHOLD = 0.3133
self._POSE_PITCH_THRESHOLD_SLACK = 0.3237
@@ -46,9 +44,9 @@ class DRIVER_MONITOR_SETTINGS:
self._POSE_YAW_THRESHOLD = 0.4020
self._POSE_YAW_THRESHOLD_SLACK = 0.5042
self._POSE_YAW_THRESHOLD_STRICT = self._POSE_YAW_THRESHOLD
self._PITCH_NATURAL_OFFSET = 0.029 # initial value before offset is learned
self._PITCH_NATURAL_OFFSET = 0.011 # initial value before offset is learned
self._PITCH_NATURAL_THRESHOLD = 0.449
self._YAW_NATURAL_OFFSET = 0.097 # initial value before offset is learned
self._YAW_NATURAL_OFFSET = 0.075 # initial value before offset is learned
self._PITCH_MAX_OFFSET = 0.124
self._PITCH_MIN_OFFSET = -0.0881
self._YAW_MAX_OFFSET = 0.289
@@ -57,7 +55,7 @@ class DRIVER_MONITOR_SETTINGS:
self._POSESTD_THRESHOLD = 0.3
self._HI_STD_FALLBACK_TIME = int(10 / self._DT_DMON) # fall back to wheel touch if model is uncertain for 10s
self._DISTRACTED_FILTER_TS = 0.25 # 0.6Hz
self._ALWAYS_ON_ALERT_MIN_SPEED = 7
self._ALWAYS_ON_ALERT_MIN_SPEED = 11
self._POSE_CALIB_MIN_SPEED = 13 # 30 mph
self._POSE_OFFSET_MIN_COUNT = int(60 / self._DT_DMON) # valid data counts before calibration completes, 1min cumulative
@@ -136,11 +134,8 @@ class DriverMonitoring:
self.pose = DriverPose(self.settings._POSE_OFFSET_MAX_COUNT)
self.blink = DriverBlink()
self.eev1 = 0.
self.eev2 = 1.
self.ee1_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT)
self.ee2_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT)
self.ee1_calibrated = False
self.ee2_calibrated = False
self.always_on = always_on
self.distracted_types = []
@@ -258,7 +253,7 @@ class DriverMonitoring:
driver_data = driver_state.rightDriverData if self.wheel_on_right else driver_state.leftDriverData
if not all(len(x) > 0 for x in (driver_data.faceOrientation, driver_data.facePosition,
driver_data.faceOrientationStd, driver_data.facePositionStd,
driver_data.readyProb, driver_data.notReadyProb)):
driver_data.notReadyProb)):
return
self.face_detected = driver_data.faceProb > self.settings._FACE_THRESHOLD
@@ -275,7 +270,6 @@ class DriverMonitoring:
self.blink.right = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) \
* (driver_data.sunglassesProb < self.settings._SG_THRESHOLD)
self.eev1 = driver_data.notReadyProb[0]
self.eev2 = driver_data.readyProb[0]
self.distracted_types = self._get_distracted_types()
self.driver_distracted = (DistractedType.DISTRACTED_E2E in self.distracted_types or DistractedType.DISTRACTED_POSE in self.distracted_types
@@ -289,12 +283,10 @@ class DriverMonitoring:
self.pose.pitch_offseter.push_and_update(self.pose.pitch)
self.pose.yaw_offseter.push_and_update(self.pose.yaw)
self.ee1_offseter.push_and_update(self.eev1)
self.ee2_offseter.push_and_update(self.eev2)
self.pose.calibrated = self.pose.pitch_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT and \
self.pose.yaw_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
self.ee1_calibrated = self.ee1_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
self.ee2_calibrated = self.ee2_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
self.is_model_uncertain = self.hi_stds > self.settings._HI_STD_FALLBACK_TIME
self._set_timers(self.face_detected and not self.is_model_uncertain)
-2
View File
@@ -24,8 +24,6 @@ def make_msg(face_detected, distracted=False, model_uncertain=False):
ds.leftDriverData.rightBlinkProb = 1. * distracted
ds.leftDriverData.faceOrientationStd = [1.*model_uncertain, 1.*model_uncertain, 1.*model_uncertain]
ds.leftDriverData.facePositionStd = [1.*model_uncertain, 1.*model_uncertain]
# TODO: test both separately when e2e is used
ds.leftDriverData.readyProb = [0., 0., 0., 0.]
ds.leftDriverData.notReadyProb = [0., 0.]
return ds
Binary file not shown.
Binary file not shown.
@@ -32,7 +32,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
// automatic updates toggle
ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"),
tr("FrogPilot will automatically update itself and it's assets when you're offroad and have an active internet connection."), "");
automaticUpdatesToggle->setVisible(params.getBool("IsReleaseBranch"));
addItem(automaticUpdatesToggle);
// download update btn
Binary file not shown.
+195
View File
@@ -431,6 +431,41 @@
<translation>بدء تشغيل الكاميرا</translation>
</message>
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">اختيار</translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">إلغاء</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ExperimentalModeButton</name>
<message>
@@ -1164,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source>
<translation type="gpt-5-generated">&lt;b&gt;تعديلات Twilsonco المعتمدة على العزم لتنعيم التوجيه في المنعطفات.&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -2075,6 +2126,42 @@
<source>&lt;b&gt;Following behavior that mimics human drivers&lt;/b&gt; by closing gaps behind faster vehicles for quicker takeoffs and dynamically adjusting the desired following distance for gentler, more efficient braking.</source>
<translation type="gpt-5-generated">&lt;b&gt;سلوك متابعة يحاكي السائقين البشر&lt;/b&gt; عبر تقليل الفجوات خلف المركبات الأسرع لانطلاق أسرع وضبط مسافة المتابعة المطلوبة ديناميكياً لكبح ألطف وأكثر كفاءة.</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -2476,6 +2563,94 @@
<source>Select a Model 🗺 = Navigation | 📡 = Radar | 👀 = VOACC</source>
<translation type="gpt-5-generated">اختر نموذجًا 🗺 = الملاحة | 📡 = الرادار | 👀 = VOACC</translation>
</message>
<message>
<source>Automatically download new driving models as they become available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete driving models from the device.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Download driving models to the device.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add or remove models from the &lt;b&gt;Model Randomizer&lt;/b&gt;'s blacklist list.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset or view the saved ratings for the driving models.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model to add to the blacklist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model to remove from the blacklist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to remove all of your blacklisted models?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset all of your model drives and scores?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model - 🗺 = Navigation | 📡 = Radar | 👀 = VOACC</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The "Model Randomizer" only works with downloaded models. Do you want to download all the driving models?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3631,6 +3806,22 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">دعم comma Pedal</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
@@ -4322,6 +4513,10 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
<source>&lt;b&gt;Set the road-edge thickness.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Default matches half of the MUTCD lane-line width standard of 10 centimeters.</source>
<translation type="gpt-5-generated">&lt;b&gt;اضبط سماكة حافة الطريق.&lt;/b&gt;&lt;br&gt;&lt;br&gt;القيمة الافتراضية تطابق نصف معيار MUTCD لعرض خط المسار وهو 10 سنتيمترات.</translation>
</message>
<message>
<source>Driving Model: Current</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotWheelPanel</name>
Binary file not shown.
+195
View File
@@ -432,6 +432,41 @@
<translation type="gpt-5-generated">camera start now</translation>
</message>
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Pick</translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ExperimentalModeButton</name>
<message>
@@ -1165,6 +1200,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source>
<translation type="gpt-5-generated">&lt;b&gt;Twilsonco make torque tweak. Steering smooth in curve.&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -2077,6 +2128,42 @@
<source>&lt;b&gt;Following behavior that mimics human drivers&lt;/b&gt; by closing gaps behind faster vehicles for quicker takeoffs and dynamically adjusting the desired following distance for gentler, more efficient braking.</source>
<translation type="gpt-5-generated">&lt;b&gt;Make car follow like human&lt;/b&gt;. Close gap behind fast car for quick takeoff. Change follow distance on the fly for soft, smart brake.</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -2478,6 +2565,94 @@
<source>Select a Model 🗺 = Navigation | 📡 = Radar | 👀 = VOACC</source>
<translation type="gpt-5-generated">Pick Model 🗺 = Navigation | 📡 = Radar | 👀 = VOACC</translation>
</message>
<message>
<source>Automatically download new driving models as they become available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete driving models from the device.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Download driving models to the device.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add or remove models from the &lt;b&gt;Model Randomizer&lt;/b&gt;'s blacklist list.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset or view the saved ratings for the driving models.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model to add to the blacklist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model to remove from the blacklist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to remove all of your blacklisted models?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset all of your model drives and scores?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model - 🗺 = Navigation | 📡 = Radar | 👀 = VOACC</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The "Model Randomizer" only works with downloaded models. Do you want to download all the driving models?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished">Reset</translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3635,6 +3810,22 @@ Developer - Many custom setting for seasoned enthusiast</translation>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal Help</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
@@ -4328,6 +4519,10 @@ Developer - Many custom setting for seasoned enthusiast</translation>
<source>&lt;b&gt;Set the road-edge thickness.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Default matches half of the MUTCD lane-line width standard of 10 centimeters.</source>
<translation type="gpt-5-generated">&lt;b&gt;Set road-edge thickness.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Default same as half MUTCD lane-line width standard, 10 centimeters.</translation>
</message>
<message>
<source>Driving Model: Current</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotWheelPanel</name>
Binary file not shown.
+195
View File
@@ -431,6 +431,41 @@
<translation>Kamera startet</translation>
</message>
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Auswählen</translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Abbrechen</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ExperimentalModeButton</name>
<message>
@@ -1164,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source>
<translation type="gpt-5-generated">&lt;b&gt;Twilsoncos drehmomentbasierte Anpassungen zur Glättung der Lenkung in Kurven.&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -2075,6 +2126,42 @@
<source>&lt;b&gt;Following behavior that mimics human drivers&lt;/b&gt; by closing gaps behind faster vehicles for quicker takeoffs and dynamically adjusting the desired following distance for gentler, more efficient braking.</source>
<translation type="gpt-5-generated">&lt;b&gt;Dem Fahrverhalten menschlicher Fahrer nachempfunden&lt;/b&gt;, indem Lücken hinter schnelleren Fahrzeugen geschlossen werden, um schneller anzufahren, und der gewünschte Folgeabstand dynamisch angepasst wird, um sanfteres und effizienteres Bremsen zu ermöglichen.</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -2476,6 +2563,94 @@
<source>Select a Model 🗺 = Navigation | 📡 = Radar | 👀 = VOACC</source>
<translation type="gpt-5-generated">Modell auswählen 🗺 = Navigation | 📡 = Radar | 👀 = VOACC</translation>
</message>
<message>
<source>Automatically download new driving models as they become available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete driving models from the device.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Download driving models to the device.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add or remove models from the &lt;b&gt;Model Randomizer&lt;/b&gt;'s blacklist list.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset or view the saved ratings for the driving models.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model to add to the blacklist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model to remove from the blacklist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to remove all of your blacklisted models?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset all of your model drives and scores?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model - 🗺 = Navigation | 📡 = Radar | 👀 = VOACC</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The "Model Randomizer" only works with downloaded models. Do you want to download all the driving models?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished">Zurücksetzen</translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3631,6 +3806,22 @@ Entwickler Hochgradig anpassbare Einstellungen für versierte Enthusiasten</
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">Unterstützung für comma Pedal</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
@@ -4322,6 +4513,10 @@ Entwickler Hochgradig anpassbare Einstellungen für versierte Enthusiasten</
<source>&lt;b&gt;Set the road-edge thickness.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Default matches half of the MUTCD lane-line width standard of 10 centimeters.</source>
<translation type="gpt-5-generated">&lt;b&gt;Stellen Sie die Randstreifendicke ein.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Standard entspricht der Hälfte des MUTCD-Standards für Fahrbahnmarkierungsbreite von 10 Zentimetern.</translation>
</message>
<message>
<source>Driving Model: Current</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotWheelPanel</name>
Binary file not shown.
+195
View File
@@ -431,6 +431,41 @@
<translation type="gpt-5-generated">Quack! Camera startingwaddle on!</translation>
</message>
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Quack-select!</translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ExperimentalModeButton</name>
<message>
@@ -1166,6 +1201,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source>
<translation type="gpt-5-generated">&lt;b&gt;Quack! Twilsoncos torque tweaks to smooth out steering in curves, waddle-waddle.&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -2077,6 +2128,42 @@
<source>&lt;b&gt;Following behavior that mimics human drivers&lt;/b&gt; by closing gaps behind faster vehicles for quicker takeoffs and dynamically adjusting the desired following distance for gentler, more efficient braking.</source>
<translation type="gpt-5-generated">&lt;b&gt;Quack! Mimic human drivers&lt;/b&gt; by waddling up to close gaps behind faster cars for zippy takeoffs, and duckily tweak the following distance for smoother, more efficient braking.</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -2478,6 +2565,94 @@
<source>Select a Model 🗺 = Navigation | 📡 = Radar | 👀 = VOACC</source>
<translation type="gpt-5-generated">Quack a Model 🗺 = Navigation, 📡 = Radar, 👀 = VOACC, waddle!</translation>
</message>
<message>
<source>Automatically download new driving models as they become available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete driving models from the device.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Download driving models to the device.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add or remove models from the &lt;b&gt;Model Randomizer&lt;/b&gt;'s blacklist list.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset or view the saved ratings for the driving models.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model to add to the blacklist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model to remove from the blacklist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to remove all of your blacklisted models?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset all of your model drives and scores?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a model - 🗺 = Navigation | 📡 = Radar | 👀 = VOACC</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The "Model Randomizer" only works with downloaded models. Do you want to download all the driving models?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3633,6 +3808,22 @@ Developer - Ultra-custom settings for seasoned duckthusiasts</translation>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">Quack! comma Pedal Support, waddle on!</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
@@ -4324,6 +4515,10 @@ Developer - Ultra-custom settings for seasoned duckthusiasts</translation>
<source>&lt;b&gt;Set the road-edge thickness.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Default matches half of the MUTCD lane-line width standard of 10 centimeters.</source>
<translation type="gpt-5-generated">&lt;b&gt;Quack! Set the road-edge thickness, waddlers.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Default quacks to half the MUTCD lane-line width standard of 10 centimeters.</translation>
</message>
<message>
<source>Driving Model: Current</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotWheelPanel</name>
Binary file not shown.

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