Compare commits

...

56 Commits

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

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

This reverts commit af687e501cc4bdcda7840453d06595d7ea674148.

Reapply "Latty Boi"

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

This reverts commit 20f7d6631bb152860781b46533bb0e96223be132.

Reapply "Upstream Lateral"

This reverts commit 2a7a563e219a1b688529d341919fc377fed5038e.

Update latcontrol_torque.py

more lateral

Update ui
2025-11-07 00:14:06 -06:00
firestar5683 2d51ca9538 Revert "Pedal Panda?"
This reverts commit 938fe7a088.
2025-10-30 22:36:32 -05:00
firestar5683 78223aa635 Pedal? 2025-10-28 16:48:48 -05:00
83 changed files with 4929 additions and 1134 deletions
+3 -3
View File
@@ -530,14 +530,14 @@ struct CarParams {
struct LateralTorqueTuning { struct LateralTorqueTuning {
useSteeringAngle @0 :Bool; useSteeringAngle @0 :Bool;
kp @1 :Float32;
ki @2 :Float32;
kd @8 : Float32;
friction @3 :Float32; friction @3 :Float32;
steeringAngleDeadzoneDeg @5 :Float32; steeringAngleDeadzoneDeg @5 :Float32;
latAccelFactor @6 :Float32; latAccelFactor @6 :Float32;
latAccelOffset @7 :Float32; latAccelOffset @7 :Float32;
kpDEPRECATED @1 :Float32;
kiDEPRECATED @2 :Float32;
kfDEPRECATED @4 :Float32; kfDEPRECATED @4 :Float32;
kdDEPRECATED @8 : Float32;
} }
struct LongitudinalPIDTuning { struct LongitudinalPIDTuning {
+1 -1
View File
@@ -105,7 +105,7 @@ struct FrogPilotCarParams @0xf35cc4560bbf6ec2 {
isHDA2 @3 :Bool; isHDA2 @3 :Bool;
openpilotLongitudinalControlDisabled @4 :Bool; openpilotLongitudinalControlDisabled @4 :Bool;
safetyConfigs @5 :List(SafetyConfig); safetyConfigs @5 :List(SafetyConfig);
canUseSASCM @6 :Bool;
struct SafetyConfig { struct SafetyConfig {
safetyParam @0 :UInt16; safetyParam @0 :UInt16;
+35 -38
View File
@@ -5262,7 +5262,7 @@ const ::capnp::_::RawSchema s_9622723fcbd14c2e = {
0, 5, i_9622723fcbd14c2e, nullptr, nullptr, { &s_9622723fcbd14c2e, nullptr, nullptr, 0, 0, nullptr }, false 0, 5, i_9622723fcbd14c2e, nullptr, nullptr, { &s_9622723fcbd14c2e, nullptr, nullptr, 0, 0, nullptr }, false
}; };
#endif // !CAPNP_LITE #endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<166> b_80366e0e804ecc1d = { static const ::capnp::_::AlignedData<163> b_80366e0e804ecc1d = {
{ 0, 0, 0, 0, 5, 0, 6, 0, { 0, 0, 0, 0, 5, 0, 6, 0,
29, 204, 78, 128, 14, 110, 54, 128, 29, 204, 78, 128, 14, 110, 54, 128,
20, 0, 0, 0, 1, 0, 5, 0, 20, 0, 0, 0, 1, 0, 5, 0,
@@ -5289,62 +5289,62 @@ static const ::capnp::_::AlignedData<166> b_80366e0e804ecc1d = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
240, 0, 0, 0, 3, 0, 1, 0, 240, 0, 0, 0, 3, 0, 1, 0,
252, 0, 0, 0, 2, 0, 1, 0, 252, 0, 0, 0, 2, 0, 1, 0,
5, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
249, 0, 0, 0, 106, 0, 0, 0, 249, 0, 0, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 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,
253, 0, 0, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
248, 0, 0, 0, 3, 0, 1, 0, 248, 0, 0, 0, 3, 0, 1, 0,
4, 1, 0, 0, 2, 0, 1, 0, 4, 1, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 106, 0, 0, 0, 1, 1, 0, 0, 74, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 3, 0, 1, 0, 0, 1, 0, 0, 3, 0, 1, 0,
12, 1, 0, 0, 2, 0, 1, 0, 12, 1, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0, 0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
9, 1, 0, 0, 74, 0, 0, 0, 9, 1, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8, 1, 0, 0, 3, 0, 1, 0, 8, 1, 0, 0, 3, 0, 1, 0,
20, 1, 0, 0, 2, 0, 1, 0, 20, 1, 0, 0, 2, 0, 1, 0,
7, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
17, 1, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
16, 1, 0, 0, 3, 0, 1, 0,
28, 1, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0, 0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
25, 1, 0, 0, 202, 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, 0, 0, 0, 0, 0, 0, 0, 0,
32, 1, 0, 0, 3, 0, 1, 0, 32, 1, 0, 0, 3, 0, 1, 0,
44, 1, 0, 0, 2, 0, 1, 0, 44, 1, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0, 0, 0, 1, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41, 1, 0, 0, 122, 0, 0, 0, 41, 1, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
40, 1, 0, 0, 3, 0, 1, 0, 40, 1, 0, 0, 3, 0, 1, 0,
52, 1, 0, 0, 2, 0, 1, 0, 52, 1, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 7, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
49, 1, 0, 0, 122, 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,
8, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 8, 0, 0, 0, 0, 0, 1, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57, 1, 0, 0, 106, 0, 0, 0, 49, 1, 0, 0, 26, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56, 1, 0, 0, 3, 0, 1, 0, 44, 1, 0, 0, 3, 0, 1, 0,
68, 1, 0, 0, 2, 0, 1, 0, 56, 1, 0, 0, 2, 0, 1, 0,
117, 115, 101, 83, 116, 101, 101, 114, 117, 115, 101, 83, 116, 101, 101, 114,
105, 110, 103, 65, 110, 103, 108, 101, 105, 110, 103, 65, 110, 103, 108, 101,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -5355,8 +5355,7 @@ static const ::capnp::_::AlignedData<166> b_80366e0e804ecc1d = {
1, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107, 112, 68, 69, 80, 82, 69, 67, 107, 112, 0, 0, 0, 0, 0, 0,
65, 84, 69, 68, 0, 0, 0, 0,
10, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -5364,8 +5363,7 @@ static const ::capnp::_::AlignedData<166> b_80366e0e804ecc1d = {
10, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107, 105, 68, 69, 80, 82, 69, 67, 107, 105, 0, 0, 0, 0, 0, 0,
65, 84, 69, 68, 0, 0, 0, 0,
10, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -5420,8 +5418,7 @@ static const ::capnp::_::AlignedData<166> b_80366e0e804ecc1d = {
10, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107, 100, 68, 69, 80, 82, 69, 67, 107, 100, 0, 0, 0, 0, 0, 0,
65, 84, 69, 68, 0, 0, 0, 0,
10, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -5435,7 +5432,7 @@ static const ::capnp::_::AlignedData<166> b_80366e0e804ecc1d = {
static const uint16_t m_80366e0e804ecc1d[] = {3, 8, 4, 2, 1, 6, 7, 5, 0}; 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}; static const uint16_t i_80366e0e804ecc1d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
const ::capnp::_::RawSchema s_80366e0e804ecc1d = { const ::capnp::_::RawSchema s_80366e0e804ecc1d = {
0x80366e0e804ecc1d, b_80366e0e804ecc1d.words, 166, nullptr, m_80366e0e804ecc1d, 0x80366e0e804ecc1d, b_80366e0e804ecc1d.words, 163, nullptr, m_80366e0e804ecc1d,
0, 9, i_80366e0e804ecc1d, nullptr, nullptr, { &s_80366e0e804ecc1d, nullptr, nullptr, 0, 0, nullptr }, false 0, 9, i_80366e0e804ecc1d, nullptr, nullptr, { &s_80366e0e804ecc1d, nullptr, nullptr, 0, 0, nullptr }, false
}; };
#endif // !CAPNP_LITE #endif // !CAPNP_LITE
+18 -18
View File
@@ -3134,9 +3134,9 @@ public:
inline bool getUseSteeringAngle() const; inline bool getUseSteeringAngle() const;
inline float getKpDEPRECATED() const; inline float getKp() const;
inline float getKiDEPRECATED() const; inline float getKi() const;
inline float getFriction() const; inline float getFriction() const;
@@ -3148,7 +3148,7 @@ public:
inline float getLatAccelOffset() const; inline float getLatAccelOffset() const;
inline float getKdDEPRECATED() const; inline float getKd() const;
private: private:
::capnp::_::StructReader _reader; ::capnp::_::StructReader _reader;
@@ -3181,11 +3181,11 @@ public:
inline bool getUseSteeringAngle(); inline bool getUseSteeringAngle();
inline void setUseSteeringAngle(bool value); inline void setUseSteeringAngle(bool value);
inline float getKpDEPRECATED(); inline float getKp();
inline void setKpDEPRECATED(float value); inline void setKp(float value);
inline float getKiDEPRECATED(); inline float getKi();
inline void setKiDEPRECATED(float value); inline void setKi(float value);
inline float getFriction(); inline float getFriction();
inline void setFriction(float value); inline void setFriction(float value);
@@ -3202,8 +3202,8 @@ public:
inline float getLatAccelOffset(); inline float getLatAccelOffset();
inline void setLatAccelOffset(float value); inline void setLatAccelOffset(float value);
inline float getKdDEPRECATED(); inline float getKd();
inline void setKdDEPRECATED(float value); inline void setKd(float value);
private: private:
::capnp::_::StructBuilder _builder; ::capnp::_::StructBuilder _builder;
@@ -7754,30 +7754,30 @@ inline void CarParams::LateralTorqueTuning::Builder::setUseSteeringAngle(bool va
::capnp::bounded<0>() * ::capnp::ELEMENTS, value); ::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
} }
inline float CarParams::LateralTorqueTuning::Reader::getKpDEPRECATED() const { inline float CarParams::LateralTorqueTuning::Reader::getKp() const {
return _reader.getDataField<float>( return _reader.getDataField<float>(
::capnp::bounded<1>() * ::capnp::ELEMENTS); ::capnp::bounded<1>() * ::capnp::ELEMENTS);
} }
inline float CarParams::LateralTorqueTuning::Builder::getKpDEPRECATED() { inline float CarParams::LateralTorqueTuning::Builder::getKp() {
return _builder.getDataField<float>( return _builder.getDataField<float>(
::capnp::bounded<1>() * ::capnp::ELEMENTS); ::capnp::bounded<1>() * ::capnp::ELEMENTS);
} }
inline void CarParams::LateralTorqueTuning::Builder::setKpDEPRECATED(float value) { inline void CarParams::LateralTorqueTuning::Builder::setKp(float value) {
_builder.setDataField<float>( _builder.setDataField<float>(
::capnp::bounded<1>() * ::capnp::ELEMENTS, value); ::capnp::bounded<1>() * ::capnp::ELEMENTS, value);
} }
inline float CarParams::LateralTorqueTuning::Reader::getKiDEPRECATED() const { inline float CarParams::LateralTorqueTuning::Reader::getKi() const {
return _reader.getDataField<float>( return _reader.getDataField<float>(
::capnp::bounded<2>() * ::capnp::ELEMENTS); ::capnp::bounded<2>() * ::capnp::ELEMENTS);
} }
inline float CarParams::LateralTorqueTuning::Builder::getKiDEPRECATED() { inline float CarParams::LateralTorqueTuning::Builder::getKi() {
return _builder.getDataField<float>( return _builder.getDataField<float>(
::capnp::bounded<2>() * ::capnp::ELEMENTS); ::capnp::bounded<2>() * ::capnp::ELEMENTS);
} }
inline void CarParams::LateralTorqueTuning::Builder::setKiDEPRECATED(float value) { inline void CarParams::LateralTorqueTuning::Builder::setKi(float value) {
_builder.setDataField<float>( _builder.setDataField<float>(
::capnp::bounded<2>() * ::capnp::ELEMENTS, value); ::capnp::bounded<2>() * ::capnp::ELEMENTS, value);
} }
@@ -7852,16 +7852,16 @@ inline void CarParams::LateralTorqueTuning::Builder::setLatAccelOffset(float val
::capnp::bounded<7>() * ::capnp::ELEMENTS, value); ::capnp::bounded<7>() * ::capnp::ELEMENTS, value);
} }
inline float CarParams::LateralTorqueTuning::Reader::getKdDEPRECATED() const { inline float CarParams::LateralTorqueTuning::Reader::getKd() const {
return _reader.getDataField<float>( return _reader.getDataField<float>(
::capnp::bounded<8>() * ::capnp::ELEMENTS); ::capnp::bounded<8>() * ::capnp::ELEMENTS);
} }
inline float CarParams::LateralTorqueTuning::Builder::getKdDEPRECATED() { inline float CarParams::LateralTorqueTuning::Builder::getKd() {
return _builder.getDataField<float>( return _builder.getDataField<float>(
::capnp::bounded<8>() * ::capnp::ELEMENTS); ::capnp::bounded<8>() * ::capnp::ELEMENTS);
} }
inline void CarParams::LateralTorqueTuning::Builder::setKdDEPRECATED(float value) { inline void CarParams::LateralTorqueTuning::Builder::setKd(float value) {
_builder.setDataField<float>( _builder.setDataField<float>(
::capnp::bounded<8>() * ::capnp::ELEMENTS, value); ::capnp::bounded<8>() * ::capnp::ELEMENTS, value);
} }
+51 -35
View File
@@ -642,7 +642,7 @@ const ::capnp::_::RawSchema s_aedffd8f31e7b55d = {
}; };
#endif // !CAPNP_LITE #endif // !CAPNP_LITE
CAPNP_DEFINE_ENUM(EventName_aedffd8f31e7b55d, aedffd8f31e7b55d); CAPNP_DEFINE_ENUM(EventName_aedffd8f31e7b55d, aedffd8f31e7b55d);
static const ::capnp::_::AlignedData<123> b_f35cc4560bbf6ec2 = { static const ::capnp::_::AlignedData<139> b_f35cc4560bbf6ec2 = {
{ 0, 0, 0, 0, 5, 0, 6, 0, { 0, 0, 0, 0, 5, 0, 6, 0,
194, 110, 191, 11, 86, 196, 92, 243, 194, 110, 191, 11, 86, 196, 92, 243,
13, 0, 0, 0, 1, 0, 1, 0, 13, 0, 0, 0, 1, 0, 1, 0,
@@ -652,7 +652,7 @@ static const ::capnp::_::AlignedData<123> b_f35cc4560bbf6ec2 = {
21, 0, 0, 0, 2, 1, 0, 0, 21, 0, 0, 0, 2, 1, 0, 0,
33, 0, 0, 0, 23, 0, 0, 0, 33, 0, 0, 0, 23, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45, 0, 0, 0, 87, 1, 0, 0, 45, 0, 0, 0, 143, 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, 0, 0, 0,
99, 117, 115, 116, 111, 109, 46, 99, 99, 117, 115, 116, 111, 109, 46, 99,
@@ -664,49 +664,56 @@ static const ::capnp::_::AlignedData<123> b_f35cc4560bbf6ec2 = {
1, 0, 0, 0, 106, 0, 0, 0, 1, 0, 0, 0, 106, 0, 0, 0,
83, 97, 102, 101, 116, 121, 67, 111, 83, 97, 102, 101, 116, 121, 67, 111,
110, 102, 105, 103, 0, 0, 0, 0, 110, 102, 105, 103, 0, 0, 0, 0,
24, 0, 0, 0, 3, 0, 4, 0, 28, 0, 0, 0, 3, 0, 4, 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, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
153, 0, 0, 0, 98, 0, 0, 0, 181, 0, 0, 0, 98, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
152, 0, 0, 0, 3, 0, 1, 0, 180, 0, 0, 0, 3, 0, 1, 0,
164, 0, 0, 0, 2, 0, 1, 0, 192, 0, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161, 0, 0, 0, 90, 0, 0, 0, 189, 0, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
160, 0, 0, 0, 3, 0, 1, 0,
172, 0, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
169, 0, 0, 0, 66, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
164, 0, 0, 0, 3, 0, 1, 0,
176, 0, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
173, 0, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
168, 0, 0, 0, 3, 0, 1, 0,
180, 0, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
177, 0, 0, 0, 42, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
188, 0, 0, 0, 3, 0, 1, 0, 188, 0, 0, 0, 3, 0, 1, 0,
200, 0, 0, 0, 2, 0, 1, 0, 200, 0, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
197, 0, 0, 0, 66, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
192, 0, 0, 0, 3, 0, 1, 0,
204, 0, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
201, 0, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
196, 0, 0, 0, 3, 0, 1, 0,
208, 0, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
205, 0, 0, 0, 42, 1, 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,
5, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 5, 0, 0, 0, 0, 0, 1, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
197, 0, 0, 0, 114, 0, 0, 0, 225, 0, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
196, 0, 0, 0, 3, 0, 1, 0, 224, 0, 0, 0, 3, 0, 1, 0,
224, 0, 0, 0, 2, 0, 1, 0, 252, 0, 0, 0, 2, 0, 1, 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, 99, 97, 110, 85, 115, 101, 80, 101,
100, 97, 108, 0, 0, 0, 0, 0, 100, 97, 108, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
@@ -764,6 +771,15 @@ static const ::capnp::_::AlignedData<123> b_f35cc4560bbf6ec2 = {
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, 0,
14, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 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,
0, 0, 0, 0, 0, 0, 0, 0, } 0, 0, 0, 0, 0, 0, 0, 0, }
}; };
@@ -772,11 +788,11 @@ static const ::capnp::_::AlignedData<123> b_f35cc4560bbf6ec2 = {
static const ::capnp::_::RawSchema* const d_f35cc4560bbf6ec2[] = { static const ::capnp::_::RawSchema* const d_f35cc4560bbf6ec2[] = {
&s_8d65dd40bad40951, &s_8d65dd40bad40951,
}; };
static const uint16_t m_f35cc4560bbf6ec2[] = {0, 1, 2, 3, 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}; static const uint16_t i_f35cc4560bbf6ec2[] = {0, 1, 2, 3, 4, 5, 6};
const ::capnp::_::RawSchema s_f35cc4560bbf6ec2 = { const ::capnp::_::RawSchema s_f35cc4560bbf6ec2 = {
0xf35cc4560bbf6ec2, b_f35cc4560bbf6ec2.words, 123, d_f35cc4560bbf6ec2, m_f35cc4560bbf6ec2, 0xf35cc4560bbf6ec2, b_f35cc4560bbf6ec2.words, 139, d_f35cc4560bbf6ec2, m_f35cc4560bbf6ec2,
1, 6, i_f35cc4560bbf6ec2, nullptr, nullptr, { &s_f35cc4560bbf6ec2, nullptr, nullptr, 0, 0, nullptr }, false 1, 7, i_f35cc4560bbf6ec2, nullptr, nullptr, { &s_f35cc4560bbf6ec2, nullptr, nullptr, 0, 0, nullptr }, false
}; };
#endif // !CAPNP_LITE #endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<36> b_8d65dd40bad40951 = { static const ::capnp::_::AlignedData<36> b_8d65dd40bad40951 = {
+19
View File
@@ -669,6 +669,8 @@ public:
inline bool hasSafetyConfigs() const; inline bool hasSafetyConfigs() const;
inline ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>::Reader getSafetyConfigs() const; inline ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>::Reader getSafetyConfigs() const;
inline bool getCanUseSASCM() const;
private: private:
::capnp::_::StructReader _reader; ::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind> template <typename, ::capnp::Kind>
@@ -719,6 +721,9 @@ public:
inline void adoptSafetyConfigs(::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>>&& value); 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 ::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>> disownSafetyConfigs();
inline bool getCanUseSASCM();
inline void setCanUseSASCM(bool value);
private: private:
::capnp::_::StructBuilder _builder; ::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind> template <typename, ::capnp::Kind>
@@ -2220,6 +2225,20 @@ inline ::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfi
::capnp::bounded<0>() * ::capnp::POINTERS)); ::capnp::bounded<0>() * ::capnp::POINTERS));
} }
inline bool FrogPilotCarParams::Reader::getCanUseSASCM() const {
return _reader.getDataField<bool>(
::capnp::bounded<4>() * ::capnp::ELEMENTS);
}
inline bool FrogPilotCarParams::Builder::getCanUseSASCM() {
return _builder.getDataField<bool>(
::capnp::bounded<4>() * ::capnp::ELEMENTS);
}
inline void FrogPilotCarParams::Builder::setCanUseSASCM(bool value) {
_builder.setDataField<bool>(
::capnp::bounded<4>() * ::capnp::ELEMENTS, value);
}
inline ::uint16_t FrogPilotCarParams::SafetyConfig::Reader::getSafetyParam() const { inline ::uint16_t FrogPilotCarParams::SafetyConfig::Reader::getSafetyParam() const {
return _reader.getDataField< ::uint16_t>( return _reader.getDataField< ::uint16_t>(
::capnp::bounded<0>() * ::capnp::ELEMENTS); ::capnp::bounded<0>() * ::capnp::ELEMENTS);
Binary file not shown.
+13
View File
@@ -224,6 +224,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"AdvancedLateralTune", PERSISTENT}, {"AdvancedLateralTune", PERSISTENT},
{"AdvancedLongitudinalTune", PERSISTENT}, {"AdvancedLongitudinalTune", PERSISTENT},
{"AggressiveFollow", PERSISTENT}, {"AggressiveFollow", PERSISTENT},
{"AggressiveFollowHigh", PERSISTENT},
{"AggressiveJerkAcceleration", PERSISTENT}, {"AggressiveJerkAcceleration", PERSISTENT},
{"AggressiveJerkDanger", PERSISTENT}, {"AggressiveJerkDanger", PERSISTENT},
{"AggressiveJerkDeceleration", PERSISTENT}, {"AggressiveJerkDeceleration", PERSISTENT},
@@ -321,6 +322,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DynamicPedalsOnUI", PERSISTENT}, {"DynamicPedalsOnUI", PERSISTENT},
{"EngageVolume", PERSISTENT}, {"EngageVolume", PERSISTENT},
{"ExperimentalGMTune", PERSISTENT}, {"ExperimentalGMTune", PERSISTENT},
{"EVTuning", PERSISTENT},
{"Fahrenheit", PERSISTENT}, {"Fahrenheit", PERSISTENT},
{"FavoriteDestinations", PERSISTENT | DONT_LOG}, {"FavoriteDestinations", PERSISTENT | DONT_LOG},
{"FlashPanda", CLEAR_ON_MANAGER_START}, {"FlashPanda", CLEAR_ON_MANAGER_START},
@@ -346,6 +348,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"FrogsGoMoosTweak", PERSISTENT}, {"FrogsGoMoosTweak", PERSISTENT},
{"FullMap", PERSISTENT}, {"FullMap", PERSISTENT},
{"GasRegenCmd", PERSISTENT}, {"GasRegenCmd", PERSISTENT},
{"GMPedalLongitudinal", PERSISTENT},
{"GoatScream", PERSISTENT}, {"GoatScream", PERSISTENT},
{"GreenLightAlert", PERSISTENT}, {"GreenLightAlert", PERSISTENT},
{"HideAlerts", PERSISTENT}, {"HideAlerts", PERSISTENT},
@@ -406,6 +409,10 @@ std::unordered_map<std::string, uint32_t> keys = {
{"ModelToDownload", CLEAR_ON_MANAGER_START}, {"ModelToDownload", CLEAR_ON_MANAGER_START},
{"ModelUI", PERSISTENT}, {"ModelUI", PERSISTENT},
{"ModelVersions", PERSISTENT}, {"ModelVersions", PERSISTENT},
{"ModelReleasedDates", PERSISTENT},
{"CommunityFavorites", PERSISTENT},
{"UserFavorites", PERSISTENT},
{"SortModelsByDate", PERSISTENT},
{"NavigationUI", PERSISTENT}, {"NavigationUI", PERSISTENT},
{"NextMapSpeedLimit", CLEAR_ON_MANAGER_START}, {"NextMapSpeedLimit", CLEAR_ON_MANAGER_START},
{"NewLongAPI", PERSISTENT}, {"NewLongAPI", PERSISTENT},
@@ -451,6 +458,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"RandomThemes", PERSISTENT}, {"RandomThemes", PERSISTENT},
{"RefuseVolume", PERSISTENT}, {"RefuseVolume", PERSISTENT},
{"RelaxedFollow", PERSISTENT}, {"RelaxedFollow", PERSISTENT},
{"RelaxedFollowHigh", PERSISTENT},
{"RelaxedJerkAcceleration", PERSISTENT}, {"RelaxedJerkAcceleration", PERSISTENT},
{"RelaxedJerkDanger", PERSISTENT}, {"RelaxedJerkDanger", PERSISTENT},
{"RelaxedJerkDeceleration", PERSISTENT}, {"RelaxedJerkDeceleration", PERSISTENT},
@@ -510,6 +518,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"SpeedLimitsFiltered", PERSISTENT | DONT_LOG}, {"SpeedLimitsFiltered", PERSISTENT | DONT_LOG},
{"SpeedLimitSources", PERSISTENT}, {"SpeedLimitSources", PERSISTENT},
{"StandardFollow", PERSISTENT}, {"StandardFollow", PERSISTENT},
{"StandardFollowHigh", PERSISTENT},
{"StandardJerkAcceleration", PERSISTENT}, {"StandardJerkAcceleration", PERSISTENT},
{"StandardJerkDanger", PERSISTENT}, {"StandardJerkDanger", PERSISTENT},
{"StandardJerkDeceleration", PERSISTENT}, {"StandardJerkDeceleration", PERSISTENT},
@@ -526,6 +535,8 @@ std::unordered_map<std::string, uint32_t> keys = {
{"SteerDelayStock", PERSISTENT}, {"SteerDelayStock", PERSISTENT},
{"SteerFriction", PERSISTENT}, {"SteerFriction", PERSISTENT},
{"SteerFrictionStock", PERSISTENT}, {"SteerFrictionStock", PERSISTENT},
{"SteerOffset", PERSISTENT},
{"SteerOffsetStock", PERSISTENT},
{"SteerLatAccel", PERSISTENT}, {"SteerLatAccel", PERSISTENT},
{"SteerLatAccelStock", PERSISTENT}, {"SteerLatAccelStock", PERSISTENT},
{"SteerKP", PERSISTENT}, {"SteerKP", PERSISTENT},
@@ -540,6 +551,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"StoppedTimer", PERSISTENT}, {"StoppedTimer", PERSISTENT},
{"TacoTune", PERSISTENT}, {"TacoTune", PERSISTENT},
{"TacoTuneHacks", PERSISTENT}, {"TacoTuneHacks", PERSISTENT},
{"TrailerLoad", PERSISTENT},
{"TestAlert", CLEAR_ON_MANAGER_START}, {"TestAlert", CLEAR_ON_MANAGER_START},
{"TetheringEnabled", PERSISTENT}, {"TetheringEnabled", PERSISTENT},
{"ThemeDownloadProgress", CLEAR_ON_MANAGER_START}, {"ThemeDownloadProgress", CLEAR_ON_MANAGER_START},
@@ -579,6 +591,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"WheelIcon", PERSISTENT}, {"WheelIcon", PERSISTENT},
{"WheelSpeed", PERSISTENT}, {"WheelSpeed", PERSISTENT},
{"StopDistance", PERSISTENT}, {"StopDistance", PERSISTENT},
{"RecoveryPower", PERSISTENT},
{"WheelToDownload", CLEAR_ON_MANAGER_START}, {"WheelToDownload", CLEAR_ON_MANAGER_START},
}; };
Binary file not shown.
+27 -4
View File
@@ -107,7 +107,7 @@ class ModelManager:
self.downloading_model = False self.downloading_model = False
return return
if model_version in ("v8", "v9", "v10", "v11"): if model_version in ("v8", "v9", "v10", "v11", "v12"):
# Download all PKL and metadata files for multi-file tinygrad models (v8 and v9) # Download all PKL and metadata files for multi-file tinygrad models (v8 and v9)
filenames = [ filenames = [
f"{model_to_download}_driving_policy_tinygrad.pkl", f"{model_to_download}_driving_policy_tinygrad.pkl",
@@ -115,6 +115,11 @@ class ModelManager:
f"{model_to_download}_driving_policy_metadata.pkl", f"{model_to_download}_driving_policy_metadata.pkl",
f"{model_to_download}_driving_vision_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: for filename in filenames:
model_path = MODELS_PATH / filename model_path = MODELS_PATH / filename
model_url = f"{repo_url}/Models/{filename}" model_url = f"{repo_url}/Models/{filename}"
@@ -208,13 +213,18 @@ class ModelManager:
except Exception: except Exception:
model_version = None model_version = None
if model_version in ("v8", "v9", "v10", "v11"): if model_version in ("v8", "v9", "v10", "v11", "v12"):
v8_v9_files = [ v8_v9_files = [
f"{model}_driving_policy_tinygrad.pkl", f"{model}_driving_policy_tinygrad.pkl",
f"{model}_driving_vision_tinygrad.pkl", f"{model}_driving_vision_tinygrad.pkl",
f"{model}_driving_policy_metadata.pkl", f"{model}_driving_policy_metadata.pkl",
f"{model}_driving_vision_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): if all((MODELS_PATH / f).is_file() for f in v8_v9_files):
downloaded_models.add(model) downloaded_models.add(model)
elif model_version == "v7": elif model_version == "v7":
@@ -259,13 +269,18 @@ class ModelManager:
except Exception: except Exception:
model_version = None model_version = None
if model_version in ("v8", "v9", "v10", "v11"): if model_version in ("v8", "v9", "v10", "v11", "v12"):
v8_v9_files = [ v8_v9_files = [
f"{model}_driving_policy_tinygrad.pkl", f"{model}_driving_policy_tinygrad.pkl",
f"{model}_driving_vision_tinygrad.pkl", f"{model}_driving_vision_tinygrad.pkl",
f"{model}_driving_policy_metadata.pkl", f"{model}_driving_policy_metadata.pkl",
f"{model}_driving_vision_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: for filename in v8_v9_files:
path = MODELS_PATH / filename path = MODELS_PATH / filename
expected_size = model_sizes.get(filename.rsplit(".", 1)[0]) expected_size = model_sizes.get(filename.rsplit(".", 1)[0])
@@ -296,7 +311,10 @@ class ModelManager:
params.put("AvailableModels", ",".join(self.available_models)) params.put("AvailableModels", ",".join(self.available_models))
params.put("AvailableModelNames", ",".join([model["name"] for model in model_info])) params.put("AvailableModelNames", ",".join([model["name"] for model in model_info]))
params.put("AvailableModelSeries", ",".join(self.model_series)) 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("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)) params.put("AvailableModelSeries", ",".join(self.model_series))
print("Models list updated successfully") print("Models list updated successfully")
@@ -375,13 +393,18 @@ class ModelManager:
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory) handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
return return
if model_version in ("v8", "v9", "v10", "v11"): if model_version in ("v8", "v9", "v10", "v11", "v12"):
required_files = [ required_files = [
f"{model}_driving_policy_tinygrad.pkl", f"{model}_driving_policy_tinygrad.pkl",
f"{model}_driving_vision_tinygrad.pkl", f"{model}_driving_vision_tinygrad.pkl",
f"{model}_driving_policy_metadata.pkl", f"{model}_driving_policy_metadata.pkl",
f"{model}_driving_vision_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()] missing = [f for f in required_files if not (MODELS_PATH / f).is_file()]
if missing: if missing:
print(f"Tinygrad model {model} is missing files. Preparing to download...") print(f"Tinygrad model {model} is missing files. Preparing to download...")
+55 -19
View File
@@ -13,13 +13,13 @@ from openpilot.common.conversions import Conversions as CV
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.selfdrive.car import gen_empty_fingerprint from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.selfdrive.car.car_helpers import interfaces 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.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.mock.interface import CarInterface from openpilot.selfdrive.car.mock.interface import CarInterface
from openpilot.selfdrive.car.mock.values import CAR as MOCK from openpilot.selfdrive.car.mock.values import CAR as MOCK
from openpilot.selfdrive.car.toyota.values import ToyotaFlags, ToyotaFrogPilotFlags from openpilot.selfdrive.car.toyota.values import ToyotaFlags, ToyotaFrogPilotFlags
from openpilot.selfdrive.controls.lib.desire_helper import LANE_CHANGE_SPEED_MIN from openpilot.selfdrive.controls.lib.desire_helper import LANE_CHANGE_SPEED_MIN
from openpilot.selfdrive.controls.lib.latcontrol_torque import KP
from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.system.hardware import HARDWARE from openpilot.system.hardware import HARDWARE
from openpilot.system.hardware.power_monitoring import VBATT_PAUSE_CHARGING from openpilot.system.hardware.power_monitoring import VBATT_PAUSE_CHARGING
@@ -33,6 +33,7 @@ params_memory = Params("/dev/shm/params")
GearShifter = car.CarState.GearShifter GearShifter = car.CarState.GearShifter
SafetyModel = car.CarParams.SafetyModel SafetyModel = car.CarParams.SafetyModel
TransmissionType = car.CarParams.TransmissionType
CITY_SPEED_LIMIT = 25 # 55mph is typically the minimum speed for highways 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 CRUISING_SPEED = 5 # Roughly the speed cars go when not touching the gas while in drive
@@ -95,6 +96,8 @@ EXCLUDED_KEYS = {
} }
TINYGRAD_FILES = [ 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_metadata.pkl", "policy metadata"),
("driving_policy_tinygrad.pkl", "policy model"), ("driving_policy_tinygrad.pkl", "policy model"),
("driving_vision_metadata.pkl", "vision metadata"), ("driving_vision_metadata.pkl", "vision metadata"),
@@ -127,9 +130,11 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("AdjacentPath", "0", 3, "0"), ("AdjacentPath", "0", 3, "0"),
("AdjacentPathMetrics", "0", 3, "0"), ("AdjacentPathMetrics", "0", 3, "0"),
("AdvancedCustomUI", "0", 2, "0"), ("AdvancedCustomUI", "0", 2, "0"),
("AdvancedLateralTune", "0", 2, "0"), ("AdvancedLateralTune", "1", 2, "0"),
("AdvancedLongitudinalTune", "0", 3, "0"), ("AdvancedLongitudinalTune", "0", 3, "0"),
("EVTuning", "", 3, "0"),
("AggressiveFollow", "1.25", 2, "1.25"), ("AggressiveFollow", "1.25", 2, "1.25"),
("AggressiveFollowHigh", "1.25", 2, "1.25"),
("AggressiveJerkAcceleration", "50", 3, "50"), ("AggressiveJerkAcceleration", "50", 3, "50"),
("AggressiveJerkDanger", "100", 3, "100"), ("AggressiveJerkDanger", "100", 3, "100"),
("AggressiveJerkDeceleration", "50", 3, "50"), ("AggressiveJerkDeceleration", "50", 3, "50"),
@@ -148,11 +153,13 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("AvailableModelNames", "", 1, ""), ("AvailableModelNames", "", 1, ""),
("AvailableModelSeries", "", 1, ""), ("AvailableModelSeries", "", 1, ""),
("AvailableModels", "", 1, ""), ("AvailableModels", "", 1, ""),
("CommunityFavorites", "", 1, ""),
("UserFavorites", "", 0, ""),
("BigMap", "0", 2, "0"), ("BigMap", "0", 2, "0"),
("BlacklistedModels", "", 2, ""), ("BlacklistedModels", "", 2, ""),
("BlindSpotMetrics", "1", 3, "0"), ("BlindSpotMetrics", "1", 3, "0"),
("BlindSpotPath", "1", 1, "0"), ("BlindSpotPath", "1", 1, "0"),
("BorderMetrics", "0", 3, "0"), ("BorderMetrics", "1", 3, "0"),
("CalibratedLateralAcceleration", str(DEFAULT_LATERAL_ACCELERATION), 2, str(DEFAULT_LATERAL_ACCELERATION)), ("CalibratedLateralAcceleration", str(DEFAULT_LATERAL_ACCELERATION), 2, str(DEFAULT_LATERAL_ACCELERATION)),
("CalibrationProgress", "0", 3, "0"), ("CalibrationProgress", "0", 3, "0"),
("CameraView", "3", 2, "0"), ("CameraView", "3", 2, "0"),
@@ -191,7 +198,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("CustomUI", "1", 1, "0"), ("CustomUI", "1", 1, "0"),
("DecelerationProfile", "1", 2, "0"), ("DecelerationProfile", "1", 2, "0"),
("DeveloperMetrics", "1", 3, "0"), ("DeveloperMetrics", "1", 3, "0"),
("DeveloperSidebar", "1", 3, "0"), ("DeveloperSidebar", "0", 3, "0"),
("DeveloperSidebarMetric1", "1", 3, "0"), ("DeveloperSidebarMetric1", "1", 3, "0"),
("DeveloperSidebarMetric2", "2", 3, "0"), ("DeveloperSidebarMetric2", "2", 3, "0"),
("DeveloperSidebarMetric3", "3", 3, "0"), ("DeveloperSidebarMetric3", "3", 3, "0"),
@@ -200,7 +207,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("DeveloperSidebarMetric6", "6", 3, "0"), ("DeveloperSidebarMetric6", "6", 3, "0"),
("DeveloperSidebarMetric7", "7", 3, "0"), ("DeveloperSidebarMetric7", "7", 3, "0"),
("DeveloperWidgets", "1", 3, "0"), ("DeveloperWidgets", "1", 3, "0"),
("DeveloperUI", "0", 3, "0"), ("DeveloperUI", "1", 3, "0"),
("DeviceManagement", "1", 1, "0"), ("DeviceManagement", "1", 1, "0"),
("DeviceShutdown", "9", 1, "33"), ("DeviceShutdown", "9", 1, "33"),
("DisableOnroadUploads", "0", 2, "0"), ("DisableOnroadUploads", "0", 2, "0"),
@@ -218,7 +225,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("Fahrenheit", "0", 3, "0"), ("Fahrenheit", "0", 3, "0"),
("FavoriteDestinations", "", 0, ""), ("FavoriteDestinations", "", 0, ""),
("ForceAutoTune", "0", 2, "0"), ("ForceAutoTune", "0", 2, "0"),
("ForceAutoTuneOff", "0", 2, "0"), ("ForceAutoTuneOff", "1", 2, "0"),
("ForceFingerprint", "0", 2, "0"), ("ForceFingerprint", "0", 2, "0"),
("ForceMPHDashboard", "0", 2, "0"), ("ForceMPHDashboard", "0", 2, "0"),
("ForceStops", "0", 2, "0"), ("ForceStops", "0", 2, "0"),
@@ -229,6 +236,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("FrogsGoMoosTweak", "1", 2, "0"), ("FrogsGoMoosTweak", "1", 2, "0"),
("FullMap", "0", 2, "0"), ("FullMap", "0", 2, "0"),
("GasRegenCmd", "1", 2, "0"), ("GasRegenCmd", "1", 2, "0"),
("GMPedalLongitudinal", "1", 2, "1"),
("GithubSshKeys", "", 0, ""), ("GithubSshKeys", "", 0, ""),
("GithubUsername", "", 0, ""), ("GithubUsername", "", 0, ""),
("GoatScream", "0", 1, "0"), ("GoatScream", "0", 1, "0"),
@@ -268,6 +276,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("LongitudinalActuatorDelay", "", 3, ""), ("LongitudinalActuatorDelay", "", 3, ""),
("LongitudinalActuatorDelayStock", "", 3, ""), ("LongitudinalActuatorDelayStock", "", 3, ""),
("LongitudinalTune", "1", 0, "0"), ("LongitudinalTune", "1", 0, "0"),
("TrailerLoad", "0", 2, "0"),
("LongPitch", "1", 2, "0"), ("LongPitch", "1", 2, "0"),
("LoudBlindspotAlert", "0", 0, "0"), ("LoudBlindspotAlert", "0", 0, "0"),
("LowVoltageShutdown", str(VBATT_PAUSE_CHARGING), 2, str(VBATT_PAUSE_CHARGING)), ("LowVoltageShutdown", str(VBATT_PAUSE_CHARGING), 2, str(VBATT_PAUSE_CHARGING)),
@@ -283,8 +292,10 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("Model", DEFAULT_MODEL, 1, DEFAULT_MODEL), ("Model", DEFAULT_MODEL, 1, DEFAULT_MODEL),
("ModelDrivesAndScores", "", 2, ""), ("ModelDrivesAndScores", "", 2, ""),
("ModelRandomizer", "0", 2, "0"), ("ModelRandomizer", "0", 2, "0"),
("ModelReleasedDates", "", 1, ""),
("ModelUI", "1", 2, "0"), ("ModelUI", "1", 2, "0"),
("ModelVersions", "", 2, ""), ("ModelVersions", "", 2, ""),
("SortModelsByDate", "0", 2, "0"),
("NavigationUI", "1", 1, "0"), ("NavigationUI", "1", 1, "0"),
("NavSettingLeftSide", "0", 0, "0"), ("NavSettingLeftSide", "0", 0, "0"),
("NavSettingTime24h", "0", 0, "0"), ("NavSettingTime24h", "0", 0, "0"),
@@ -325,13 +336,14 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("RecordFront", "0", 0, "0"), ("RecordFront", "0", 0, "0"),
("RefuseVolume", "101", 2, "101"), ("RefuseVolume", "101", 2, "101"),
("RelaxedFollow", "1.75", 2, "1.75"), ("RelaxedFollow", "1.75", 2, "1.75"),
("RelaxedFollowHigh", "1.75", 2, "1.75"),
("RelaxedJerkAcceleration", "50", 3, "50"), ("RelaxedJerkAcceleration", "50", 3, "50"),
("RelaxedJerkDanger", "100", 3, "100"), ("RelaxedJerkDanger", "100", 3, "100"),
("RelaxedJerkDeceleration", "50", 3, "50"), ("RelaxedJerkDeceleration", "50", 3, "50"),
("RelaxedJerkSpeed", "50", 3, "50"), ("RelaxedJerkSpeed", "50", 3, "50"),
("RelaxedJerkSpeedDecrease", "50", 3, "50"), ("RelaxedJerkSpeedDecrease", "50", 3, "50"),
("RelaxedPersonalityProfile", "1", 2, "0"), ("RelaxedPersonalityProfile", "1", 2, "0"),
("ReverseCruise", "0", 1, "0"), ("ReverseCruise", "1", 1, "0"),
("RoadEdgesWidth", "2", 2, "2"), ("RoadEdgesWidth", "2", 2, "2"),
("RoadNameUI", "1", 2, "0"), ("RoadNameUI", "1", 2, "0"),
("RotatingWheel", "1", 1, "0"), ("RotatingWheel", "1", 1, "0"),
@@ -355,7 +367,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("ShownToggleDescriptions", "", 0, ""), ("ShownToggleDescriptions", "", 0, ""),
("ShowSLCOffset", "1", 0, "0"), ("ShowSLCOffset", "1", 0, "0"),
("ShowSpeedLimits", "1", 1, "0"), ("ShowSpeedLimits", "1", 1, "0"),
("ShowSteering", "0", 3, "0"), ("ShowSteering", "1", 3, "0"),
("ShowStoppingPoint", "0", 2, "0"), ("ShowStoppingPoint", "0", 2, "0"),
("ShowStoppingPointMetrics", "0", 2, "0"), ("ShowStoppingPointMetrics", "0", 2, "0"),
("ShowStorageLeft", "0", 3, "0"), ("ShowStorageLeft", "0", 3, "0"),
@@ -382,6 +394,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("StartupMessageBottom", "Human-tested, frog-approved 🐸", 0, "Always keep hands on wheel and eyes on road"), ("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"), ("StartupMessageTop", "Hop in and buckle up!", 0, "Be ready to take over at any time"),
("StandardFollow", "1.45", 2, "1.45"), ("StandardFollow", "1.45", 2, "1.45"),
("StandardFollowHigh", "1.45", 2, "1.45"),
("StandardJerkAcceleration", "50", 3, "50"), ("StandardJerkAcceleration", "50", 3, "50"),
("StandardJerkDanger", "100", 3, "100"), ("StandardJerkDanger", "100", 3, "100"),
("StandardJerkDeceleration", "50", 3, "50"), ("StandardJerkDeceleration", "50", 3, "50"),
@@ -396,6 +409,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("SteerDelayStock", "", 3, ""), ("SteerDelayStock", "", 3, ""),
("SteerFriction", "", 3, ""), ("SteerFriction", "", 3, ""),
("SteerFrictionStock", "", 3, ""), ("SteerFrictionStock", "", 3, ""),
("SteerOffset", "", 3, ""),
("SteerOffsetStock", "", 3, ""),
("SteerKP", "", 3, ""), ("SteerKP", "", 3, ""),
("SteerKPStock", "", 3, ""), ("SteerKPStock", "", 3, ""),
("SteerLatAccel", "", 3, ""), ("SteerLatAccel", "", 3, ""),
@@ -418,8 +433,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("TrafficJerkSpeed", "50", 3, "50"), ("TrafficJerkSpeed", "50", 3, "50"),
("TrafficJerkSpeedDecrease", "50", 3, "50"), ("TrafficJerkSpeedDecrease", "50", 3, "50"),
("TrafficPersonalityProfile", "1", 2, "0"), ("TrafficPersonalityProfile", "1", 2, "0"),
("TuningLevel", "0", 0, "0"), ("TuningLevel", "3", 0, "0"),
("TuningLevelConfirmed", "0", 0, "0"), ("TuningLevelConfirmed", "1", 0, "0"),
("TurnDesires", "0", 2, "0"), ("TurnDesires", "0", 2, "0"),
("UnlimitedLength", "1", 2, "0"), ("UnlimitedLength", "1", 2, "0"),
("UnlockDoors", "1", 0, "0"), ("UnlockDoors", "1", 0, "0"),
@@ -437,7 +452,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("WarningSoftVolume", "101", 2, "101"), ("WarningSoftVolume", "101", 2, "101"),
("WheelIcon", "frog", 0, "stock"), ("WheelIcon", "frog", 0, "stock"),
("WheelSpeed", "0", 2, "0"), ("WheelSpeed", "0", 2, "0"),
("StopDistance", "6", 3, "6") ("StopDistance", "6", 3, "6"),
("RecoveryPower", "1.0", 2, "1.0")
] ]
misc_tuning_levels: list[tuple[str, str | bytes, int, str]] = [ misc_tuning_levels: list[tuple[str, str | bytes, int, str]] = [
@@ -557,6 +573,7 @@ class FrogPilotVariables:
toggle.has_pedal = CP.enableGasInterceptor toggle.has_pedal = CP.enableGasInterceptor
has_radar = not CP.radarUnavailable has_radar = not CP.radarUnavailable
toggle.has_sdsu = toggle.car_make == "toyota" and bool(CP.flags & ToyotaFlags.SMART_DSU.value) 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 has_sng = CP.autoResumeSng
toggle.has_zss = toggle.car_make == "toyota" and bool(FPCP.fpFlags & ToyotaFrogPilotFlags.ZSS.value) toggle.has_zss = toggle.car_make == "toyota" and bool(FPCP.fpFlags & ToyotaFrogPilotFlags.ZSS.value)
is_angle_car = CP.steerControlType == car.CarParams.SteerControlType.angle is_angle_car = CP.steerControlType == car.CarParams.SteerControlType.angle
@@ -567,7 +584,7 @@ class FrogPilotVariables:
startAccel = CP.startAccel startAccel = CP.startAccel
stopAccel = CP.stopAccel stopAccel = CP.stopAccel
steerActuatorDelay = CP.steerActuatorDelay steerActuatorDelay = CP.steerActuatorDelay
steerKp = CP.lateralTuning.pid.kp if CP.lateralTuning.which() == "pid" else KP steerKp = CP.lateralTuning.torque.kp
steerRatio = CP.steerRatio steerRatio = CP.steerRatio
toggle.stoppingDecelRate = CP.stoppingDecelRate toggle.stoppingDecelRate = CP.stoppingDecelRate
taco_hacks_allowed = CP.safetyConfigs[0].safetyModel == SafetyModel.hyundaiCanfd taco_hacks_allowed = CP.safetyConfigs[0].safetyModel == SafetyModel.hyundaiCanfd
@@ -600,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.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.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.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.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.steerKp = [[0], [np.clip(params.get_float("SteerKP"), steerKp * 0.5, steerKp * 1.5) if advanced_lateral_tuning and is_torque_car and tuning_level >= level["SteerKP"] else steerKp]]
toggle.latAccelFactor = np.clip(params.get_float("SteerLatAccel"), latAccelFactor * 0.75, latAccelFactor * 1.25) if advanced_lateral_tuning and tuning_level >= level["SteerLatAccel"] else latAccelFactor toggle.latAccelFactor = np.clip(params.get_float("SteerLatAccel"), latAccelFactor * 0.75, latAccelFactor * 1.25) if advanced_lateral_tuning and tuning_level >= level["SteerLatAccel"] else latAccelFactor
@@ -608,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 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") 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.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.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 toggle.stopAccel = np.clip(params.get_float("StopAccel"), -4, 0) if advanced_longitudinal_tuning and tuning_level >= level["StopAccel"] else stopAccel
@@ -617,6 +642,8 @@ class FrogPilotVariables:
toggle.stop_distance = params.get_float("StopDistance") if advanced_longitudinal_tuning and tuning_level >= level["StopDistance"] else 6.0 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.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.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") toggle.engage_volume = params.get_int("EngageVolume") if toggle.alert_volume_controller and tuning_level >= level["EngageVolume"] else default.get_int("EngageVolume")
@@ -672,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_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 = 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_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")) 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_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_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_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 = 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_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")) 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_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_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_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 = 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_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")) 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_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_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_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 = [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_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") 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) 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)
@@ -822,13 +852,17 @@ class FrogPilotVariables:
toggle.human_following = longitudinal_tuning and (params.get_bool("HumanFollowing") if tuning_level >= level["HumanFollowing"] else default.get_bool("HumanFollowing")) 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.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.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.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 "" toggle.available_models = params.get("AvailableModels", encoding="utf-8") or ""
toggle.available_model_names = params.get("AvailableModelNames", 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.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.model_versions = params.get("ModelVersions", encoding="utf-8") or ""
toggle.available_model_series = params.get("AvailableModelSeries", 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}*"))] 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")) 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 toggle.available_models and toggle.available_model_names and downloaded_models and toggle.model_versions:
@@ -859,7 +893,7 @@ class FrogPilotVariables:
toggle.model_version = DEFAULT_MODEL_VERSION toggle.model_version = DEFAULT_MODEL_VERSION
toggle.classic_longitudinal = toggle.model_version in {"v1", "v2", "v3", "v4"} toggle.classic_longitudinal = toggle.model_version in {"v1", "v2", "v3", "v4"}
toggle.classic_model = toggle.model_version in {"v1", "v2", "v3", "v4"} toggle.classic_model = toggle.model_version in {"v1", "v2", "v3", "v4"}
toggle.tinygrad_model = toggle.model_version in {"v8", "v9", "v10", "v11"} toggle.tinygrad_model = toggle.model_version in {"v8", "v9", "v10", "v11", "v12"}
toggle.tomb_raider = toggle.model == "space-lab" 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.model_ui = params.get_bool("ModelUI") if tuning_level >= level["ModelUI"] else default.get_bool("ModelUI")
@@ -974,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.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.put("FrogPilotToggles", json.dumps(toggle.__dict__))
params_memory.remove("FrogPilotTogglesUpdated") params_memory.remove("FrogPilotTogglesUpdated")
+1 -1
View File
@@ -87,7 +87,7 @@ class FrogPilotPlanner:
params_memory.remove("LastGPSPosition") 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 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: if check_lane_width and v_ego >= frogpilot_toggles.minimum_lane_change_speed:
@@ -47,14 +47,18 @@ A_CRUISE_MIN_SPORT = A_CRUISE_MIN * 2
# MPH = [0.0, 11, 22, 34, 45, 56, 89] # 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_BP_CUSTOM = [0.0, 5., 10., 15., 20., 25., 40.]
A_CRUISE_MAX_VALS_ECO = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] 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 = [1.5, 1.5, 1.25, 1.5, 1.5, 1.5, 2.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): def get_max_accel_eco(v_ego, ev_tuning=True):
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_ECO)) 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): def get_max_accel_sport(v_ego, ev_tuning=True):
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT)) 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): def get_max_accel_low_speeds(max_accel, v_cruise):
return float(akima_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]))
@@ -75,22 +79,23 @@ class FrogPilotAcceleration:
def update(self, v_ego, sm, frogpilot_toggles): def update(self, v_ego, sm, frogpilot_toggles):
eco_gear = sm["frogpilotCarState"].ecoGear eco_gear = sm["frogpilotCarState"].ecoGear
sport_gear = sm["frogpilotCarState"].sportGear sport_gear = sm["frogpilotCarState"].sportGear
ev_tuning = frogpilot_toggles.ev_tuning
if sm["frogpilotCarState"].trafficModeEnabled: if sm["frogpilotCarState"].trafficModeEnabled:
self.max_accel = get_max_accel(v_ego) self.max_accel = get_max_accel(v_ego)
elif frogpilot_toggles.map_acceleration and (eco_gear or sport_gear): elif frogpilot_toggles.map_acceleration and (eco_gear or sport_gear):
if eco_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: else:
if frogpilot_toggles.acceleration_profile == 2: 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: else:
self.max_accel = get_max_allowed_accel(v_ego) self.max_accel = get_max_allowed_accel(v_ego)
else: else:
if frogpilot_toggles.acceleration_profile == 1: 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: 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: elif frogpilot_toggles.acceleration_profile == 3:
self.max_accel = get_max_allowed_accel(v_ego) self.max_accel = get_max_allowed_accel(v_ego)
else: else:
@@ -1,11 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import numpy as np import numpy as np
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.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 from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT
TRAFFIC_MODE_BP = [0., CITY_SPEED_LIMIT] TRAFFIC_MODE_BP = [0., CITY_SPEED_LIMIT]
PERSONALITY_BP = [20. * CV.KPH_TO_MS, 90. * CV.KPH_TO_MS]
class FrogPilotFollowing: class FrogPilotFollowing:
def __init__(self, FrogPilotPlanner): def __init__(self, FrogPilotPlanner):
@@ -48,12 +50,16 @@ class FrogPilotFollowing:
frogpilot_toggles.custom_personalities, sm["controlsState"].personality 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.aggressive_follow,
frogpilot_toggles.standard_follow, frogpilot_toggles.standard_follow,
frogpilot_toggles.relaxed_follow, frogpilot_toggles.relaxed_follow,
frogpilot_toggles.custom_personalities, sm["controlsState"].personality 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: else:
self.base_acceleration_jerk = 0 self.base_acceleration_jerk = 0
self.base_danger_jerk = 0 self.base_danger_jerk = 0
+1 -1
View File
@@ -51,7 +51,7 @@ class FrogPilotTracking:
self.sound = FrogPilotAudibleAlert.none self.sound = FrogPilotAudibleAlert.none
self.state = State.disabled 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): def update(self, now, time_validated, sm, frogpilot_toggles):
v_cruise = min(sm["controlsState"].vCruiseCluster, V_CRUISE_MAX) * CV.KPH_TO_MS v_cruise = min(sm["controlsState"].vCruiseCluster, V_CRUISE_MAX) * CV.KPH_TO_MS
@@ -13,7 +13,6 @@ from openpilot.common.numpy_fast import interp
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N
from openpilot.selfdrive.controls.lib.latcontrol import LatControl from openpilot.selfdrive.controls.lib.latcontrol import LatControl
from openpilot.selfdrive.controls.lib.latcontrol_torque import KD, KI, KP
from openpilot.selfdrive.controls.lib.pid import PIDController from openpilot.selfdrive.controls.lib.pid import PIDController
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.modeld.constants import ModelConstants
@@ -162,7 +161,7 @@ class LatControlNNFF(LatControl):
self.nnff_loaded = self.lat_torque_nn_model is not None self.nnff_loaded = self.lat_torque_nn_model is not None
self.torque_params = CP.lateralTuning.torque self.torque_params = CP.lateralTuning.torque
self.pid = PIDController(KP, KI, k_d=KD, self.pid = PIDController(self.torque_params.kp, self.torque_params.ki,
pos_limit=self.steer_max, neg_limit=-self.steer_max) pos_limit=self.steer_max, neg_limit=-self.steer_max)
self.torque_from_lateral_accel = CI.torque_from_lateral_accel() self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
self.use_steering_angle = self.torque_params.useSteeringAngle self.use_steering_angle = self.torque_params.useSteeringAngle
BIN
View File
Binary file not shown.
Binary file not shown.
+53 -33
View File
@@ -20,6 +20,30 @@ from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles
BASE_URL = "https://nominatim.openstreetmap.org" BASE_URL = "https://nominatim.openstreetmap.org"
MINIMUM_POPULATION = 100_000 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): def get_city_center(latitude, longitude):
try: try:
@@ -44,14 +68,7 @@ def get_city_center(latitude, longitude):
if data: if data:
tags = data[0] tags = data[0]
population = (tags.get("extratags") or {}).get("population") population_value = get_population_value((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
if population_value is not None and population_value >= MINIMUM_POPULATION: if population_value is not None and population_value >= MINIMUM_POPULATION:
latitude_value = float(tags["lat"]) 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 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}" 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 = session.get(f"{BASE_URL}/search", params={"addressdetails": 1, "extratags": 1, "format": "jsonv2", "limit": 5, "q": query}, timeout=10)
response.raise_for_status() response.raise_for_status()
@@ -99,14 +120,14 @@ def get_city_center(latitude, longitude):
def update_branch_commits(now): def update_branch_commits(now):
points = [] points = []
for branch in ["FrogPilot", "FrogPilot-Staging", "FrogPilot-Testing"]: branch = get_build_metadata().channel # Current running branch
try: try:
response = requests.get(f"https://api.github.com/repos/FrogAi/FrogPilot/commits/{branch}") response = requests.get(f"https://api.github.com/repos/firestar5683/StarPilot/commits/{branch}")
response.raise_for_status() response.raise_for_status()
sha = response.json()["sha"] sha = response.json()["sha"]
points.append(Point("branch_commits").field("commit", sha).tag("branch", branch).time(now)) points.append(Point("branch_commits").field("commit", sha).tag("branch", branch).time(now))
except Exception as e: except Exception as e:
print(f"Failed to fetch commit for {branch}: {e}") print(f"Failed to fetch commit for {branch}: {e}")
return points return points
@@ -129,11 +150,9 @@ def send_stats():
if frogpilot_toggles.car_make == "mock": if frogpilot_toggles.car_make == "mock":
return return
bucket = os.environ.get("STATS_BUCKET", "") bucket = "StarPilot"
org_ID = os.environ.get("STATS_ORG_ID", "") org_ID = "StarPilot"
token = os.environ.get("STATS_TOKEN", "") url = "https://stats.firestar.link"
url = os.environ.get("STATS_URL", "")
frogpilot_stats = json.loads(params.get("FrogPilotStats") or "{}") frogpilot_stats = json.loads(params.get("FrogPilotStats") or "{}")
location = json.loads(params.get("LastGPSPosition") or "{}") location = json.loads(params.get("LastGPSPosition") or "{}")
@@ -161,14 +180,19 @@ def send_stats():
user_point = ( user_point = (
Point("user_stats") 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("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("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("event", 1)
.field("frogpilot_drives", int(frogpilot_stats.get("FrogPilotDrives", 0))) .field("frogpilot_drives", int(frogpilot_stats.get("FrogPilotDrives", 0)))
.field("frogpilot_hours", float(frogpilot_stats.get("FrogPilotSeconds", 0)) / (60 * 60)) .field("frogpilot_hours", float(frogpilot_stats.get("FrogPilotSeconds", 0)) / (60 * 60))
@@ -178,13 +202,12 @@ def send_stats():
.field("has_openpilot_longitudinal", frogpilot_toggles.openpilot_longitudinal) .field("has_openpilot_longitudinal", frogpilot_toggles.openpilot_longitudinal)
.field("has_pedal", frogpilot_toggles.has_pedal) .field("has_pedal", frogpilot_toggles.has_pedal)
.field("has_sdsu", frogpilot_toggles.has_sdsu) .field("has_sdsu", frogpilot_toggles.has_sdsu)
.field("has_sascm", frogpilot_toggles.has_sascm)
.field("has_zss", frogpilot_toggles.has_zss) .field("has_zss", frogpilot_toggles.has_zss)
.field("latitude", latitude) .field("latitude", latitude)
.field("longitude", longitude) .field("longitude", longitude)
.field("rainbow_path", frogpilot_toggles.rainbow_path) .field("rainbow_path", frogpilot_toggles.rainbow_path)
.field("random_events", frogpilot_toggles.random_events) .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_aol_seconds", float(frogpilot_stats.get("AOLTime", 0)))
.field("total_lateral_seconds", float(frogpilot_stats.get("LateralTime", 0))) .field("total_lateral_seconds", float(frogpilot_stats.get("LateralTime", 0)))
.field("total_longitudinal_seconds", float(frogpilot_stats.get("LongitudinalTime", 0))) .field("total_longitudinal_seconds", float(frogpilot_stats.get("LongitudinalTime", 0)))
@@ -193,15 +216,12 @@ def send_stats():
.field("up_to_date", is_up_to_date(build_metadata)) .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)) .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(now) .time(now)
) )
all_points = [user_point] + update_branch_commits(now) all_points = [user_point] + update_branch_commits(now)
client = InfluxDBClient(org=org_ID, token=token, url=url) 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) client.write_api(write_options=SYNCHRONOUS).write(bucket=bucket, org=org_ID, record=all_points)
print("Successfully sent FrogPilot stats!") print("Successfully sent FrogPilot stats!")
except Exception as exception: except Exception as exception:
+3 -4
View File
@@ -11,16 +11,15 @@ ConfidenceClass = log.ModelDataV2.ConfidenceClass
# Return curvature for lateral action. If the model outputs desired_curvature and we're not in mlsim mode, # 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. # use it directly; otherwise derive from the plan using yaw and yaw-rate.
def get_curvature_from_output(output: dict, v_ego: float, lat_action_t: float, mlsim: bool) -> float: def get_curvature_from_output(output: dict, plan: np.ndarray, v_ego: float, lat_action_t: float, mlsim: bool) -> float:
if not mlsim: if not mlsim:
desired = output.get('desired_curvature') desired = output.get('desired_curvature')
if desired is not None: if desired is not None:
return float(desired[0, 0]) return float(desired[0, 0])
plan_out = output['plan'][0]
# Use yaw (index 2) and yaw_rate (index 2) # Use yaw (index 2) and yaw_rate (index 2)
theta = plan_out[:, Plan.T_FROM_CURRENT_EULER][:, 2] theta = plan[:, Plan.T_FROM_CURRENT_EULER][:, 2]
theta_dot = plan_out[:, Plan.ORIENTATION_RATE][:, 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)) return float(get_curvature_from_plan(theta, theta_dot, ModelConstants.T_IDXS, v_ego, lat_action_t))
@@ -97,12 +97,12 @@ class Parser:
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, 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)) out_shape=(ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH))
if 'plan' in outs: if 'plan' in outs:
if outs['plan'].shape[1] == 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH: plan_mhp = outs['plan'].shape[1] != 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH
self.parse_mdn('plan', outs, in_N=0, out_N=0, plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0)
out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH)) self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N,
else: out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, if 'planplus' in outs:
out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH)) 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: if 'lane_lines' in outs:
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, 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)) out_shape=(ModelConstants.NUM_LANE_LINES, ModelConstants.IDX_N, ModelConstants.LANE_LINES_WIDTH))
+110 -31
View File
@@ -35,14 +35,17 @@ PROCESS_NAME = "frogpilot.tinygrad_modeld.tinygrad_modeld"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
LAT_SMOOTH_SECONDS = 0.1 LAT_SMOOTH_SECONDS = 0.0
LONG_SMOOTH_SECONDS = 0.3 LONG_SMOOTH_SECONDS = 0.3
MIN_LAT_CONTROL_SPEED = 0.3 MIN_LAT_CONTROL_SPEED = 0.3
def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action, 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, mlsim: bool, is_v9: 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] plan = model_output['plan'][0]
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], desired_accel, should_stop = get_accel_from_plan_tomb_raider(plan[:,Plan.VELOCITY][:,0],
plan[:,Plan.ACCELERATION][:,0], plan[:,Plan.ACCELERATION][:,0],
ModelConstants.T_IDXS, ModelConstants.T_IDXS,
@@ -56,7 +59,7 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.
else: else:
desired_curvature = prev_action.desiredCurvature desired_curvature = prev_action.desiredCurvature
else: else:
desired_curvature = get_curvature_from_output(model_output, v_ego, lat_action_t, mlsim=mlsim) desired_curvature = get_curvature_from_output(model_output, plan, v_ego, lat_action_t, mlsim=mlsim)
if v_ego > MIN_LAT_CONTROL_SPEED: if v_ego > MIN_LAT_CONTROL_SPEED:
desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS) desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS)
else: else:
@@ -81,6 +84,34 @@ class ModelState:
output: np.ndarray output: np.ndarray
prev_desire: np.ndarray # for tracking the rising edge of the pulse prev_desire: np.ndarray # for tracking the rising edge of the pulse
def _build_policy_inputs(self, input_shapes: dict[str, tuple[int, ...]]) -> tuple[dict[str, np.ndarray], str | None]:
numpy_inputs: dict[str, np.ndarray] = {}
# 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)
# 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)
# 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): def __init__(self, context: CLContext):
# Dynamically build paths based on current model ID # Dynamically build paths based on current model ID
params = Params() params = Params()
@@ -99,13 +130,17 @@ class ModelState:
models_dir = Path(__file__).parent / "models" models_dir = Path(__file__).parent / "models"
VISION_PKL_PATH = models_dir / "driving_vision_tinygrad.pkl" VISION_PKL_PATH = models_dir / "driving_vision_tinygrad.pkl"
POLICY_PKL_PATH = models_dir / "driving_policy_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" VISION_METADATA_PATH = models_dir / "driving_vision_metadata.pkl"
POLICY_METADATA_PATH = models_dir / "driving_policy_metadata.pkl" POLICY_METADATA_PATH = models_dir / "driving_policy_metadata.pkl"
OFF_POLICY_METADATA_PATH = models_dir / "driving_off_policy_metadata.pkl"
else: else:
VISION_PKL_PATH = model_dir / f"{model_id}_driving_vision_tinygrad.pkl" VISION_PKL_PATH = model_dir / f"{model_id}_driving_vision_tinygrad.pkl"
POLICY_PKL_PATH = model_dir / f"{model_id}_driving_policy_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" VISION_METADATA_PATH = model_dir / f"{model_id}_driving_vision_metadata.pkl"
POLICY_METADATA_PATH = model_dir / f"{model_id}_driving_policy_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 ModelVersion is not set or not available, try to determine it from available model data
if not model_version: if not model_version:
@@ -157,7 +192,7 @@ class ModelState:
self.policy_generation = model_version or "v8" self.policy_generation = model_version or "v8"
self.is_v11 = (self.policy_generation == "v11") self.is_v11 = (self.policy_generation == "v11")
self.is_v9 = (self.policy_generation == "v9") self.is_v9 = (self.policy_generation == "v9")
self.mlsim = (self.policy_generation in ("v8", "v10", "v11")) 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.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.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
@@ -168,33 +203,50 @@ class ModelState:
# policy inputs (built dynamically to support all generations) # policy inputs (built dynamically to support all generations)
self.numpy_inputs = {} self.numpy_inputs, self.prev_desired_curv_key = self._build_policy_inputs(self.policy_input_shapes)
# Always-supported inputs (if model expects them) # Off-policy model (optional)
desire_key_init = next((k for k in self.policy_input_shapes if k.startswith('desire')), None) self.off_policy_enabled = False
if desire_key_init: self.off_policy_input_shapes: dict[str, tuple[int, ...]] = {}
self.numpy_inputs[desire_key_init] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32) self.off_policy_output_slices: dict[str, slice] = {}
if 'traffic_convention' in self.policy_input_shapes: self.off_policy_numpy_inputs: dict[str, np.ndarray] = {}
self.numpy_inputs['traffic_convention'] = np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32) self.off_policy_prev_desired_curv_key: str | None = None
if 'features_buffer' in self.policy_input_shapes: self.off_policy_desire_key: str | None = None
self.numpy_inputs['features_buffer'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32) self.off_policy_inputs: dict[str, Tensor] | None = None
self.off_policy_output: np.ndarray | None = None
# Optional inputs for non-v11 (and some v10/v9 variants) off_policy_metadata = None
# Lateral control params if self.policy_generation == "v12" or OFF_POLICY_METADATA_PATH.is_file() or OFF_POLICY_PKL_PATH.is_file():
if 'lateral_control_params' in self.policy_input_shapes: try:
self.numpy_inputs['lateral_control_params'] = np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32) 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}")
# Previous desired curvature: handle both singular and plural key names across model versions if off_policy_metadata is not None:
self.prev_desired_curv_key = None self.off_policy_input_shapes = off_policy_metadata['input_shapes']
if 'prev_desired_curv' in self.policy_input_shapes: self.off_policy_output_slices = off_policy_metadata['output_slices']
self.prev_desired_curv_key = 'prev_desired_curv' off_policy_output_size = off_policy_metadata['output_shapes']['outputs'][1]
self.numpy_inputs['prev_desired_curv'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32) self.off_policy_numpy_inputs, self.off_policy_prev_desired_curv_key = self._build_policy_inputs(self.off_policy_input_shapes)
elif 'prev_desired_curvs' in self.policy_input_shapes: self.off_policy_desire_key = next((k for k in self.off_policy_numpy_inputs if k.startswith('desire')), None)
self.prev_desired_curv_key = 'prev_desired_curvs' self.off_policy_inputs = {k: Tensor(v, device='NPY').realize() for k, v in self.off_policy_numpy_inputs.items()}
self.numpy_inputs['prev_desired_curvs'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32) 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 the policy expects it) # Optional temporal buffer for previous desired curvature (allocate only if any model expects it)
if getattr(self, 'prev_desired_curv_key', None) is not None: 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) self.full_prev_desired_curv = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
@@ -204,6 +256,7 @@ class ModelState:
self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()} 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.policy_output = np.zeros(policy_output_size, dtype=np.float32)
self.parser = Parser() self.parser = Parser()
self.off_policy_parser = Parser(ignore_missing=True)
with open(VISION_PKL_PATH, "rb") as f: with open(VISION_PKL_PATH, "rb") as f:
self.vision_run = pickle.load(f) self.vision_run = pickle.load(f)
@@ -229,10 +282,18 @@ class ModelState:
self.full_desire[0,:-1] = self.full_desire[0,1:] self.full_desire[0,:-1] = self.full_desire[0,1:]
self.full_desire[0,-1] = new_desire 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) 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']
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
if 'lateral_control_params' in self.numpy_inputs: if 'lateral_control_params' in self.numpy_inputs:
self.numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params'] 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: if prepare_only:
return None return None
@@ -254,7 +315,10 @@ class ModelState:
self.full_features_buffer[0,:-1] = self.full_features_buffer[0,1:] self.full_features_buffer[0,:-1] = self.full_features_buffer[0,1:]
self.full_features_buffer[0,-1] = vision_outputs_dict['hidden_state'][0, :] self.full_features_buffer[0,-1] = vision_outputs_dict['hidden_state'][0, :]
self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs] 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() 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)) policy_outputs_dict = self.parser.parse_policy_outputs(self.slice_outputs(self.policy_output, self.policy_output_slices))
@@ -271,9 +335,24 @@ class ModelState:
else: else:
self.numpy_inputs[self.prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs] 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} 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: 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 return combined_outputs_dict
@@ -442,7 +521,7 @@ def main(demo=False):
drivingdata_send = messaging.new_message('drivingModelData') drivingdata_send = messaging.new_message('drivingModelData')
posenet_send = messaging.new_message('cameraOdometry') 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, model.mlsim, model.is_v9) 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 prev_action = action
fill_model_msg(drivingdata_send, modelv2_send, model_output, action, fill_model_msg(drivingdata_send, modelv2_send, model_output, action,
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
@@ -1,19 +1,74 @@
#include "frogpilot/ui/qt/offroad/expandable_multi_option_dialog.h" #include "frogpilot/ui/qt/offroad/expandable_multi_option_dialog.h"
#include <QPushButton> #include <QPushButton>
#include <QButtonGroup>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <QScrollBar> #include <QScrollBar>
#include <QTimer> #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" #include "selfdrive/ui/qt/widgets/scrollview.h"
ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_text, ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_text,
const QMap<QString, QStringList> &seriesToModels, const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent) const QString &current, QWidget *parent,
: DialogBase(parent), seriesToModels(seriesToModels) { 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); QFrame *container = new QFrame(this);
container->setStyleSheet(R"( container->setStyleSheet(R"(
@@ -50,6 +105,43 @@ ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_t
padding-left: 80px; padding-left: 80px;
} }
QPushButton.series-header:hover { background-color: #404040; } 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); QVBoxLayout *main_layout = new QVBoxLayout(container);
@@ -60,85 +152,103 @@ ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_t
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop); main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
main_layout->addSpacing(25); main_layout->addSpacing(25);
QWidget *listWidget = new QWidget(this); // Sort controls - simple cycling button
QVBoxLayout *listLayout = new QVBoxLayout(listWidget); QHBoxLayout *sortLayout = new QHBoxLayout();
listLayout->setSpacing(10); sortLayout->setContentsMargins(0, 0, 0, 0);
sortLayout->setSpacing(20);
sortLayout->addStretch(); // Push to the right
QButtonGroup *group = new QButtonGroup(listWidget); QLabel *sortLabel = new QLabel(tr("Sort by:"), this);
group->setExclusive(true); sortLabel->setStyleSheet("font-size: 50px; color: white;");
sortLayout->addWidget(sortLabel);
QPushButton *confirm_btn = new QPushButton(tr("Select")); QPushButton *sortButton = new QPushButton(tr("Alphabetical"), this);
confirm_btn->setObjectName("confirm_btn"); sortButton->setStyleSheet(R"(
confirm_btn->setEnabled(false); QPushButton {
background-color: #4F4F4F;
ScrollView *scroll_view = new ScrollView(listWidget, this); border: 2px solid transparent;
scroll_view->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); border-radius: 10px;
padding: 10px 20px;
// Create series headers and their expandable content font-size: 50px;
for (const QString &series : seriesToModels.keys()) { color: white;
// Series header button min-width: 250px;
QPushButton *seriesHeader = new QPushButton("" + series); text-align: center;
seriesHeader->setProperty("class", "series-header");
seriesHeader->setCheckable(false);
seriesExpanded[series] = false;
QObject::connect(seriesHeader, &QPushButton::clicked, [this, series, seriesHeader, scroll_view]() {
toggleSeries(series, seriesHeader, scroll_view);
});
listLayout->addWidget(seriesHeader);
// Container for series models (initially hidden)
QWidget *seriesContainer = new QWidget();
QVBoxLayout *seriesLayout = new QVBoxLayout(seriesContainer);
seriesLayout->setContentsMargins(20, 0, 0, 0);
seriesLayout->setSpacing(10);
seriesContainer->hide();
// Add models for this series
for (const QString &model : seriesToModels[series]) {
QPushButton *modelButton = new QPushButton(model);
modelButton->setCheckable(true);
modelButton->setChecked(model == current);
modelButton->setProperty("class", "model-option");
QObject::connect(modelButton, &QPushButton::toggled, [=](bool checked) mutable {
if (checked) {
selection = model;
confirm_btn->setEnabled(true);
// Manually apply selected style
modelButton->setStyleSheet("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;"
"}");
} else {
if (selection == model) {
confirm_btn->setEnabled(false);
}
// Reset to default style
modelButton->setStyleSheet("");
}
});
group->addButton(modelButton);
seriesLayout->addWidget(modelButton);
} }
QPushButton:hover { background-color: #5A5A5A; }
)");
seriesWidgets[series] = seriesContainer; // Set initial button text based on sort mode
listLayout->addWidget(seriesContainer); 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"));
} }
// Add stretch to keep buttons spaced correctly QWidget *sortWidget = new QWidget(container);
listLayout->addStretch(1); sortWidget->setLayout(sortLayout);
sortWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
sortLayout->setSizeConstraint(QLayout::SetFixedSize);
sortWidget->setStyleSheet("background: transparent;");
main_layout->addWidget(scroll_view); 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); main_layout->addSpacing(35);
// Cancel + confirm buttons // Cancel + confirm buttons
@@ -148,18 +258,25 @@ ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_t
QPushButton *cancel_btn = new QPushButton(tr("Cancel")); QPushButton *cancel_btn = new QPushButton(tr("Cancel"));
QObject::connect(cancel_btn, &QPushButton::clicked, this, &ConfirmationDialog::reject); QObject::connect(cancel_btn, &QPushButton::clicked, this, &ConfirmationDialog::reject);
QObject::connect(confirm_btn, &QPushButton::clicked, this, &ConfirmationDialog::accept); QObject::connect(confirmButton, &QPushButton::clicked, this, &ConfirmationDialog::accept);
blayout->addWidget(cancel_btn); blayout->addWidget(cancel_btn);
blayout->addWidget(confirm_btn); blayout->addWidget(confirmButton);
QVBoxLayout *outer_layout = new QVBoxLayout(this); QVBoxLayout *outer_layout = new QVBoxLayout(this);
outer_layout->setContentsMargins(50, 50, 50, 50); outer_layout->setContentsMargins(50, 50, 50, 50);
outer_layout->addWidget(container); outer_layout->addWidget(container);
// Initial sorting
updateSorting();
} }
void ExpandableMultiOptionDialog::toggleSeries(const QString &series, QPushButton *headerButton, ScrollView *scrollView) { void ExpandableMultiOptionDialog::toggleSeries(const QString &series, QPushButton *headerButton) {
if (!headerButton) return;
QWidget *container = seriesWidgets.value(series, nullptr);
if (!container) return;
bool expanded = seriesExpanded[series]; bool expanded = seriesExpanded[series];
QWidget *container = seriesWidgets[series];
QString seriesName = series; QString seriesName = series;
if (expanded) { if (expanded) {
@@ -171,21 +288,18 @@ void ExpandableMultiOptionDialog::toggleSeries(const QString &series, QPushButto
seriesExpanded[series] = true; seriesExpanded[series] = true;
headerButton->setText("" + seriesName); headerButton->setText("" + seriesName);
// Auto-scroll to show expanded content // Auto-scroll to place the series at the top of the viewport when expanded
if (scrollView) { if (scrollView) {
QTimer::singleShot(50, [container, scrollView]() { QPointer<QPushButton> headerPtr(headerButton);
QRect containerRect = container->geometry(); QPointer<ScrollView> scrollPtr(scrollView);
QScrollBar *vScrollBar = scrollView->verticalScrollBar(); QTimer::singleShot(50, [headerPtr, scrollPtr]() {
if (vScrollBar) { if (!scrollPtr || !headerPtr) return;
int currentValue = vScrollBar->value(); QWidget *contents = scrollPtr->widget();
int containerBottom = containerRect.bottom(); if (!contents) return;
int viewportHeight = scrollView->viewport()->height(); if (QScrollBar *vScrollBar = scrollPtr->verticalScrollBar()) {
QPoint headerTop = headerPtr->mapTo(contents, QPoint(0, 0));
// If container extends beyond viewport, scroll to show it int targetValue = qMax(headerTop.y() - 20, 0);
if (containerBottom > currentValue + viewportHeight) { vScrollBar->setValue(targetValue);
int targetValue = containerBottom - viewportHeight + 50; // Add some padding
vScrollBar->setValue(targetValue);
}
} }
}); });
} }
@@ -196,11 +310,457 @@ void ExpandableMultiOptionDialog::toggleSeries(const QString &series, QPushButto
} }
QString ExpandableMultiOptionDialog::getSelection(const QString &prompt_text, QString ExpandableMultiOptionDialog::getSelection(const QString &prompt_text,
const QMap<QString, QStringList> &seriesToModels, const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent) { const QString &current, QWidget *parent,
ExpandableMultiOptionDialog d = ExpandableMultiOptionDialog(prompt_text, seriesToModels, current, 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()) { if (d.exec()) {
return d.selection; return d.selection;
} }
return ""; 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());
}
}
}
@@ -6,23 +6,71 @@
#include <QWidget> #include <QWidget>
#include <QMap> #include <QMap>
#include <QList> #include <QList>
#include <QPointer>
#include <QComboBox>
#include <QMenu>
#include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/widgets/scrollview.h"
class QPushButton;
class ExpandableMultiOptionDialog : public DialogBase { class ExpandableMultiOptionDialog : public DialogBase {
Q_OBJECT Q_OBJECT
public: public:
explicit ExpandableMultiOptionDialog(const QString &prompt_text, const QMap<QString, QStringList> &seriesToModels, explicit ExpandableMultiOptionDialog(const QString &prompt_text, const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent); 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, static QString getSelection(const QString &prompt_text, const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent); 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 selection;
QString getCurrentSortMode() const { return currentSortMode; }
QStringList getUserFavorites() const;
private: private:
void toggleSeries(const QString &series, QPushButton *headerButton, ScrollView *scrollView); 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> seriesToModels;
QMap<QString, QStringList> baseSeriesToModels;
QMap<QString, QWidget*> seriesWidgets; QMap<QString, QWidget*> seriesWidgets;
QMap<QString, bool> seriesExpanded; 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;
}; };
@@ -269,6 +269,7 @@ void FrogPilotSettingsWindow::updateVariables() {
hasPedal = CP.getEnableGasInterceptor(); hasPedal = CP.getEnableGasInterceptor();
hasRadar = !CP.getRadarUnavailable(); hasRadar = !CP.getRadarUnavailable();
hasSDSU = frogpilot_toggles.value("has_sdsu").toBool(); hasSDSU = frogpilot_toggles.value("has_sdsu").toBool();
hasSASCM = frogpilot_toggles.value("has_sascm").toBool();
hasSNG = hasOpenpilotLongitudinal && CP.getAutoResumeSng(); hasSNG = hasOpenpilotLongitudinal && CP.getAutoResumeSng();
hasZSS = frogpilot_toggles.value("has_zss").toBool(); hasZSS = frogpilot_toggles.value("has_zss").toBool();
isAngleCar = CP.getSteerControlType() == cereal::CarParams::SteerControlType::ANGLE; isAngleCar = CP.getSteerControlType() == cereal::CarParams::SteerControlType::ANGLE;
@@ -284,6 +285,7 @@ void FrogPilotSettingsWindow::updateVariables() {
longitudinalActuatorDelay = CP.getLongitudinalActuatorDelay(); longitudinalActuatorDelay = CP.getLongitudinalActuatorDelay();
startAccel = CP.getStartAccel(); startAccel = CP.getStartAccel();
steerActuatorDelay = CP.getSteerActuatorDelay(); steerActuatorDelay = CP.getSteerActuatorDelay();
steerOffset = 0.0f;
steerKp = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::PID ? CP.getLateralTuning().getPid().getKpV()[0] : 0.6; steerKp = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::PID ? CP.getLateralTuning().getPid().getKpV()[0] : 0.6;
steerRatio = CP.getSteerRatio(); steerRatio = CP.getSteerRatio();
stopAccel = CP.getStopAccel(); stopAccel = CP.getStopAccel();
@@ -295,6 +297,7 @@ void FrogPilotSettingsWindow::updateVariables() {
float currentDelayStock = params.getFloat("SteerDelayStock"); float currentDelayStock = params.getFloat("SteerDelayStock");
float currentFrictionStock = params.getFloat("SteerFrictionStock"); float currentFrictionStock = params.getFloat("SteerFrictionStock");
float currentSteerOffsetStock = params.getFloat("SteerOffsetStock");
float currentKPStock = params.getFloat("SteerKPStock"); float currentKPStock = params.getFloat("SteerKPStock");
float currentLatAccelStock = params.getFloat("SteerLatAccelStock"); float currentLatAccelStock = params.getFloat("SteerLatAccelStock");
float currentLongDelayStock = params.getFloat("LongitudinalActuatorDelayStock"); float currentLongDelayStock = params.getFloat("LongitudinalActuatorDelayStock");
@@ -319,6 +322,13 @@ void FrogPilotSettingsWindow::updateVariables() {
params.putFloat("SteerFrictionStock", friction); 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 (currentKPStock != steerKp && steerKp != 0) {
if (params.getFloat("SteerKP") == currentKPStock || currentKPStock == 0) { if (params.getFloat("SteerKP") == currentKPStock || currentKPStock == 0) {
params.putFloat("SteerKP", steerKp); params.putFloat("SteerKP", steerKp);
@@ -391,6 +401,7 @@ void FrogPilotSettingsWindow::updateVariables() {
canUsePedal = FPCP.getCanUsePedal(); canUsePedal = FPCP.getCanUsePedal();
canUseSDSU = FPCP.getCanUseSDSU(); canUseSDSU = FPCP.getCanUseSDSU();
canUseSASCM = FPCP.getCanUseSASCM();
openpilotLongitudinalControlDisabled = FPCP.getOpenpilotLongitudinalControlDisabled(); openpilotLongitudinalControlDisabled = FPCP.getOpenpilotLongitudinalControlDisabled();
} }
@@ -13,6 +13,7 @@ public:
bool canUsePedal = false; bool canUsePedal = false;
bool canUseSDSU = false; bool canUseSDSU = false;
bool canUseSASCM = false;
bool forceOpenDescriptions = false; bool forceOpenDescriptions = false;
bool hasAutoTune = true; bool hasAutoTune = true;
bool hasBSM = true; bool hasBSM = true;
@@ -24,6 +25,7 @@ public:
bool hasPedal = false; bool hasPedal = false;
bool hasRadar = true; bool hasRadar = true;
bool hasSDSU = false; bool hasSDSU = false;
bool hasSASCM = false;
bool hasSNG = false; bool hasSNG = false;
bool hasZSS = false; bool hasZSS = false;
bool isAngleCar = false; bool isAngleCar = false;
@@ -45,6 +47,7 @@ public:
float longitudinalActuatorDelay; float longitudinalActuatorDelay;
float startAccel; float startAccel;
float steerActuatorDelay; float steerActuatorDelay;
float steerOffset;
float steerKp; float steerKp;
float steerRatio; float steerRatio;
float stopAccel; float stopAccel;
+22 -4
View File
@@ -41,6 +41,7 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
{"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"}, {"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", 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."), ""}, {"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."), ""}, {"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."), ""}, {"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."), ""}, {"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."), ""}, {"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."), ""},
@@ -87,6 +88,9 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
} else if (param == "SteerFriction") { } else if (param == "SteerFriction") {
std::vector<QString> steerFrictionButton{"Reset"}; 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); 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") { } else if (param == "SteerKP") {
std::vector<QString> steerKPButton{"Reset"}; std::vector<QString> steerKPButton{"Reset"};
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); 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);
@@ -216,6 +220,14 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
} }
}); });
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"]); steerKPToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerKP"]);
QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() { QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Kp Factor</b> to its default value?"), this)) { if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Kp Factor</b> to its default value?"), this)) {
@@ -255,6 +267,7 @@ void FrogPilotLateralPanel::showEvent(QShowEvent *event) {
steerDelayToggle->setTitle(QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(parent->steerActuatorDelay, 'f', 2))); 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))); 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->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2)));
steerKPToggle->updateControl(parent->steerKp * 0.5, parent->steerKp * 1.5); 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->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2)));
@@ -340,7 +353,6 @@ void FrogPilotLateralPanel::updateToggles() {
} }
} }
bool forcingAutoTune = !parent->hasAutoTune && params.getBool("ForceAutoTune");
bool forcingAutoTuneOff = parent->hasAutoTune && params.getBool("ForceAutoTuneOff"); bool forcingAutoTuneOff = parent->hasAutoTune && params.getBool("ForceAutoTuneOff");
bool forcingTorqueController = !parent->isAngleCar && params.getBool("ForceTorqueController"); bool forcingTorqueController = !parent->isAngleCar && params.getBool("ForceTorqueController");
bool usingNNFF = parent->hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF"); bool usingNNFF = parent->hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
@@ -401,7 +413,13 @@ void FrogPilotLateralPanel::updateToggles() {
else if (key == "SteerFriction") { else if (key == "SteerFriction") {
setVisible &= parent->friction != 0; setVisible &= parent->friction != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune; setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= !usingNNFF;
}
else if (key == "SteerOffset") {
setVisible &= parent->isGM;
setVisible &= parent->isTorqueCar || forcingTorqueController; setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= !usingNNFF; setVisible &= !usingNNFF;
} }
@@ -413,14 +431,14 @@ void FrogPilotLateralPanel::updateToggles() {
else if (key == "SteerLatAccel") { else if (key == "SteerLatAccel") {
setVisible &= parent->latAccelFactor != 0; setVisible &= parent->latAccelFactor != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune; setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
setVisible &= parent->isTorqueCar || forcingTorqueController; setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= !usingNNFF; setVisible &= !usingNNFF;
} }
else if (key == "SteerRatio") { else if (key == "SteerRatio") {
setVisible &= parent->steerRatio != 0; setVisible &= parent->steerRatio != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune; setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
} }
toggle->setVisible(setVisible); toggle->setVisible(setVisible);
+3 -1
View File
@@ -31,6 +31,7 @@ private:
float friction; float friction;
float latAccelFactor; float latAccelFactor;
float steerActuatorDelay; float steerActuatorDelay;
float steerOffset;
float steerKp; float steerKp;
float steerRatio; float steerRatio;
@@ -38,7 +39,7 @@ private:
std::map<QString, AbstractControl*> toggles; 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> aolKeys = {"AlwaysOnLateralLKAS", "AlwaysOnLateralMain", "PauseAOLOnBrake"};
QSet<QString> laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"}; QSet<QString> laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"};
QSet<QString> lateralTuneKeys = {"NNFF", "NNFFLite", "TurnDesires"}; QSet<QString> lateralTuneKeys = {"NNFF", "NNFFLite", "TurnDesires"};
@@ -48,6 +49,7 @@ private:
FrogPilotParamValueButtonControl *steerDelayToggle; FrogPilotParamValueButtonControl *steerDelayToggle;
FrogPilotParamValueButtonControl *steerFrictionToggle; FrogPilotParamValueButtonControl *steerFrictionToggle;
FrogPilotParamValueButtonControl *steerOffsetToggle;
FrogPilotParamValueButtonControl *steerLatAccelToggle; FrogPilotParamValueButtonControl *steerLatAccelToggle;
FrogPilotParamValueButtonControl *steerKPToggle; FrogPilotParamValueButtonControl *steerKPToggle;
FrogPilotParamValueButtonControl *steerRatioToggle; FrogPilotParamValueButtonControl *steerRatioToggle;
File diff suppressed because it is too large Load Diff
@@ -40,19 +40,19 @@ private:
std::map<QString, AbstractControl*> toggles; std::map<QString, AbstractControl*> toggles;
QSet<QString> advancedLongitudinalTuneKeys = {"LongitudinalActuatorDelay", "StartAccel", "StopAccel", "StoppingDecelRate", "VEgoStarting", "VEgoStopping"}; QSet<QString> advancedLongitudinalTuneKeys = {"EVTuning", "LongitudinalActuatorDelay", "StartAccel", "StopAccel", "StoppingDecelRate", "VEgoStarting", "VEgoStopping"};
QSet<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"}; QSet<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveFollowHigh", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"};
QSet<QString> conditionalExperimentalKeys = {"CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CENavigation", "CESignalSpeed", "ShowCEMStatus"}; QSet<QString> conditionalExperimentalKeys = {"CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CENavigation", "CESignalSpeed", "ShowCEMStatus"};
QSet<QString> curveSpeedKeys = {"CalibratedLateralAcceleration", "CalibrationProgress", "ResetCurveData", "ShowCSCStatus"}; QSet<QString> curveSpeedKeys = {"CalibratedLateralAcceleration", "CalibrationProgress", "ResetCurveData", "ShowCSCStatus"};
QSet<QString> customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"}; 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> 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> speedLimitControllerKeys = {"SLCOffsets", "SLCFallback", "SLCOverride", "SLCPriority", "SLCQOL", "SLCVisuals"};
QSet<QString> speedLimitControllerOffsetsKeys = {"Offset1", "Offset2", "Offset3", "Offset4", "Offset5", "Offset6", "Offset7"}; QSet<QString> speedLimitControllerOffsetsKeys = {"Offset1", "Offset2", "Offset3", "Offset4", "Offset5", "Offset6", "Offset7"};
QSet<QString> speedLimitControllerQOLKeys = {"ForceMPHDashboard", "SetSpeedLimit", "SLCConfirmation", "SLCLookaheadHigher", "SLCLookaheadLower", "SLCMapboxFiller"}; QSet<QString> speedLimitControllerQOLKeys = {"ForceMPHDashboard", "SetSpeedLimit", "SLCConfirmation", "SLCLookaheadHigher", "SLCLookaheadLower", "SLCMapboxFiller"};
QSet<QString> speedLimitControllerVisualKeys = {"ShowSLCOffset", "SpeedLimitSources"}; 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> trafficPersonalityKeys = {"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", "ResetTrafficPersonality"};
QSet<QString> parentKeys; QSet<QString> parentKeys;
+319 -222
View File
@@ -1,10 +1,13 @@
#include "frogpilot/ui/qt/offroad/model_settings.h" #include "frogpilot/ui/qt/offroad/model_settings.h"
#include "frogpilot/ui/qt/offroad/expandable_multi_option_dialog.h" #include "frogpilot/ui/qt/offroad/expandable_multi_option_dialog.h"
#include <QFile> #include <QFile>
#include <QFileInfo>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QDoubleSpinBox> #include <QDoubleSpinBox>
#include <QPushButton> #include <QPushButton>
#include <QDialog>
#include <algorithm>
FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
QStackedLayout *modelLayout = new QStackedLayout(); QStackedLayout *modelLayout = new QStackedLayout();
@@ -27,12 +30,14 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
{"DeleteModel", tr("Delete Driving Models"), tr("Delete driving models from the device."), ""}, {"DeleteModel", tr("Delete Driving Models"), tr("Delete driving models from the device."), ""},
{"DownloadModel", tr("Download Driving Models"), tr("Download driving models to 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."), ""}, {"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."), ""}, {"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."), ""}, {"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."), ""}, {"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."), ""}, {"SelectModel", tr("Select Driving Model"), tr("Select the active driving model."), ""},
}; };
FrogPilotParamValueButtonControl *recoveryPowerToggle = nullptr;
FrogPilotParamValueButtonControl *stopDistanceToggle = nullptr; FrogPilotParamValueButtonControl *stopDistanceToggle = nullptr;
for (const auto &[param, title, desc, icon] : modelToggles) { for (const auto &[param, title, desc, icon] : modelToggles) {
@@ -41,56 +46,72 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
if (param == "DeleteModel") { if (param == "DeleteModel") {
deleteModelButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DELETE"), tr("DELETE ALL")}); deleteModelButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DELETE"), tr("DELETE ALL")});
QObject::connect(deleteModelButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) { QObject::connect(deleteModelButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
QStringList deletableModels; QMap<QString, QString> deletableModelsMap = getDeletableModelDisplayNames();
for (const QString &file : modelDir.entryList(QDir::Files)) { noModelsDownloaded = deletableModelsMap.isEmpty();
QString base = QFileInfo(file).baseName();
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) { if (noModelsDownloaded) {
if (base.startsWith(modelKey)) { return;
QString modelName = modelFileToNameMapProcessed.value(modelKey);
if (!deletableModels.contains(modelName)) {
deletableModels.append(modelName);
}
}
}
} }
deletableModels.removeAll(processModelName(currentModel));
deletableModels.removeAll(modelFileToNameMapProcessed.value(QString::fromStdString(params_default.get("Model"))));
deletableModels.removeAll("Space Lab");
noModelsDownloaded = deletableModels.isEmpty();
if (id == 0) { if (id == 0) {
// Group deletable models by series // Group deletable models by series and keep a lookup for selected names
QMap<QString, QStringList> deletableSeriesToModels; QMap<QString, QStringList> deletableSeriesToModels;
for (const QString &modelName : deletableModels) { QMap<QString, QString> displayNameToKey;
QString modelKey = modelFileToNameMapProcessed.key(modelName); QMap<QString, QString> deletableFileToNameMap;
QString series = modelSeriesMap.value(modelKey, "Custom Series"); for (auto it = deletableModelsMap.constBegin(); it != deletableModelsMap.constEnd(); ++it) {
deletableSeriesToModels[series].append(modelName); 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 // Sort models within each series
for (QString &series : deletableSeriesToModels.keys()) { for (QString &series : deletableSeriesToModels.keys()) {
deletableSeriesToModels[series].sort(); QStringList &models = deletableSeriesToModels[series];
models.removeDuplicates();
std::sort(models.begin(), models.end());
} }
QString modelToDelete = ExpandableMultiOptionDialog::getSelection(tr("Select a driving model to delete"), deletableSeriesToModels, "", this); QString savedSortMode = QString::fromStdString(params.get("ModelSortMode"));
if (!modelToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the \"%1\" model?").arg(modelToDelete), tr("Delete"), this)) { if (savedSortMode.isEmpty()) savedSortMode = "alphabetical";
QString modelFile = modelFileToNameMapProcessed.key(modelToDelete);
for (const QString &file : modelDir.entryList(QDir::Files)) { QString modelToDelete = ExpandableMultiOptionDialog::getSelection(tr("Select a driving model to delete"), deletableSeriesToModels, "", this,
QString base = QFileInfo(file).baseName(); QStringList(), QStringList(), QMap<QString, QString>(),
if (base.startsWith(modelFile)) { deletableFileToNameMap, savedSortMode);
QFile::remove(modelDir.filePath(file)); 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) { } else if (id == 1) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete all of your downloaded driving models?"), tr("Delete"), this)) { 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)) { for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName(); QString base = QFileInfo(file).baseName();
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) { for (const QString &modelKey : deletableKeys) {
QString modelName = modelFileToNameMapProcessed.value(modelKey); if (base.startsWith(modelKey)) {
if (deletableModels.contains(modelName) && base.startsWith(modelKey)) {
QFile::remove(modelDir.filePath(file)); QFile::remove(modelDir.filePath(file));
break; break;
} }
@@ -99,6 +120,7 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
allModelsDownloaded = false; allModelsDownloaded = false;
noModelsDownloaded = true; noModelsDownloaded = true;
deleteModelButton->setEnabled(false);
} }
} }
}); });
@@ -106,66 +128,70 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
} else if (param == "DownloadModel") { } else if (param == "DownloadModel") {
downloadModelButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DOWNLOAD"), tr("DOWNLOAD ALL")}); downloadModelButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DOWNLOAD"), tr("DOWNLOAD ALL")});
QObject::connect(downloadModelButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) { QObject::connect(downloadModelButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
auto isInstalled = [this](const QString &key) {
bool has_thneed = false;
bool has_policy_meta = false;
bool has_policy_tg = false;
bool has_vision_meta = false;
bool has_vision_tg = 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;
if (ext == "thneed") {
// Classic model (WD-40 etc.)
has_thneed = true;
} else if (ext == "pkl") {
// TinyGrad bundle uses these four exact suffixes
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_vision_metadata")) has_vision_meta = true;
else if (base.contains("_driving_vision_tinygrad")) has_vision_tg = true;
}
}
// Classic models: any matching .thneed counts as installed
if (has_thneed) return true;
// TinyGrad models: require all four policy/vision files to be present
return has_policy_meta && has_policy_tg && has_vision_meta && has_vision_tg;
};
if (id == 0) { if (id == 0) {
if (modelDownloading) { if (modelDownloading) {
params_memory.putBool("CancelModelDownload", true); params_memory.putBool("CancelModelDownload", true);
cancellingDownload = true; cancellingDownload = true;
} else { } else {
QStringList downloadableModels = availableModelNames; QMap<QString, QStringList> downloadableSeriesToModels;
for (const QString &modelKey : modelFileToNameMap.keys()) { QStringList downloadableModelNames;
QString modelName = modelFileToNameMap.value(modelKey);
if (isInstalled(modelKey)) {
downloadableModels.removeAll(modelName);
}
}
downloadableModels.removeAll("Space Lab 👀📡");
allModelsDownloaded = downloadableModels.isEmpty();
// Group downloadable models by series for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
QMap<QString, QStringList> downloadableSeriesToModels; const QString &modelKey = it.key();
for (const QString &modelName : downloadableModels) { const QString &modelName = it.value();
QString modelKey = modelFileToNameMap.key(modelName); if (modelName.isEmpty() || isModelInstalled(modelKey)) {
QString series = modelSeriesMap.value(modelKey, "Custom Series"); continue;
downloadableSeriesToModels[series].append(modelName);
} }
// Sort models within each series QString series = modelSeriesMap.value(modelKey, tr("Custom Series"));
for (QString &series : downloadableSeriesToModels.keys()) { downloadableSeriesToModels[series].append(modelName);
downloadableSeriesToModels[series].sort(); if (!downloadableModelNames.contains(modelName)) {
downloadableModelNames.append(modelName);
} }
}
QString modelToDownload = ExpandableMultiOptionDialog::getSelection(tr("Select a driving model to download"), downloadableSeriesToModels, "", this); 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()) { if (!modelToDownload.isEmpty()) {
QString modelKey = modelFileToNameMap.key(modelToDownload); QString modelKey = modelFileToNameMap.key(modelToDownload);
params_memory.put("ModelToDownload", modelKey.toStdString()); params_memory.put("ModelToDownload", modelKey.toStdString());
@@ -182,15 +208,16 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
} }
} }
} }
params_memory.put("ModelDownloadProgress", "Downloading..."); 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) { } else if (id == 1) {
@@ -308,57 +335,32 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
} else if (param == "SelectModel") { } else if (param == "SelectModel") {
selectModelButton = new ButtonControl(title, tr("SELECT"), desc); selectModelButton = new ButtonControl(title, tr("SELECT"), desc);
QObject::connect(selectModelButton, &ButtonControl::clicked, [this]() { QObject::connect(selectModelButton, &ButtonControl::clicked, [this]() {
auto isInstalled = [this](const QString &key) { // Group models by series for the enhanced dialog
bool has_thneed = false; QMap<QString, QStringList> seriesToModels;
bool has_policy_meta = false; QMap<QString, QString> installedModelFileToNameMap;
bool has_policy_tg = false; QMap<QString, QString> installedReleasedDates;
bool has_vision_meta = false;
bool has_vision_tg = false;
for (const QString &file : modelDir.entryList(QDir::Files)) { // Add all available models by series
QFileInfo fi(modelDir.filePath(file)); for (const QString &modelKey : modelFileToNameMap.keys()) {
const QString base = fi.baseName(); if (!isModelInstalled(modelKey)) {
const QString ext = fi.suffix(); continue;
if (!(base.startsWith(key) || base.startsWith(key + "_"))) continue;
if (ext == "thneed") {
// Classic model (WD-40 etc.)
has_thneed = true;
} else if (ext == "pkl") {
// TinyGrad bundle uses these four exact suffixes
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_vision_metadata")) has_vision_meta = true;
else if (base.contains("_driving_vision_tinygrad")) has_vision_tg = true;
}
} }
// Classic models: any matching .thneed counts as installed
if (has_thneed) return true;
// TinyGrad models: require all four policy/vision files to be present
return has_policy_meta && has_policy_tg && has_vision_meta && has_vision_tg;
};
// Group models by series
QMap<QString, QStringList> seriesToModels;
for (const QString &modelKey : modelFileToNameMap.keys()) {
QString modelName = modelFileToNameMap.value(modelKey); QString modelName = modelFileToNameMap.value(modelKey);
if (modelName.contains("(Default)")) { if (modelName.contains("(Default)")) {
continue; continue;
} }
if (isInstalled(modelKey)) { installedModelFileToNameMap.insert(modelKey, modelName);
QString series = modelSeriesMap.value(modelKey, "Dom Forgot To Label Me"); if (modelReleasedDates.contains(modelKey)) {
seriesToModels[series].append(modelName); installedReleasedDates.insert(modelKey, modelReleasedDates.value(modelKey));
} }
QString series = modelSeriesMap.value(modelKey, "Custom Series");
seriesToModels[series].append(modelName);
} }
// Add Space Lab to Custom Series // Sort models alphabetically within each series
QString spaceLabName = modelFileToNameMap.value("space-lab");
if (isInstalled("space-lab")) {
seriesToModels["Custom Series"].append(spaceLabName);
}
// Sort models within each series
for (QString &series : seriesToModels.keys()) { for (QString &series : seriesToModels.keys()) {
seriesToModels[series].sort(); seriesToModels[series].sort();
} }
@@ -371,54 +373,70 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
seriesToModels[defaultSeries].prepend(defaultModelName); seriesToModels[defaultSeries].prepend(defaultModelName);
} }
QString modelToSelect = ExpandableMultiOptionDialog::getSelection(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"), seriesToModels, currentModel, this); // Prepare favorites and dates for the enhanced dialog
if (!modelToSelect.isEmpty()) { QStringList userFavs = QString::fromStdString(params.get("UserFavorites")).split(",");
currentModel = modelToSelect; userFavs.removeAll("");
params.put("Model", modelFileToNameMap.key(modelToSelect).toStdString()); QStringList communityFavs = QString::fromStdString(params.get("CommunityFavorites")).split(",");
// Sync ModelVersion with the selected model if known communityFavs.removeAll("");
{
QString modelKey = modelFileToNameMap.key(modelToSelect); // Create dialog instance to access sort mode and favorites after selection
QFile vf("/data/models/.model_versions.json"); QString savedSortMode = QString::fromStdString(params.get("ModelSortMode"));
if (vf.open(QIODevice::ReadOnly)) { if (savedSortMode.isEmpty()) savedSortMode = "alphabetical";
auto doc = QJsonDocument::fromJson(vf.readAll());
if (doc.isObject()) { ExpandableMultiOptionDialog dialog(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"),
auto obj = doc.object(); seriesToModels, currentModel, this,
if (obj.contains(modelKey)) { userFavs, communityFavs, installedReleasedDates, installedModelFileToNameMap, savedSortMode);
params.put("ModelVersion", obj.value(modelKey).toString().toStdString());
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(); updateFrogPilotToggles();
if (started) { if (started) {
if (FrogPilotConfirmationDialog::toggleReboot(this)) { if (FrogPilotConfirmationDialog::toggleReboot(this)) {
Hardware::reboot(); Hardware::reboot();
}
}
selectModelButton->setValue(modelToSelect);
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);
}
} }
} }
selectModelButton->setValue(modelToSelect);
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
} }
deletableModels.removeAll(processModelName(currentModel));
deletableModels.removeAll(modelFileToNameMapProcessed.value(QString::fromStdString(params_default.get("Model"))));
noModelsDownloaded = deletableModels.isEmpty();
} }
}); });
modelToggle = selectModelButton; modelToggle = selectModelButton;
} 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") { } else if (param == "StopDistance") {
std::vector<QString> stopDistanceButton{"Reset"}; 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); modelToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 4.0, 10.0, QString(), std::map<float, QString>(), 0.1, false, {}, stopDistanceButton, false, false);
@@ -451,6 +469,16 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
} }
}); });
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) { if (stopDistanceToggle) {
QObject::connect(stopDistanceToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this, 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)) { 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)) {
@@ -465,6 +493,96 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotModelPanel::updateState); 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) { void FrogPilotModelPanel::showEvent(QShowEvent *event) {
FrogPilotUIState &fs = *frogpilotUIState(); FrogPilotUIState &fs = *frogpilotUIState();
UIState &s = *uiState(); UIState &s = *uiState();
@@ -478,6 +596,9 @@ void FrogPilotModelPanel::showEvent(QShowEvent *event) {
QStringList availableModels = QString::fromStdString(params.get("AvailableModels")).split(","); QStringList availableModels = QString::fromStdString(params.get("AvailableModels")).split(",");
availableModelNames = QString::fromStdString(params.get("AvailableModelNames")).split(","); availableModelNames = QString::fromStdString(params.get("AvailableModelNames")).split(",");
availableModelSeries = QString::fromStdString(params.get("AvailableModelSeries")).split(","); 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 // Build a simple model->version map for quick lookups elsewhere
{ {
@@ -497,78 +618,54 @@ void FrogPilotModelPanel::showEvent(QShowEvent *event) {
modelFileToNameMap.clear(); modelFileToNameMap.clear();
modelFileToNameMapProcessed.clear(); modelFileToNameMapProcessed.clear();
modelSeriesMap.clear(); modelSeriesMap.clear();
int size = qMin(qMin(availableModels.size(), availableModelNames.size()), availableModelSeries.size()); modelReleasedDates.clear();
int size = qMin(availableModels.size(), availableModelNames.size());
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
modelFileToNameMap.insert(availableModels[i], availableModelNames[i]); const QString modelKey = availableModels[i].trimmed();
modelFileToNameMapProcessed.insert(availableModels[i], processModelName(availableModelNames[i])); const QString modelName = availableModelNames[i].trimmed();
modelSeriesMap.insert(availableModels[i], availableModelSeries[i]); if (modelKey.isEmpty() || modelName.isEmpty()) {
} continue;
modelFileToNameMap.insert("space-lab", "Space Lab 👀📡");
modelFileToNameMapProcessed.insert("space-lab", "Space Lab");
modelSeriesMap.insert("space-lab", "Dom Forgot To Label Me");
auto isInstalled = [this](const QString &key) {
bool has_thneed = false;
bool has_policy_meta = false;
bool has_policy_tg = false;
bool has_vision_meta = false;
bool has_vision_tg = 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;
if (ext == "thneed") {
// Classic model (WD-40 etc.)
has_thneed = true;
} else if (ext == "pkl") {
// TinyGrad bundle uses these four exact suffixes
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_vision_metadata")) has_vision_meta = true;
else if (base.contains("_driving_vision_tinygrad")) has_vision_tg = true;
}
} }
// Classic models: any matching .thneed counts as installed QString series;
if (has_thneed) return true; if (i < availableModelSeries.size()) {
// TinyGrad models: require all four policy/vision files to be present series = availableModelSeries[i].trimmed();
return has_policy_meta && has_policy_tg && has_vision_meta && has_vision_tg; }
}; if (series.isEmpty()) {
QStringList downloadableModels = availableModelNames; series = tr("Custom Series");
for (const QString &modelKey : modelFileToNameMap.keys()) {
QString modelName = modelFileToNameMap.value(modelKey);
if (isInstalled(modelKey)) {
downloadableModels.removeAll(modelName);
} }
}
allModelsDownloaded = downloadableModels.isEmpty();
QStringList deletableModels; modelFileToNameMap.insert(modelKey, modelName);
for (const QString &file : modelDir.entryList(QDir::Files)) { modelFileToNameMapProcessed.insert(modelKey, processModelName(modelName));
QString base = QFileInfo(file).baseName(); modelSeriesMap.insert(modelKey, series);
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
if (base.startsWith(modelKey)) { if (i < releasedDatesParam.size()) {
QString modelName = modelFileToNameMapProcessed.value(modelKey); const QString released = releasedDatesParam[i].trimmed();
if (!deletableModels.contains(modelName)) { if (!released.isEmpty()) {
deletableModels.append(modelName); this->modelReleasedDates.insert(modelKey, released);
}
} }
} }
} }
deletableModels.removeAll(processModelName(currentModel)); allModelsDownloaded = true;
deletableModels.removeAll(modelFileToNameMapProcessed.value(QString::fromStdString(params_default.get("Model")))); for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
noModelsDownloaded = deletableModels.isEmpty(); if (it.value().isEmpty()) {
continue;
}
if (!isModelInstalled(it.key())) {
allModelsDownloaded = false;
break;
}
}
QString modelKey = QString::fromStdString(params.get("Model")); QString modelKey = QString::fromStdString(params.get("Model"));
if (!isInstalled(modelKey)) { if (!isModelInstalled(modelKey)) {
modelKey = QString::fromStdString(params_default.get("Model")); modelKey = QString::fromStdString(params_default.get("Model"));
} }
currentModel = modelFileToNameMap.value(modelKey); currentModel = modelFileToNameMap.value(modelKey);
selectModelButton->setValue(currentModel); selectModelButton->setValue(currentModel);
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
bool parked = !s.scene.started || fs.frogpilot_scene.parked || fs.frogpilot_toggles.value("frogs_go_moo").toBool(); bool parked = !s.scene.started || fs.frogpilot_scene.parked || fs.frogpilot_toggles.value("frogs_go_moo").toBool();
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded)); deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
@@ -666,10 +763,10 @@ void FrogPilotModelPanel::updateToggles() {
if (key == "ManageBlacklistedModels" || key == "ManageScores") { if (key == "ManageBlacklistedModels" || key == "ManageScores") {
setVisible &= params.getBool("ModelRandomizer"); setVisible &= params.getBool("ModelRandomizer");
} } else if (key == "SelectModel") {
else if (key == "SelectModel") {
setVisible &= !params.getBool("ModelRandomizer"); setVisible &= !params.getBool("ModelRandomizer");
} else if (key == "RecoveryPower") {
setVisible &= (tuningLevel == 3); // Only visible in developer tuning level
} else if (key == "StopDistance") { } else if (key == "StopDistance") {
setVisible &= (tuningLevel == 3); // Only visible in developer tuning level setVisible &= (tuningLevel == 3); // Only visible in developer tuning level
} }
+4
View File
@@ -20,6 +20,8 @@ private:
void updateModelLabels(FrogPilotListWidget *labelsList); void updateModelLabels(FrogPilotListWidget *labelsList);
void updateState(const UIState &s, const FrogPilotUIState &fs); void updateState(const UIState &s, const FrogPilotUIState &fs);
void updateToggles(); void updateToggles();
bool isModelInstalled(const QString &key) const;
QMap<QString, QString> getDeletableModelDisplayNames();
bool allModelsDownloaded; bool allModelsDownloaded;
bool allModelsDownloading; bool allModelsDownloading;
@@ -55,10 +57,12 @@ private:
QMap<QString, QString> modelFileToNameMap; QMap<QString, QString> modelFileToNameMap;
QMap<QString, QString> modelFileToNameMapProcessed; QMap<QString, QString> modelFileToNameMapProcessed;
QMap<QString, QString> modelReleasedDates;
QMap<QString, QString> modelSeriesMap; QMap<QString, QString> modelSeriesMap;
QString currentModel; QString currentModel;
QStringList availableModelNames; QStringList availableModelNames;
QStringList availableModelSeries; QStringList availableModelSeries;
}; };
@@ -168,6 +168,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
std::vector<std::tuple<QString, QString, QString, QString>> vehicleToggles { std::vector<std::tuple<QString, QString, QString, QString>> vehicleToggles {
{"GMToggles", tr("General Motors Settings"), tr("<b>FrogPilot features for General Motors vehicles.</b>"), ""}, {"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!"), ""}, {"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."), ""}, {"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."), ""}, {"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>"), ""}, {"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>"), ""}, {"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?"), ""}, {"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>"), ""}, {"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>"), ""} {"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; QStringList detected;
if (hasPedal) detected << "comma Pedal"; if (hasPedal) detected << "comma Pedal";
if (parent->hasSASCM) detected << "SASCM";
if (parent->hasSDSU) detected << "SDSU"; if (parent->hasSDSU) detected << "SDSU";
if (parent->hasZSS) detected << "ZSS"; if (parent->hasZSS) detected << "ZSS";
static_cast<LabelControl*>(toggles["HardwareDetected"])->setText(detected.isEmpty() ? tr("None") : detected.join(", ")); 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["OpenpilotLongitudinal"])->setText(hasOpenpilotLongitudinal ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["PedalSupport"])->setText(parent->canUsePedal ? 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["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["SDSUSupport"])->setText(parent->canUseSDSU ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["SNGSupport"])->setText(hasSNG ? tr("Yes") : tr("No")); static_cast<LabelControl*>(toggles["SNGSupport"])->setText(hasSNG ? tr("Yes") : tr("No"));
@@ -403,6 +407,10 @@ void FrogPilotVehiclesPanel::updateToggles() {
setVisible &= isHKGCanFd; setVisible &= isHKGCanFd;
} }
else if (key == "GMPedalLongitudinal") {
setVisible &= hasPedal;
}
else if (key == "VoltSNG") { else if (key == "VoltSNG") {
setVisible &= isVolt && !hasSNG; setVisible &= isVolt && !hasSNG;
} }
+2 -2
View File
@@ -36,11 +36,11 @@ private:
std::map<QString, AbstractControl*> toggles; 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> hkgKeys = {"NewLongAPI", "TacoTuneHacks"};
QSet<QString> longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "SNGHack", "VoltSNG"}; QSet<QString> longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "SNGHack", "VoltSNG"};
QSet<QString> toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"}; 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; QSet<QString> parentKeys;
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+30 -4
View File
@@ -10,14 +10,14 @@ const SteeringLimits GM_STEERING_LIMITS = {
}; };
const LongitudinalLimits GM_ASCM_LONG_LIMITS = { const LongitudinalLimits GM_ASCM_LONG_LIMITS = {
.max_gas = 7168, .max_gas = 8191,
.min_gas = 5500, .min_gas = 5500,
.inactive_gas = 5500, .inactive_gas = 5500,
.max_brake = 400, .max_brake = 400,
}; };
const LongitudinalLimits GM_CAM_LONG_LIMITS = { const LongitudinalLimits GM_CAM_LONG_LIMITS = {
.max_gas = 7496, .max_gas = 8848,
.min_gas = 5610, .min_gas = 5610,
.inactive_gas = 5650, .inactive_gas = 5650,
.max_brake = 400, .max_brake = 400,
@@ -172,6 +172,13 @@ static void gm_rx_hook(const CANPacket_t *to_push) {
} }
} }
// Cruise check for ACC models with pedal interceptor - block stock ACC
if ((addr == 0x1C4) && gm_has_acc && enable_gas_interceptor) {
// When pedal interceptor is active on ACC models, ignore stock cruise state
// to prevent conflicts between pedal interceptor and stock ACC
cruise_engaged_prev = false;
}
if (addr == 0xBD) { if (addr == 0xBD) {
regen_braking = (GET_BYTE(to_push, 0) >> 4) != 0U; regen_braking = (GET_BYTE(to_push, 0) >> 4) != 0U;
} }
@@ -192,6 +199,12 @@ static void gm_rx_hook(const CANPacket_t *to_push) {
} }
generic_rx_checks(stock_ecu_detected); generic_rx_checks(stock_ecu_detected);
} }
// Cruise check for Gen2 Bolt (ASCMActiveCruiseControlStatus on bus 2)
int addr = GET_ADDR(to_push);
if ((addr == 0x370) && (GET_BUS(to_push) == 2U)) {
bool cruise_engaged = (GET_BYTE(to_push, 2) >> 7) != 0U; // ACCCmdActive
cruise_engaged_prev = cruise_engaged;
}
} }
static bool gm_tx_hook(const CANPacket_t *to_send) { static bool gm_tx_hook(const CANPacket_t *to_send) {
@@ -246,6 +259,11 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U; int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U;
bool allowed_btn = (button == GM_BTN_CANCEL) && cruise_engaged_prev; bool allowed_btn = (button == GM_BTN_CANCEL) && cruise_engaged_prev;
// For ACC cars with pedal interceptor, allow cancel even if cruise_engaged_prev is false
// (since we set it to false to prevent conflicts, but still need to cancel cruise)
if (gm_hw == GM_CAM && enable_gas_interceptor && button == GM_BTN_CANCEL) {
allowed_btn = true;
}
// For standard CC, allow spamming of SET / RESUME // For standard CC, allow spamming of SET / RESUME
if (gm_cc_long) { if (gm_cc_long) {
allowed_btn |= cruise_engaged_prev && (button == GM_BTN_SET || button == GM_BTN_RESUME || button == GM_BTN_UNPRESS); allowed_btn |= cruise_engaged_prev && (button == GM_BTN_SET || button == GM_BTN_RESUME || button == GM_BTN_UNPRESS);
@@ -288,9 +306,13 @@ static int gm_fwd_hook(int bus_num, int addr) {
} }
if (bus_num == 2) { if (bus_num == 2) {
// block lkas message and acc messages if gm_cam_long, forward all others // block lkas message and acc messages
// Block 0x370 only for experimental long without pedal interceptor
bool is_lkas_msg = (addr == 0x180); bool is_lkas_msg = (addr == 0x180);
bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370); bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB);
if (gm_cam_long && !enable_gas_interceptor) {
is_acc_msg = is_acc_msg || (addr == 0x370);
}
bool block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long); bool block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long);
if (!block_msg) { if (!block_msg) {
bus_fwd = 0; bus_fwd = 0;
@@ -322,6 +344,10 @@ static safety_config gm_init(uint16_t param) {
gm_pedal_long = GET_FLAG(param, GM_PARAM_PEDAL_LONG); gm_pedal_long = GET_FLAG(param, GM_PARAM_PEDAL_LONG);
gm_cc_long = GET_FLAG(param, GM_PARAM_CC_LONG); gm_cc_long = GET_FLAG(param, GM_PARAM_CC_LONG);
gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG) && !gm_cc_long; gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG) && !gm_cc_long;
// Block ACC messages when pedal interceptor is active on ACC models
if (gm_hw == GM_CAM && enable_gas_interceptor) {
gm_cam_long = true;
}
gm_pcm_cruise = ((gm_hw == GM_CAM) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long) || (gm_hw == GM_SDGM); gm_pcm_cruise = ((gm_hw == GM_CAM) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long) || (gm_hw == GM_SDGM);
gm_skip_relay_check = GET_FLAG(param, GM_PARAM_NO_CAMERA); gm_skip_relay_check = GET_FLAG(param, GM_PARAM_NO_CAMERA);
gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC); gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC);
+19 -15
View File
@@ -804,7 +804,8 @@ class Panda:
# The panda will NAK CAN writes when there is CAN congestion. # The panda will NAK CAN writes when there is CAN congestion.
# libusb will try to send it again, with a max timeout. # libusb will try to send it again, with a max timeout.
# Timeout is in ms. If set to 0, the timeout is infinite. # 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): def can_reset_communications(self):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xc0, 0, 0, b'') self._handle.controlWrite(Panda.REQUEST_OUT, 0xc0, 0, 0, b'')
@@ -812,18 +813,18 @@ class Panda:
@ensure_can_packet_version @ensure_can_packet_version
def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS): def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS):
snds = pack_can_buffer(arr) snds = pack_can_buffer(arr)
while True: for tx in snds:
try: retries = 0
for tx in snds: while len(tx) > 0:
while True: bs = self._handle.bulkWrite(3, tx, timeout=timeout)
bs = self._handle.bulkWrite(3, tx, timeout=timeout) if bs == 0:
tx = tx[bs:] retries += 1
if len(tx) == 0: if retries > self.CAN_MAX_RETRIES:
break logging.warning("CAN send: no progress after retries, dropping")
logging.error("CAN: PARTIAL SEND MANY, RETRYING") break
break else:
except (usb1.USBErrorIO, usb1.USBErrorOverflow): retries = 0
logging.error("CAN: BAD SEND MANY, RETRYING") tx = tx[bs:]
def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS): def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS):
self.can_send_many([[addr, None, dat, bus]], timeout=timeout) self.can_send_many([[addr, None, dat, bus]], timeout=timeout)
@@ -831,13 +832,16 @@ class Panda:
@ensure_can_packet_version @ensure_can_packet_version
def can_recv(self): def can_recv(self):
dat = bytearray() dat = bytearray()
while True: for _ in range(self.CAN_MAX_RETRIES):
try: try:
dat = self._handle.bulkRead(1, 16384) # Max receive batch size + 2 extra reserve frames dat = self._handle.bulkRead(1, 16384) # Max receive batch size + 2 extra reserve frames
break break
except (usb1.USBErrorIO, usb1.USBErrorOverflow): except (usb1.USBErrorIO, usb1.USBErrorOverflow):
logging.error("CAN: BAD RECV, RETRYING") 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) msgs, self.can_rx_overflow_buffer = unpack_can_buffer(self.can_rx_overflow_buffer + dat)
return msgs return msgs
+12 -1
View File
@@ -27,7 +27,10 @@ NACK = 0x1F
CHECKSUM_START = 0xAB CHECKSUM_START = 0xAB
MIN_ACK_TIMEOUT_MS = 100 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_XFER_RETRY_COUNT = 5
MAX_TIMEOUT_RETRIES = 5 # like C++
XFER_SIZE = 0x40*31 XFER_SIZE = 0x40*31
@@ -152,6 +155,8 @@ class PandaSpiHandle(BaseHandle):
return cksum return cksum
def _wait_for_ack(self, spi, ack_val: int, timeout: int, tx: int, length: int = 1) -> bytes: 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 timeout_s = max(MIN_ACK_TIMEOUT_MS, timeout) * 1e-3
start = time.monotonic() 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("starting transfer: endpoint=%d, max_rx_len=%d", endpoint, max_rx_len)
logging.debug("==============================================") logging.debug("==============================================")
# Fix timeout=0 infinite loop: default to DEFAULT_TIMEOUT_MS
if timeout == 0:
timeout = DEFAULT_TIMEOUT_MS
n = 0 n = 0
start_time = time.monotonic() start_time = time.monotonic()
exc = PandaSpiException() 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 n += 1
logging.debug("\ntry #%d", n) logging.debug("\ntry #%d", n)
with self.dev.acquire() as spi: with self.dev.acquire() as spi:
@@ -238,6 +248,7 @@ class PandaSpiHandle(BaseHandle):
exc = e exc = e
logging.debug("SPI transfer failed, retrying", exc_info=True) 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 raise exc
def get_protocol_version(self) -> bytes: def get_protocol_version(self) -> bytes:
+24 -6
View File
@@ -85,6 +85,7 @@ class CarController(CarControllerBase):
self.regen_paddle_pressed = False self.regen_paddle_pressed = False
self.aego = 0.0 self.aego = 0.0
self.regen_paddle_timer = 0 self.regen_paddle_timer = 0
self.planner_regen_hold = False
@@ -96,6 +97,7 @@ class CarController(CarControllerBase):
def calc_pedal_command(self, accel: float, long_active: bool, car_velocity) -> Tuple[float, bool]: def calc_pedal_command(self, accel: float, long_active: bool, car_velocity) -> Tuple[float, bool]:
if not long_active: if not long_active:
self.planner_regen_hold = False
return 0., False return 0., False
# Regen paddle hysteresis (frame-based): hold 10 frames, with decrement dead-zone # Regen paddle hysteresis (frame-based): hold 10 frames, with decrement dead-zone
@@ -111,7 +113,7 @@ class CarController(CarControllerBase):
# Base paddle press hysteresis # Base paddle press hysteresis
self.regen_paddle_pressed = self.regen_paddle_timer >= 10 # 10 frames self.regen_paddle_pressed = self.regen_paddle_timer >= 10 # 10 frames
press_regen_paddle = self.regen_paddle_pressed press_regen_paddle = self.regen_paddle_pressed or self.planner_regen_hold
# Regen gain ratios from bin-averaged 600 deceleration sweep; Calculates stronger decel from paddle # Regen gain ratios from bin-averaged 600 deceleration sweep; Calculates stronger decel from paddle
@@ -144,6 +146,21 @@ class CarController(CarControllerBase):
actuators = CC.actuators actuators = CC.actuators
accel = brake_accel = actuators.accel accel = brake_accel = actuators.accel
press_regen_paddle = False press_regen_paddle = False
# Planner-driven regen hold: gate by car support and OP long active, use commanded accel thresholds
if (self.CP.enableGasInterceptor and self.CP.carFingerprint in CC_REGEN_PADDLE_CAR
and self.CP.openpilotLongitudinalControl and CC.longActive):
# Match original hysteresis intent: vehicle can usually stop without paddle up to ~1.0 m/s^2
# Use the same thresholds as the aEgo-based hysteresis, but on commanded accel for preemption
planner_press_threshold = -0.7
planner_release_threshold = -0.3
if accel <= planner_press_threshold:
self.planner_regen_hold = True
elif accel >= planner_release_threshold:
self.planner_regen_hold = False
else:
self.planner_regen_hold = False
hud_control = CC.hudControl hud_control = CC.hudControl
hud_alert = hud_control.visualAlert hud_alert = hud_control.visualAlert
hud_v_cruise = hud_control.setSpeed hud_v_cruise = hud_control.setSpeed
@@ -159,7 +176,7 @@ class CarController(CarControllerBase):
self.CP.openpilotLongitudinalControl and self.CP.openpilotLongitudinalControl and
CC.longActive and CC.longActive and
self.CP.enableGasInterceptor and self.CP.enableGasInterceptor and
self.regen_paddle_timer >= 10 # raw hysteresis-only (10 frames) (self.regen_paddle_timer >= 10 or self.planner_regen_hold) # hysteresis or planner hint
) )
regen_active = raw_regen_active regen_active = raw_regen_active
@@ -326,7 +343,7 @@ class CarController(CarControllerBase):
# Update regen_active state and last_regen_paddle_pressed for next loop # Update regen_active state and last_regen_paddle_pressed for next loop
self.last_regen_active = regen_active self.last_regen_active = regen_active
self.last_regen_paddle_pressed = self.regen_paddle_pressed self.last_regen_paddle_pressed = self.regen_paddle_pressed or self.planner_regen_hold
if paddle_sends: if paddle_sends:
interval_ns = self.last_steer_ts_ns - self.prev_steer_ts_ns interval_ns = self.last_steer_ts_ns - self.prev_steer_ts_ns
@@ -390,10 +407,11 @@ class CarController(CarControllerBase):
idx = (self.frame // 4) % 4 idx = (self.frame // 4) % 4
if self.CP.flags & GMFlags.CC_LONG.value: 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 # Using extend instead of append since the message is only sent intermittently
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, frogpilot_toggles)) can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, frogpilot_toggles))
elif CC.enabled and self.frame % 52 == 0 and CS.cruise_buttons == CruiseButtons.UNPRESS and CS.out.gasPressed and CS.out.cruiseState.speed < CS.out.vEgo < hud_v_cruise: elif (CS.out.cruiseState.enabled and CC.enabled and self.frame % 52 == 0 and
CS.cruise_buttons == CruiseButtons.UNPRESS and CS.out.gasPressed and CS.out.cruiseState.speed < CS.out.vEgo < hud_v_cruise):
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET)) can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
if self.CP.enableGasInterceptor: if self.CP.enableGasInterceptor:
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx)) can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
@@ -449,7 +467,7 @@ class CarController(CarControllerBase):
) and CS.out.cruiseState.enabled: ) and CS.out.cruiseState.enabled:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04: if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
self.last_button_frame = self.frame self.last_button_frame = self.frame
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL)) can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
else: else:
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status. # While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
+3 -3
View File
@@ -163,11 +163,11 @@ class CarState(CarStateBase):
if self.CP.carFingerprint in CC_ONLY_CAR: if self.CP.carFingerprint in CC_ONLY_CAR:
ret.accFaulted = False ret.accFaulted = False
ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS
# Try ECM first for cars that might have it (like most GMs), fall back to ASCM # Try ASCM first for cars that might have it (like misfingerprinted Bolts), fall back to ECM
try: try:
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
except:
ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0 ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0
except:
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
if self.CP.enableBsm: if self.CP.enableBsm:
if self.CP.carFingerprint not in SDGM_CAR: if self.CP.carFingerprint not in SDGM_CAR:
+51 -13
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 import create_button_events, get_safety_config
from openpilot.selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG 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.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 from openpilot.selfdrive.controls.lib.drive_helpers import get_friction
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
@@ -27,14 +27,30 @@ CAM_MSG = 0x320 # AEBCmd
ACCELERATOR_POS_MSG = 0xbe ACCELERATOR_POS_MSG = 0xbe
NON_LINEAR_TORQUE_PARAMS = { NON_LINEAR_TORQUE_PARAMS = {
CAR.CHEVROLET_BOLT_EUV: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178], CAR.CHEVROLET_BOLT_EUV: {
CAR.CHEVROLET_BOLT_CC: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178], "left": [2.6531724862969748, 1.1, 0.1719764879840985, 0.0],
CAR.GMC_ACADIA: [4.78003305, 1.0, 0.3122, 0.05591772], "right": [2.8531724862969748, 1.0, 0.1469764879840985, 0.0],
CAR.CHEVROLET_SILVERADO: [3.29974374, 1.0, 0.25571356, 0.0465122] },
CAR.CHEVROLET_BOLT_CC: {
"left": [2.6531724862969748, 1.1, 0.1719764879840985, 0.0],
"right": [2.8531724862969748, 1.0, 0.1469764879840985, 0.0],
},
CAR.GMC_ACADIA: {
"left": [4.78003305, 1.0, 0.3122, 0.05591772],
"right": [4.78003305, 1.0, 0.3122, 0.05591772],
},
CAR.CHEVROLET_SILVERADO: {
"left": [3.29974374, 1.0, 0.25571356, 0.0465122],
"right": [3.29974374, 1.0, 0.25571356, 0.0465122],
},
} }
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
def __init__(self, CP, FPCP, CarController, CarState):
super().__init__(CP, FPCP, CarController, CarState)
self.steer_offset = 0.0
@staticmethod @staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed): def get_pid_accel_limits(CP, current_speed, cruise_speed):
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
@@ -60,7 +76,9 @@ class CarInterface(CarInterfaceBase):
# This has big effect on the stability about 0 (noise when going straight) # This has big effect on the stability about 0 (noise when going straight)
non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint) non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint)
assert non_linear_torque_params, "The params are not defined" assert non_linear_torque_params, "The params are not defined"
a, b, c, d = non_linear_torque_params # Left is positive
side_key = "left" if lateral_acceleration >= 0 else "right"
a, b, c, d = non_linear_torque_params[side_key]
sig_input = a * lateral_acceleration sig_input = a * lateral_acceleration
sig = np.sign(sig_input) * (1 / (1 + exp(-fabs(sig_input))) - 0.5) sig = np.sign(sig_input) * (1 / (1 + exp(-fabs(sig_input))) - 0.5)
steer_torque = (sig * b) + (lateral_acceleration * c) + d steer_torque = (sig * b) + (lateral_acceleration * c) + d
@@ -76,20 +94,28 @@ class CarInterface(CarInterfaceBase):
torque_values, lataccel_values = self.get_lataccel_torque_siglin() torque_values, lataccel_values = self.get_lataccel_torque_siglin()
def torque_from_lateral_accel_siglin(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning): 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 return torque_from_lateral_accel_siglin
else: 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: def lateral_accel_from_torque(self) -> LateralAccelFromTorqueCallbackType:
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS: if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
torque_values, lataccel_values = self.get_lataccel_torque_siglin() torque_values, lataccel_values = self.get_lataccel_torque_siglin()
def lateral_accel_from_torque_siglin(torque: float, torque_params: car.CarParams.LateralTorqueTuning): 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 return lateral_accel_from_torque_siglin
else: 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 @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles):
@@ -97,6 +123,11 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)]
ret.autoResumeSng = False ret.autoResumeSng = False
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN] 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]: if PEDAL_MSG in fingerprint[0]:
ret.enableGasInterceptor = True ret.enableGasInterceptor = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR
@@ -210,6 +241,12 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# Bolt-only lateral tuning overrides
ret.lateralTuning.torque.kp = 1.03
ret.lateralTuning.torque.ki = 1.07
ret.lateralTuning.torque.kd = 0.93
ret.lateralTuning.torque.kfDEPRECATED = 0.02
if ret.enableGasInterceptor: if ret.enableGasInterceptor:
# ACC Bolts use pedal for full longitudinal control, not just sng # ACC Bolts use pedal for full longitudinal control, not just sng
ret.flags |= GMFlags.PEDAL_LONG.value ret.flags |= GMFlags.PEDAL_LONG.value
@@ -265,7 +302,7 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.CHEVROLET_TRAX: elif candidate == CAR.CHEVROLET_TRAX:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if ret.enableGasInterceptor: if ret.enableGasInterceptor and frogpilot_toggles.gm_pedal_longitudinal:
ret.networkLocation = NetworkLocation.fwdCamera ret.networkLocation = NetworkLocation.fwdCamera
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
ret.minEnableSpeed = -1 ret.minEnableSpeed = -1
@@ -354,8 +391,9 @@ class CarInterface(CarInterfaceBase):
if ret.vEgo < self.CP.minSteerSpeed: if ret.vEgo < self.CP.minSteerSpeed:
events.add(EventName.belowSteerSpeed) events.add(EventName.belowSteerSpeed)
if (self.CP.flags & GMFlags.CC_LONG.value) and ret.vEgo < self.CP.minEnableSpeed and ret.cruiseState.enabled: if (self.CP.flags & GMFlags.CC_LONG.value) and ret.vEgo < self.CP.minEnableSpeed:
events.add(EventName.speedTooLow) if ret.cruiseState.enabled or self.CS.out.cruiseState.enabled:
events.add(EventName.speedTooLow)
if (self.CP.flags & GMFlags.PEDAL_LONG.value) and \ if (self.CP.flags & GMFlags.PEDAL_LONG.value) and \
self.CP.transmissionType == TransmissionType.direct and \ self.CP.transmissionType == TransmissionType.direct and \
+11 -9
View File
@@ -42,8 +42,8 @@ class CarControllerParams:
self.MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen self.MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen
if CP.carFingerprint in CAMERA_ACC_CAR and CP.carFingerprint not in CC_ONLY_CAR: if CP.carFingerprint in CAMERA_ACC_CAR and CP.carFingerprint not in CC_ONLY_CAR:
self.MAX_GAS = 7496 self.MAX_GAS = 8848
self.MAX_GAS_PLUS = 7496 self.MAX_GAS_PLUS = 8848
self.MAX_ACC_REGEN = 5610 self.MAX_ACC_REGEN = 5610
self.INACTIVE_REGEN = 5650 self.INACTIVE_REGEN = 5650
# Camera ACC vehicles have no regen while enabled. # Camera ACC vehicles have no regen while enabled.
@@ -52,17 +52,17 @@ class CarControllerParams:
self.BRAKE_SWITCH_MAX = self.MAX_ACC_REGEN if CP.carFingerprint in EV_CAR else self.ZERO_GAS self.BRAKE_SWITCH_MAX = self.MAX_ACC_REGEN if CP.carFingerprint in EV_CAR else self.ZERO_GAS
elif CP.carFingerprint in SDGM_CAR: elif CP.carFingerprint in SDGM_CAR:
self.MAX_GAS = 7168 self.MAX_GAS = 7496
self.MAX_GAS_PLUS = 7168 self.MAX_GAS_PLUS = 7496
self.MAX_ACC_REGEN = 5500 self.MAX_ACC_REGEN = 5610
self.INACTIVE_REGEN = 5500 self.INACTIVE_REGEN = 5650
max_regen_acceleration = 0. max_regen_acceleration = 0.
self.BRAKE_SWITCH = self.ZERO_GAS self.BRAKE_SWITCH = self.ZERO_GAS
else: else:
self.MAX_GAS = 7168 # Safety limit, not ACC max. Stock ACC >8192 from standstill. self.MAX_GAS = 8191 # Safety limit, not ACC max. Stock ACC >8192 from standstill.
self.MAX_GAS_PLUS = 7168 # 8292 uses new bit, possible but not tested. Matches Twilsonco tw-main max self.MAX_GAS_PLUS = 8191 # 8292 uses new bit, possible but not tested. Matches Twilsonco tw-main max
self.MAX_ACC_REGEN = 5500 # Max ACC regen is slightly less than max paddle regen self.MAX_ACC_REGEN = 7110 # Max ACC regen is slightly less than max paddle regen
self.INACTIVE_REGEN = 5500 self.INACTIVE_REGEN = 5500
# ICE has much less engine braking force compared to regen in EVs, # ICE has much less engine braking force compared to regen in EVs,
# lower threshold removes some braking deadzone # lower threshold removes some braking deadzone
@@ -275,6 +275,8 @@ class GMFlags(IntFlag):
CC_LONG = 2 CC_LONG = 2
NO_CAMERA = 4 NO_CAMERA = 4
NO_ACCELERATOR_POS_MSG = 8 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, # In a Data Module, an identifier is a string used to recognize an object,
+14 -1
View File
@@ -37,7 +37,12 @@ EventName = car.CarEvent.EventName
MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS
ACCEL_MAX = 2.0 ACCEL_MAX = 2.0
ACCEL_MIN = -3.5 ACCEL_MIN = -3.5
FRICTION_THRESHOLD = 0.3 FRICTION_THRESHOLD = 0.12
def get_friction_threshold(v_ego):
# Interpolate friction threshold
from openpilot.common.numpy_fast import interp
return interp(v_ego, [1 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.12, 0.3])
TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.toml') TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.toml')
TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.toml') TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.toml')
@@ -147,8 +152,11 @@ class CarInterfaceBase(ABC):
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles) 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 # Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
if not ret.notCar: if not ret.notCar:
ret.mass = ret.mass + trailer_load_kg
ret.mass = ret.mass + STD_CARGO_KG ret.mass = ret.mass + STD_CARGO_KG
# Set params dependent on values set by the car interface # Set params dependent on values set by the car interface
@@ -179,6 +187,7 @@ class CarInterfaceBase(ABC):
elif platform in GMCAR: elif platform in GMCAR:
fp_ret.canUsePedal = True fp_ret.canUsePedal = True
fp_ret.canUseSASCM = True
elif platform in HondaCAR: elif platform in HondaCAR:
if candidate == HondaCAR.HONDA_CLARITY: if candidate == HondaCAR.HONDA_CLARITY:
@@ -283,6 +292,7 @@ class CarInterfaceBase(ABC):
ret.vEgoStopping = 0.5 ret.vEgoStopping = 0.5
ret.vEgoStarting = 0.5 ret.vEgoStarting = 0.5
ret.stoppingControl = True ret.stoppingControl = True
ret.longitudinalTuning.kfDEPRECATED = 1.
ret.longitudinalTuning.kpBP = [0.] ret.longitudinalTuning.kpBP = [0.]
ret.longitudinalTuning.kpV = [0.] ret.longitudinalTuning.kpV = [0.]
ret.longitudinalTuning.kiBP = [0.] ret.longitudinalTuning.kiBP = [0.]
@@ -298,6 +308,9 @@ class CarInterfaceBase(ABC):
tune.init('torque') tune.init('torque')
tune.torque.useSteeringAngle = use_steering_angle tune.torque.useSteeringAngle = use_steering_angle
tune.torque.kp = 0.6
tune.torque.ki = 0.3
tune.torque.kd = 0.0
tune.torque.friction = params['FRICTION'] tune.torque.friction = params['FRICTION']
tune.torque.latAccelFactor = params['LAT_ACCEL_FACTOR'] tune.torque.latAccelFactor = params['LAT_ACCEL_FACTOR']
tune.torque.latAccelOffset = 0.0 tune.torque.latAccelOffset = 0.0
+1 -1
View File
@@ -43,7 +43,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"CADILLAC_ESCALADE" = [1.899999976158142, 1.842270016670227, 0.1120000034570694] "CADILLAC_ESCALADE" = [1.899999976158142, 1.842270016670227, 0.1120000034570694]
"CADILLAC_ESCALADE_ESV_2019" = [1.15, 1.3, 0.2] "CADILLAC_ESCALADE_ESV_2019" = [1.15, 1.3, 0.2]
"CADILLAC_XT4" = [1.45, 1.6, 0.2] "CADILLAC_XT4" = [1.45, 1.6, 0.2]
"CHEVROLET_BOLT_EUV" = [2.0, 2.0, 0.05] "CHEVROLET_BOLT_EUV" = [2.0, 2.0, 0.09]
"CHEVROLET_MALIBU_CC" = [1.85, 1.85, 0.075] "CHEVROLET_MALIBU_CC" = [1.85, 1.85, 0.075]
"CHEVROLET_SILVERADO" = [1.9, 1.9, 0.112] "CHEVROLET_SILVERADO" = [1.9, 1.9, 0.112]
"CHEVROLET_TRAILBLAZER" = [1.33, 1.9, 0.16] "CHEVROLET_TRAILBLAZER" = [1.33, 1.9, 0.16]
+27 -5
View File
@@ -199,7 +199,7 @@ class Controls:
self.event_names_to_clear = set() self.event_names_to_clear = set()
self.has_menu = self.CP.carName == "gm" and not (self.CP.flags & GMFlags.NO_CAMERA.value or self.CP.carFingerprint in CC_ONLY_CAR) self.has_menu = self.CP.carName == "gm" and not (self.CP.flags & GMFlags.NO_CAMERA.value)
self.frogpilot_AM = AlertManager() self.frogpilot_AM = AlertManager()
self.frogpilot_events = Events(frogpilot=True) self.frogpilot_events = Events(frogpilot=True)
@@ -623,9 +623,31 @@ class Controls:
# Update Torque Params # Update Torque Params
if self.CP.lateralTuning.which() == 'torque': if self.CP.lateralTuning.which() == 'torque':
torque_params = self.sm['liveTorqueParameters'] 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, allow_lat_accel_learning = self.CP.carName in ['toyota', 'hyundai']
torque_params.frictionCoefficientFiltered) 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"): if self.sm.updated['liveDelay'] and hasattr(self.LaC, "update_live_delay"):
self.LaC.update_live_delay(self.sm['liveDelay'].lateralDelay) self.LaC.update_live_delay(self.sm['liveDelay'].lateralDelay)
@@ -773,7 +795,7 @@ class Controls:
if self.frogpilot_toggles.conditional_experimental_mode or self.frogpilot_toggles.slc_fallback_experimental_mode: if self.frogpilot_toggles.conditional_experimental_mode or self.frogpilot_toggles.slc_fallback_experimental_mode:
self.experimental_mode = self.sm['frogpilotPlan'].experimentalMode 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 self.LaC.pid._k_p = self.frogpilot_toggles.steerKp
# Update FrogPilot variables # Update FrogPilot variables
+1 -1
View File
@@ -982,7 +982,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
"", "",
AlertStatus.normal, AlertSize.full, AlertStatus.normal, AlertSize.full,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5), Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5),
ET.USER_DISABLE: ImmediateDisableAlert("Reverse Gear"), ET.USER_DISABLE: ImmediateDisableAlert("Wrong Gear"),
ET.NO_ENTRY: NoEntryAlert("Reverse Gear"), ET.NO_ENTRY: NoEntryAlert("Reverse Gear"),
}, },
+13 -2
View File
@@ -1,18 +1,21 @@
import math import math
from cereal import log 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 from openpilot.selfdrive.controls.lib.pid import PIDController
class LatControlPID(LatControl): class LatControlPID(LatControl):
def __init__(self, CP, CI, dt): def __init__(self, CP, CI, dt):
super().__init__(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), self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
(CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV), (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
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.ff_factor = CP.lateralTuning.pid.kf
self.get_steer_feedforward = CI.get_steer_feedforward_function() 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, lat_delay, 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 = log.ControlsState.LateralPIDState.new_message()
@@ -28,11 +31,17 @@ class LatControlPID(LatControl):
if not active: if not active:
output_torque = 0.0 output_torque = 0.0
pid_log.active = False pid_log.active = False
self.pid.reset()
else: 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 # offset does not contribute to resistive torque
ff = self.ff_factor * self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo) ff = self.ff_factor * self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo)
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 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, output_torque = self.pid.update(error,
feedforward=ff, feedforward=ff,
@@ -46,4 +55,6 @@ class LatControlPID(LatControl):
pid_log.output = float(output_torque) 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)) 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 return output_torque, angle_steers_des, pid_log
+90 -22
View File
@@ -3,10 +3,11 @@ import numpy as np
from collections import deque from collections import deque
from cereal import log from cereal import log
from openpilot.common.filter_simple import FirstOrderFilter from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD, get_friction_threshold
from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD
from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED, get_friction from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED, get_friction
from openpilot.selfdrive.controls.lib.latcontrol import LatControl from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
from openpilot.selfdrive.car.gm.values import CAR as GM_CAR
from openpilot.selfdrive.controls.lib.pid import PIDController from openpilot.selfdrive.controls.lib.pid import PIDController
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
@@ -21,29 +22,62 @@ from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_G
# Additionally, there is friction in the steering wheel that needs # Additionally, there is friction in the steering wheel that needs
# to be overcome to move it at all, this is compensated for too. # to be overcome to move it at all, this is compensated for too.
KP = 0.6 KP = 0.7
KI = 0.3 KI = 0.35
KD = 0.0
INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30] INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30]
KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP] KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP]
LOW_SPEED_X = [0, 10, 20, 30]
LOW_SPEED_Y = [12, 10.5, 8, 5]
MAX_LAT_JERK_UP = 2.5 # m/s^3
LP_FILTER_CUTOFF_HZ = 1.2 LP_FILTER_CUTOFF_HZ = 1.2
JERK_LOOKAHEAD_SECONDS = 0.19
JERK_GAIN = 0.22
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0 LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
VERSION = 0 VERSION = 2
DEBUG_TORQUE_TUNE = False
FF_SCALE_BLEND_LAT_ACCEL = 0.05
DEADZONE_BOOST_LAT_ACCEL = 0.08
UNWIND_D_DES_THRESHOLD = -1.0
UNWIND_LAT_ACCEL_NEAR_ZERO = 0.3
BOLT_CARS = (GM_CAR.CHEVROLET_BOLT_EUV, GM_CAR.CHEVROLET_BOLT_CC)
class LatControlTorque(LatControl): class LatControlTorque(LatControl):
def __init__(self, CP, CI, dt): def __init__(self, CP, CI, dt):
super().__init__(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_params = CP.lateralTuning.torque
self.torque_from_lateral_accel = CI.torque_from_lateral_accel() self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
self.lateral_accel_from_torque = CI.lateral_accel_from_torque() self.lateral_accel_from_torque = CI.lateral_accel_from_torque()
self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, KD, rate=1/self.dt) self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, rate=1/self.dt)
self.update_limits() self.update_limits()
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt) 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.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.previous_measurement = 0.0
self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt) self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * (MAX_LAT_JERK_UP - 0.5)), self.dt)
self.low_speed_reset_threshold = max(CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED)
self.debug_counter = 0
self.prev_desired_lateral_accel = 0.0
self.is_bolt = CP.carFingerprint in BOLT_CARS
self.torque_ff_scale_pos = 1.0
self.torque_ff_scale_neg = 1.0
self.torque_deadzone_boost_neg = 0.0
self.torque_ki_mult = 1.0
if self.is_bolt:
self.torque_ff_scale_pos = float(self.torque_params.kp)
self.torque_ff_scale_neg = float(self.torque_params.ki)
self.torque_ki_mult = float(self.torque_params.kd)
self.torque_deadzone_boost_neg = float(getattr(self.torque_params, "kfDEPRECATED", 0.0))
if self.torque_ki_mult > 0.0 and self.torque_ki_mult != 1.0:
self.pid._k_i = [self.pid._k_i[0], [k * self.torque_ki_mult for k in self.pid._k_i[1]]]
def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction): def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction):
self.torque_params.latAccelFactor = latAccelFactor self.torque_params.latAccelFactor = latAccelFactor
@@ -61,7 +95,15 @@ class LatControlTorque(LatControl):
if not active: if not active:
output_torque = 0.0 output_torque = 0.0
pid_log.active = False 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)
self.prev_desired_lateral_accel = 0.0
else: else:
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) measured_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY 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)) curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
@@ -69,33 +111,51 @@ class LatControlTorque(LatControl):
delay_frames = int(np.clip(lat_delay / self.dt, 1, self.lat_accel_request_buffer_len)) 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] expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames]
# TODO factor out lateral jerk from error to later replace it with delay independent alternative
future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2 future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2
self.lat_accel_request_buffer.append(future_desired_lateral_accel) 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 gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
desired_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / lat_delay setpoint = expected_lateral_accel + desired_lateral_jerk * lat_delay
desired_lateral_accel_rate = (setpoint - self.prev_desired_lateral_accel) / self.dt
unwind_detected = (desired_lateral_accel_rate < UNWIND_D_DES_THRESHOLD and
abs(setpoint) < UNWIND_LAT_ACCEL_NEAR_ZERO)
self.prev_desired_lateral_accel = setpoint
measurement = measured_curvature * CS.vEgo ** 2 measurement = measured_curvature * CS.vEgo ** 2
measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt) measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt)
measurement_rate = np.clip(measurement_rate, -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
self.previous_measurement = measurement self.previous_measurement = measurement
setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel 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 = setpoint - measurement
error_with_lsf = error * (1 + low_speed_factor / max(current_kp, 1e-3))
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
pid_log.error = float(error) pid_log.error = float(error_with_lsf)
ff = gravity_adjusted_future_lateral_accel ff = gravity_adjusted_future_lateral_accel
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll # latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
ff -= self.torque_params.latAccelOffset ff -= self.torque_params.latAccelOffset
# TODO jerk is weighted by lat_delay for legacy reasons, but should be made independent of it ff_scale = 1.0
ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) if self.is_bolt:
ff_scale = np.interp(ff, [-FF_SCALE_BLEND_LAT_ACCEL, 0.0, FF_SCALE_BLEND_LAT_ACCEL],
[self.torque_ff_scale_neg, 1.0, self.torque_ff_scale_pos])
ff *= ff_scale
ff += get_friction(error_with_lsf + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, get_friction_threshold(CS.vEgo), self.torque_params)
deadzone_boost_active = False
if self.is_bolt and self.torque_deadzone_boost_neg > 0.0 and gravity_adjusted_future_lateral_accel < 0.0:
if abs(gravity_adjusted_future_lateral_accel) < DEADZONE_BOOST_LAT_ACCEL:
boost_scale = np.interp(abs(gravity_adjusted_future_lateral_accel), [0.0, DEADZONE_BOOST_LAT_ACCEL], [1.0, 0.0])
ff -= self.torque_deadzone_boost_neg * boost_scale
deadzone_boost_active = True
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 if CS.vEgo < self.low_speed_reset_threshold:
output_lataccel = self.pid.update(pid_log.error, self.pid.reset()
-measurement_rate, freeze_integrator = (steer_limited_by_safety or CS.steeringPressed or
feedforward=ff, CS.vEgo < self.low_speed_reset_threshold or unwind_detected)
speed=CS.vEgo, output_lataccel = self.pid.update(pid_log.error, error_rate=-measurement_rate, speed=CS.vEgo, feedforward=ff, freeze_integrator=freeze_integrator)
freeze_integrator=freeze_integrator)
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params) output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
pid_log.active = True pid_log.active = True
@@ -103,11 +163,19 @@ class LatControlTorque(LatControl):
pid_log.i = float(self.pid.i) pid_log.i = float(self.pid.i)
pid_log.d = float(self.pid.d) pid_log.d = float(self.pid.d)
pid_log.f = float(self.pid.f) pid_log.f = float(self.pid.f)
pid_log.output = float(-output_torque) # TODO: log lat accel? pid_log.output = float(-output_torque) # TODO: log lat accel?
pid_log.actualLateralAccel = float(measurement) pid_log.actualLateralAccel = float(measurement)
pid_log.desiredLateralAccel = float(setpoint) pid_log.desiredLateralAccel = float(setpoint)
pid_log.desiredLateralJerk = float(desired_lateral_jerk) pid_log.desiredLateralJerk = float(desired_lateral_jerk)
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited)) pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
if DEBUG_TORQUE_TUNE and self.is_bolt:
self.debug_counter += 1
if self.debug_counter % 50 == 0:
print(f"bolt_torque ff_scale={ff_scale:.3f} pos={self.torque_ff_scale_pos:.3f} "
f"neg={self.torque_ff_scale_neg:.3f} deadzone_boost_active={deadzone_boost_active}")
self.prev_steering_pressed = CS.steeringPressed
# TODO left is positive in this convention # TODO left is positive in this convention
return -output_torque, 0.0, pid_log return -output_torque, 0.0, pid_log
+20
View File
@@ -179,6 +179,26 @@ class LongControl:
else: else:
output_accel = raw_output_accel 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]) self.last_output_accel = clip(output_accel, accel_limits[0], accel_limits[1])
return self.last_output_accel return self.last_output_accel
@@ -5,7 +5,6 @@ import numpy as np
from cereal import log from cereal import log
from openpilot.common.numpy_fast import clip, interp from openpilot.common.numpy_fast import clip, interp
from openpilot.common.realtime import DT_MDL from openpilot.common.realtime import DT_MDL
from openpilot.common.swaglog import cloudlog
from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
# WARNING: imports outside of constants will not trigger a rebuild # WARNING: imports outside of constants will not trigger a rebuild
@@ -422,9 +421,6 @@ class LongitudinalMpc:
scale = float(np.interp(uncertainty, [0.45, 0.60], [1.2, 1.5])) scale = float(np.interp(uncertainty, [0.45, 0.60], [1.2, 1.5]))
speed_jerk *= scale speed_jerk *= scale
if abs(filter_time_factor - prev_filter_time_factor) > 1e-3:
cloudlog.error(f"LON_FILTER; filter_time_factor={filter_time_factor:.2f}; uncertainty={uncertainty:.3f}; v_ego={v_ego:.2f} mps; lead_dist={lead_dist:.2f} m; accel_reengage={accel_reengage}")
if self.mode == 'acc': if self.mode == 'acc':
a_change_cost = acceleration_jerk if prev_accel_constraint else 0 a_change_cost = acceleration_jerk if prev_accel_constraint else 0
cost_weights = [self.current_x_ego_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]
@@ -627,11 +623,7 @@ class LongitudinalMpc:
self.prev_a = np.interp(T_IDXS + self.dt, T_IDXS, self.a_solution) self.prev_a = np.interp(T_IDXS + self.dt, T_IDXS, self.a_solution)
t = time.monotonic()
if self.solution_status != 0: 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() self.reset()
# reset = 1 # reset = 1
# print(f"long_mpc timings: total internal {self.solve_time:.2e}, external: {(time.monotonic() - t0):.2e} qp {self.time_qp_solution:.2e}, \ # print(f"long_mpc timings: total internal {self.solve_time:.2e}, external: {(time.monotonic() - t0):.2e} qp {self.time_qp_solution:.2e}, \
+4 -20
View File
@@ -17,9 +17,9 @@ from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_UNSET, CONTR
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
LON_MPC_STEP = 0.2 # first step is 0.2s LON_MPC_STEP = 0.2 # first step is 0.2s
A_CRUISE_MIN = -1.2 A_CRUISE_MIN = -1.0
A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6] A_CRUISE_MAX_BP = [0.0, 5., 10., 15., 20., 25., 40.]
A_CRUISE_MAX_BP = [0., 10.0, 25., 40.] A_CRUISE_MAX_VALS = [1.125, 1.125, 1.125, 1.125, 1.25, 1.25, 1.5]
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N] CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
ALLOW_THROTTLE_THRESHOLD = 0.5 ALLOW_THROTTLE_THRESHOLD = 0.5
MIN_ALLOW_THROTTLE_SPEED = 2.5 MIN_ALLOW_THROTTLE_SPEED = 2.5
@@ -111,9 +111,6 @@ class LongitudinalPlanner:
self.a_desired_trajectory = np.zeros(CONTROL_N) self.a_desired_trajectory = np.zeros(CONTROL_N)
self.j_desired_trajectory = np.zeros(CONTROL_N) self.j_desired_trajectory = np.zeros(CONTROL_N)
self.solverExecutionTime = 0.0 self.solverExecutionTime = 0.0
# logging cadence & state
self.last_uncert_log_t = 0.0
self.prev_uncert_over = False
# ---- Rubberband mitigation state ---- # ---- Rubberband mitigation state ----
# Two uncertainty tracks (slow/fast) for asymmetric gating # Two uncertainty tracks (slow/fast) for asymmetric gating
@@ -138,7 +135,7 @@ class LongitudinalPlanner:
@property @property
def mlsim(self): def mlsim(self):
return self.generation in ("v8", "v10", "v11") return self.generation in ("v8", "v10", "v11", "v12")
def get_mpc_mode(self) -> str: def get_mpc_mode(self) -> str:
""" """
@@ -344,19 +341,6 @@ class LongitudinalPlanner:
# now_t defined earlier # now_t defined earlier
over = uncertainty > 1.0 over = uncertainty > 1.0
# Log on threshold edge or at ~1 Hz
if over != self.prev_uncert_over or (now_t - self.last_uncert_log_t) > 1.0:
try:
cloudlog.error(
f"LON_UNCERT; v_ego={v_ego:.2f} mps; desireEntropy={desire_entropy:.3f}; "
f"brakeRawMax={(raw_brake_max if 'raw_brake_max' in locals() else -1.0):.3f}; "
f"brakeDecayed={(disengage_risk if 'disengage_risk' in locals() else -1.0):.3f}; "
f"lam={(lam if 'lam' in locals() else -1.0):.2f}; uncertainty={uncertainty:.3f}; over={over}"
)
except Exception as e:
cloudlog.warning(f"LON_UNCERT log error: {e}")
self.prev_uncert_over = over
self.last_uncert_log_t = now_t
# Asymmetric accel release with hysteresis + dwell to prevent on/off pulsing # Asymmetric accel release with hysteresis + dwell to prevent on/off pulsing
rise_dwell_s, fall_dwell_s = 0.6, 0.4 rise_dwell_s, fall_dwell_s = 0.6, 0.4
+15 -7
View File
@@ -34,7 +34,8 @@ MIN_BUCKET_POINTS = np.array([100, 300, 500, 500, 500, 500, 300, 100])
MIN_ENGAGE_BUFFER = 2 # secs MIN_ENGAGE_BUFFER = 2 # secs
VERSION = 1 # bump this to invalidate old parameter caches 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): def slope2rot(slope):
@@ -72,7 +73,10 @@ class TorqueEstimator(ParameterEstimator):
self.offline_friction = 0.0 self.offline_friction = 0.0
self.offline_latAccelFactor = 0.0 self.offline_latAccelFactor = 0.0
self.resets = 0.0 self.resets = 0.0
self.use_params = CP.carName in ALLOWED_CARS and CP.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 CP.lateralTuning.which() == 'torque': if CP.lateralTuning.which() == 'torque':
self.offline_friction = CP.lateralTuning.torque.friction self.offline_friction = CP.lateralTuning.torque.friction
@@ -104,11 +108,11 @@ class TorqueEstimator(ParameterEstimator):
cache_CP = msg cache_CP = msg
if self.get_restore_key(cache_CP, cache_ltp.version) == self.get_restore_key(CP, VERSION): if self.get_restore_key(cache_CP, cache_ltp.version) == self.get_restore_key(CP, VERSION):
if cache_ltp.liveValid: if cache_ltp.liveValid:
initial_params = { if self.allow_lat_accel_learning:
'latAccelFactor': cache_ltp.latAccelFactorFiltered, initial_params['latAccelFactor'] = cache_ltp.latAccelFactorFiltered
'latAccelOffset': cache_ltp.latAccelOffsetFiltered, initial_params['latAccelOffset'] = cache_ltp.latAccelOffsetFiltered
'frictionCoefficient': cache_ltp.frictionCoefficientFiltered if self.allow_friction_learning:
} initial_params['frictionCoefficient'] = cache_ltp.frictionCoefficientFiltered
initial_params['points'] = cache_ltp.points initial_params['points'] = cache_ltp.points
self.decay = cache_ltp.decay self.decay = cache_ltp.decay
self.filtered_points.load_points(initial_params['points']) self.filtered_points.load_points(initial_params['points'])
@@ -155,6 +159,10 @@ class TorqueEstimator(ParameterEstimator):
def update_params(self, params): def update_params(self, params):
self.decay = min(self.decay + DT_MDL, MAX_FILTER_DECAY) self.decay = min(self.decay + DT_MDL, MAX_FILTER_DECAY)
for param, value in params.items(): 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(value)
self.filtered_params[param].update_alpha(self.decay) self.filtered_params[param].update_alpha(self.decay)
+3 -3
View File
@@ -33,7 +33,7 @@ class DRIVER_MONITOR_SETTINGS:
self._SG_THRESHOLD = 0.9 self._SG_THRESHOLD = 0.9
self._BLINK_THRESHOLD = 0.865 self._BLINK_THRESHOLD = 0.865
self._EE_THRESH11 = 0.4 self._EE_THRESH11 = 0.6
self._EE_THRESH12 = 15.0 self._EE_THRESH12 = 15.0
self._EE_MAX_OFFSET1 = 0.06 self._EE_MAX_OFFSET1 = 0.06
self._EE_MIN_OFFSET1 = 0.025 self._EE_MIN_OFFSET1 = 0.025
@@ -44,9 +44,9 @@ class DRIVER_MONITOR_SETTINGS:
self._POSE_YAW_THRESHOLD = 0.4020 self._POSE_YAW_THRESHOLD = 0.4020
self._POSE_YAW_THRESHOLD_SLACK = 0.5042 self._POSE_YAW_THRESHOLD_SLACK = 0.5042
self._POSE_YAW_THRESHOLD_STRICT = self._POSE_YAW_THRESHOLD 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._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_MAX_OFFSET = 0.124
self._PITCH_MIN_OFFSET = -0.0881 self._PITCH_MIN_OFFSET = -0.0881
self._YAW_MAX_OFFSET = 0.289 self._YAW_MAX_OFFSET = 0.289
Binary file not shown.
Binary file not shown.
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">اختيار</translation> <translation type="unfinished">اختيار</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">إلغاء</translation> <translation type="unfinished">إلغاء</translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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> <translation type="gpt-5-generated">&lt;b&gt;تعديلات Twilsonco المعتمدة على العزم لتنعيم التوجيه في المنعطفات.&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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> <translation type="gpt-5-generated">&lt;b&gt;سلوك متابعة يحاكي السائقين البشر&lt;/b&gt; عبر تقليل الفجوات خلف المركبات الأسرع لانطلاق أسرع وضبط مسافة المتابعة المطلوبة ديناميكياً لكبح ألطف وأكثر كفاءة.</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">دعم comma Pedal</translation> <translation type="gpt-5-generated">دعم comma Pedal</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -434,6 +434,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">Pick</translation> <translation type="unfinished">Pick</translation>
@@ -442,6 +462,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Stop</translation> <translation type="unfinished">Stop</translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1176,6 +1200,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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> <translation type="gpt-5-generated">&lt;b&gt;Twilsonco make torque tweak. Steering smooth in curve.&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2088,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> <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> <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>
<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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2505,6 +2581,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2525,6 +2609,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2554,13 +2642,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished">Reset</translation> <translation type="unfinished">Reset</translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3718,6 +3810,22 @@ Developer - Many custom setting for seasoned enthusiast</translation>
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal Help</translation> <translation type="gpt-5-generated">comma Pedal Help</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">Auswählen</translation> <translation type="unfinished">Auswählen</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Abbrechen</translation> <translation type="unfinished">Abbrechen</translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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> <translation type="gpt-5-generated">&lt;b&gt;Twilsoncos drehmomentbasierte Anpassungen zur Glättung der Lenkung in Kurven.&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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> <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>
<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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished">Zurücksetzen</translation> <translation type="unfinished">Zurücksetzen</translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Entwickler Hochgradig anpassbare Einstellungen für versierte Enthusiasten</
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">Unterstützung für comma Pedal</translation> <translation type="gpt-5-generated">Unterstützung für comma Pedal</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">Quack-select!</translation> <translation type="unfinished">Quack-select!</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1177,6 +1201,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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> <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>
<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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2088,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> <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> <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>
<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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2505,6 +2581,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2525,6 +2609,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2554,13 +2642,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3716,6 +3808,22 @@ Developer - Ultra-custom settings for seasoned duckthusiasts</translation>
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">Quack! comma Pedal Support, waddle on!</translation> <translation type="gpt-5-generated">Quack! comma Pedal Support, waddle on!</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">Seleccione</translation> <translation type="unfinished">Seleccione</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Cancelar</translation> <translation type="unfinished">Cancelar</translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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;Ajustes basados en par de Twilsonco para suavizar la dirección en curvas.&lt;/b&gt;</translation> <translation type="gpt-5-generated">&lt;b&gt;Ajustes basados en par de Twilsonco para suavizar la dirección en curvas.&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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;Comportamiento de seguimiento que imita a los conductores humanos&lt;/b&gt; cerrando huecos detrás de vehículos más rápidos para salidas más rápidas y ajustando dinámicamente la distancia de seguimiento deseada para un frenado más suave y eficiente.</translation> <translation type="gpt-5-generated">&lt;b&gt;Comportamiento de seguimiento que imita a los conductores humanos&lt;/b&gt; cerrando huecos detrás de vehículos más rápidos para salidas más rápidas y ajustando dinámicamente la distancia de seguimiento deseada para un frenado más suave y eficiente.</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3713,6 +3805,22 @@ Desarrollador: configuración altamente personalizable para entusiastas veterano
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">Compatibilidad con comma Pedal</translation> <translation type="gpt-5-generated">Compatibilidad con comma Pedal</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">Sélectionner</translation> <translation type="unfinished">Sélectionner</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Annuler</translation> <translation type="unfinished">Annuler</translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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;Ajustements basés sur le couple de Twilsonco pour adoucir la direction dans les virages.&lt;/b&gt;</translation> <translation type="gpt-5-generated">&lt;b&gt;Ajustements basés sur le couple de Twilsonco pour adoucir la direction dans les virages.&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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;Un comportement de suivi qui imite les conducteurs humains&lt;/b&gt; en réduisant les écarts derrière les véhicules plus rapides pour des départs plus rapides et en ajustant dynamiquement la distance de suivi souhaitée pour un freinage plus doux et plus efficace.</translation> <translation type="gpt-5-generated">&lt;b&gt;Un comportement de suivi qui imite les conducteurs humains&lt;/b&gt; en réduisant les écarts derrière les véhicules plus rapides pour des départs plus rapides et en ajustant dynamiquement la distance de suivi souhaitée pour un freinage plus doux et plus efficace.</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished">Réinitialiser</translation> <translation type="unfinished">Réinitialiser</translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3713,6 +3805,22 @@ Développeur Paramètres hautement personnalisables pour passionnés chevron
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">Prise en charge de comma Pedal</translation> <translation type="gpt-5-generated">Prise en charge de comma Pedal</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">Ribbit! Pick it, croak!</translation> <translation type="unfinished">Ribbit! Pick it, croak!</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Ribbit! Cancel croak!</translation> <translation type="unfinished">Ribbit! Cancel croak!</translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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;Ribbit! Twilsoncos torque tweaks smooth steering through curves, croak.&lt;/b&gt;</translation> <translation type="gpt-5-generated">&lt;b&gt;Ribbit! Twilsoncos torque tweaks smooth steering through curves, croak.&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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;Croaky follow like humans&lt;/b&gt;, ribbit! Hop to close gaps behind faster cars for quick takeoffs, and croak-adjust the follow distance for gentler, thriftier braking.</translation> <translation type="gpt-5-generated">&lt;b&gt;Croaky follow like humans&lt;/b&gt;, ribbit! Hop to close gaps behind faster cars for quick takeoffs, and croak-adjust the follow distance for gentler, thriftier braking.</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - Highly customizable settings for seasoned swamp pros</translation>
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">Ribbit! comma Pedal Support croak!</translation> <translation type="gpt-5-generated">Ribbit! comma Pedal Support croak!</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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> <translation type="gpt-5-generated">&lt;b&gt;Twilsoncoのトルクベース調整&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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> <translation type="gpt-5-generated">&lt;b&gt;&lt;/b&gt;調</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3712,6 +3804,22 @@ Developer - こだわりのある上級者向けの高度にカスタマイズ
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal </translation> <translation type="gpt-5-generated">comma Pedal </translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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> <translation type="gpt-5-generated">&lt;b&gt; Twilsonco의 .&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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> <translation type="gpt-5-generated">&lt;b&gt; &lt;/b&gt;, .</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3713,6 +3805,22 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal </translation> <translation type="gpt-5-generated">comma Pedal </translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">Be pickin'</translation> <translation type="unfinished">Be pickin'</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Belay</translation> <translation type="unfinished">Belay</translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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 torque-based tweaks t smooth out steerin in curves, arr!&lt;/b&gt;</translation> <translation type="gpt-5-generated">&lt;b&gt;Twilsoncos torque-based tweaks t smooth out steerin in curves, arr!&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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;Behavin like real hands at the wheel&lt;/b&gt; by closin gaps astern o swifter wagons fer quicker shove-offs, an smartly trimmin the wanted followin distance fer gentler, more shipshape brakn.</translation> <translation type="gpt-5-generated">&lt;b&gt;Behavin like real hands at the wheel&lt;/b&gt; by closin gaps astern o swifter wagons fer quicker shove-offs, an smartly trimmin the wanted followin distance fer gentler, more shipshape brakn.</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - Highly customizable riggins fer seasoned enthusiasts</translation
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal Support, arrr</translation> <translation type="gpt-5-generated">comma Pedal Support, arrr</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">Selecione</translation> <translation type="unfinished">Selecione</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Cancelar</translation> <translation type="unfinished">Cancelar</translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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;Ajustes baseados em torque do Twilsonco para suavizar a direção em curvas.&lt;/b&gt;</translation> <translation type="gpt-5-generated">&lt;b&gt;Ajustes baseados em torque do Twilsonco para suavizar a direção em curvas.&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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;Comportamento de acompanhamento que imita motoristas humanos&lt;/b&gt; ao fechar lacunas atrás de veículos mais rápidos para arrancadas mais rápidas e ajustar dinamicamente a distância de seguimento desejada para frenagens mais suaves e eficientes.</translation> <translation type="gpt-5-generated">&lt;b&gt;Comportamento de acompanhamento que imita motoristas humanos&lt;/b&gt; ao fechar lacunas atrás de veículos mais rápidos para arrancadas mais rápidas e ajustar dinamicamente a distância de seguimento desejada para frenagens mais suaves e eficientes.</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Desenvolvedor - Configurações altamente personalizáveis para entusiastas expe
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">Compatibilidade com comma Pedal</translation> <translation type="gpt-5-generated">Compatibilidade com comma Pedal</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">Select</translation> <translation type="unfinished">Select</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1179,6 +1203,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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 torque-wrought tweaks to make steering flow smoother midst the curves.&lt;/b&gt;</translation> <translation type="gpt-5-generated">&lt;b&gt;Twilsoncos torque-wrought tweaks to make steering flow smoother midst the curves.&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2092,6 +2132,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> <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;Following comportment that doth mirror mortal coachmen&lt;/b&gt; by narrowing gaps behind swifter chariots for hastier set-offs and by dynamically tuning the desired following distance for gentler, more thrifty braking.</translation> <translation type="gpt-5-generated">&lt;b&gt;Following comportment that doth mirror mortal coachmen&lt;/b&gt; by narrowing gaps behind swifter chariots for hastier set-offs and by dynamically tuning the desired following distance for gentler, more thrifty braking.</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2509,6 +2585,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2529,6 +2613,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2558,13 +2646,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3724,6 +3816,22 @@ Developer - Most customizable settings for well-tried enthusiasts</translation>
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal Support</translation> <translation type="gpt-5-generated">comma Pedal Support</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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> <translation type="gpt-5-generated">&lt;b&gt; Twilsonco &lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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> <translation type="gpt-5-generated">&lt;b&gt;&lt;/b&gt; </translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated"> comma Pedal</translation> <translation type="gpt-5-generated"> comma Pedal</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished">Seç</translation> <translation type="unfinished">Seç</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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;Virajlarda direksiyonu yumuşatmak için Twilsonconun torka dayalı ayarlamaları.&lt;/b&gt;</translation> <translation type="gpt-5-generated">&lt;b&gt;Virajlarda direksiyonu yumuşatmak için Twilsonconun torka dayalı ayarlamaları.&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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;İnsan sürücüleri taklit eden takip davranışı&lt;/b&gt;: Daha hızlı araçların arkasındaki boşlukları kapatarak daha hızlı kalkışlar ve daha yumuşak, daha verimli frenleme için istenen takip mesafesini dinamik olarak ayarlama.</translation> <translation type="gpt-5-generated">&lt;b&gt;İnsan sürücüleri taklit eden takip davranışı&lt;/b&gt;: Daha hızlı araçların arkasındaki boşlukları kapatarak daha hızlı kalkışlar ve daha yumuşak, daha verimli frenleme için istenen takip mesafesini dinamik olarak ayarlama.</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished">Sıfırla</translation> <translation type="unfinished">Sıfırla</translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3713,6 +3805,22 @@ Geliştirici - Tecrübeli meraklılar için yüksek özelleştirilebilir ayarlar
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal Desteği</translation> <translation type="gpt-5-generated">comma Pedal Desteği</translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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> <translation type="gpt-5-generated">&lt;b&gt;Twilsonco &lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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> <translation type="gpt-5-generated">&lt;b&gt;仿&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal </translation> <translation type="gpt-5-generated">comma Pedal </translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context> </context>
<context> <context>
<name>ExpandableMultiOptionDialog</name> <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> <message>
<source>Select</source> <source>Select</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -441,6 +461,10 @@
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExperimentalModeButton</name> <name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source> <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> <translation type="gpt-5-generated">&lt;b&gt;Twilsonco 調&lt;/b&gt;</translation>
</message> </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>
<context> <context>
<name>FrogPilotLongitudinalPanel</name> <name>FrogPilotLongitudinalPanel</name>
@@ -2086,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> <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> <translation type="gpt-5-generated">&lt;b&gt;仿&lt;/b&gt;調</translation>
</message> </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>
<context> <context>
<name>FrogPilotManageControl</name> <name>FrogPilotManageControl</name>
@@ -2503,6 +2579,14 @@
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source> <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> <translation type="unfinished"></translation>
</message> </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> <message>
<source>Stop Distance</source> <source>Stop Distance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source> <source>Select the active driving model.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>There are no more models to blacklist! The only available model is "%1"!</source> <source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Reset</source> <source>Reset</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>FrogPilotModelReview</name> <name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - 為資深愛好者提供高度自訂的設定</translation>
<source>comma Pedal Support</source> <source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal </translation> <translation type="gpt-5-generated">comma Pedal </translation>
</message> </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>
<context> <context>
<name>FrogPilotVisualsPanel</name> <name>FrogPilotVisualsPanel</name>
BIN
View File
Binary file not shown.
+46
View File
@@ -92,6 +92,40 @@ def manager_init() -> None:
params.put_bool("IsTestedBranch", build_metadata.tested_channel) params.put_bool("IsTestedBranch", build_metadata.tested_channel)
params.put_bool("IsReleaseBranch", build_metadata.release_channel) params.put_bool("IsReleaseBranch", build_metadata.release_channel)
# One-time migration to align FrogPilot defaults after install
frogpilot_migration_flag_file = "/data/media/0/frogpilot_migrated.flag"
if not os.path.exists(frogpilot_migration_flag_file):
params.put_bool("NNFF", False)
params.put_bool("NNFFLite", False)
params.put_bool("AdvancedLateralTune", True)
params.put_bool("ForceAutoTuneOff", True)
params.put_bool("ForceAutoTune", False)
params.put_bool("CECurves", False)
params.put_bool("CENavigation", False)
params.put_bool("ShowCEMStatus", True)
params.put_bool("CESlowerLead", True)
params.put_bool("CEStoppedLead", True)
params.put_int("CEModelStopTime", 8)
params.put_bool("ReverseCruise", True)
params.put_bool("HumanFollowing", False)
params.put_bool("HumanAcceleration", False)
params.put_int("TuningLevel", 3)
params.put_bool("TuningLevelConfirmed", True)
params.put_bool("DeveloperUI", True)
params.put_bool("DeveloperWidgets", True)
params.put_bool("DeveloperSidebar", False)
params.put_bool("LeadInfo", True)
params.put_bool("BorderMetrics", True)
params.put_bool("ShowSteering", True)
params.put_bool("BlindSpotMetrics", True)
with open(frogpilot_migration_flag_file, "w") as f:
f.write("migrated")
# One-time migration for HumanAcceleration and HumanFollowing to off # One-time migration for HumanAcceleration and HumanFollowing to off
migration_flag_file = "/data/media/0/frogpilot_human_toggles_migrated.flag" migration_flag_file = "/data/media/0/frogpilot_human_toggles_migrated.flag"
if not os.path.exists(migration_flag_file): if not os.path.exists(migration_flag_file):
@@ -134,6 +168,18 @@ def manager_init() -> None:
with open(nnff_migration_flag_file, "w") as f: with open(nnff_migration_flag_file, "w") as f:
f.write("migrated") f.write("migrated")
# One-time migration for lateral tuning/auto-tune preferences
lateral_tuning_migration_flag_file = "/data/media/0/frogpilot_lateral_tuning_migrated.flag"
if not os.path.exists(lateral_tuning_migration_flag_file):
if not params.get_bool("AdvancedLateralTune"):
params.put_bool("AdvancedLateralTune", True)
if params.get_bool("ForceAutoTune"):
params.put_bool("ForceAutoTune", False)
if not params.get_bool("ForceAutoTuneOff"):
params.put_bool("ForceAutoTuneOff", True)
with open(lateral_tuning_migration_flag_file, "w") as f:
f.write("migrated")
# set dongle id # set dongle id
reg_res = register(show_spinner=True) reg_res = register(show_spinner=True)
if reg_res: if reg_res:
+23 -19
View File
@@ -1,5 +1,6 @@
"""Install exception handler for process crash.""" import glob
import os import os
import re
import sentry_sdk import sentry_sdk
import traceback import traceback
from datetime import datetime from datetime import datetime
@@ -9,15 +10,16 @@ from sentry_sdk.integrations.threading import ThreadingIntegration
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.system.hardware import HARDWARE, PC from openpilot.system.hardware import HARDWARE, PC
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware.hw import Paths
from openpilot.system.version import get_build_metadata, get_version from openpilot.system.version import get_build_metadata, get_version
from openpilot.frogpilot.common.frogpilot_variables import ERROR_LOGS_PATH, params from openpilot.frogpilot.common.frogpilot_variables import ERROR_LOGS_PATH, params
class SentryProject(Enum): class SentryProject(Enum):
# python project # python project
SELFDRIVE = os.environ.get("SENTRY_DSN", "") SELFDRIVE = "https://7305139359a548fcb348ec09497dc389@bugsink.firestar.link/1"
# native project # native project
SELFDRIVE_NATIVE = os.environ.get("SENTRY_DSN", "") SELFDRIVE_NATIVE = "https://7305139359a548fcb348ec09497dc389@bugsink.firestar.link/1"
def report_tombstone(fn: str, message: str, contents: str) -> None: def report_tombstone(fn: str, message: str, contents: str) -> None:
@@ -26,6 +28,12 @@ def report_tombstone(fn: str, message: str, contents: str) -> None:
with sentry_sdk.configure_scope() as scope: with sentry_sdk.configure_scope() as scope:
scope.set_extra("tombstone_fn", fn) scope.set_extra("tombstone_fn", fn)
scope.set_extra("tombstone", contents) scope.set_extra("tombstone", contents)
# Attach qlog for debugging context
qlogs = glob.glob(f"{Paths.log_root()}/*/qlog")
if qlogs:
scope.add_attachment(path=max(qlogs, key=os.path.getmtime), filename="qlog")
sentry_sdk.capture_message(message=message) sentry_sdk.capture_message(message=message)
sentry_sdk.flush() sentry_sdk.flush()
@@ -51,8 +59,14 @@ def capture_exception(*args, crash_log=True, **kwargs) -> None:
cloudlog.error("crash", exc_info=kwargs.get('exc_info', 1)) cloudlog.error("crash", exc_info=kwargs.get('exc_info', 1))
try: try:
sentry_sdk.capture_exception(*args, **kwargs) with sentry_sdk.push_scope() as scope:
sentry_sdk.flush() # https://github.com/getsentry/sentry-python/issues/291 # Attach qlog for debugging context
qlogs = glob.glob(f"{Paths.log_root()}/*/qlog")
if qlogs:
scope.add_attachment(path=max(qlogs, key=os.path.getmtime), filename="qlog")
sentry_sdk.capture_exception(*args, **kwargs)
sentry_sdk.flush() # https://github.com/getsentry/sentry-python/issues/291
except Exception: except Exception:
cloudlog.exception("sentry exception") cloudlog.exception("sentry exception")
@@ -90,25 +104,15 @@ def save_exception(exc_text: str, crash_log) -> None:
def init(project: SentryProject) -> bool: def init(project: SentryProject) -> bool:
build_metadata = get_build_metadata() if PC:
FrogPilot = "frogai" in build_metadata.openpilot.git_origin.lower()
if not FrogPilot or PC:
return False return False
build_metadata = get_build_metadata()
short_branch = build_metadata.channel short_branch = build_metadata.channel
if short_branch in ["COMMA", "HEAD"]: env = short_branch
return if re.search("test", short_branch, re.IGNORECASE):
elif short_branch == "FrogPilot-Development":
env = "Development"
elif build_metadata.release_channel:
env = "Release"
elif short_branch == "FrogPilot-Testing":
env = "Testing" env = "Testing"
elif build_metadata.tested_channel:
env = "Staging"
else:
env = short_branch
dongle_id = params.get("DongleId", encoding="utf-8") dongle_id = params.get("DongleId", encoding="utf-8")
installed = params.get("InstallDate", encoding="utf-8") installed = params.get("InstallDate", encoding="utf-8")
+65 -3
View File
@@ -184,6 +184,39 @@ def finalize_update(params) -> None:
"""Take the current OverlayFS merged view and finalize a copy outside of """Take the current OverlayFS merged view and finalize a copy outside of
OverlayFS, ready to be swapped-in at BASEDIR. Copy using shutil.copytree""" OverlayFS, ready to be swapped-in at BASEDIR. Copy using shutil.copytree"""
def get_directory_size(path: str) -> int:
size = 0
for root, _, files in os.walk(path):
for name in files:
file_path = os.path.join(root, name)
if not os.path.islink(file_path):
try:
size += os.lstat(file_path).st_size
except FileNotFoundError:
pass
return size
total_size = get_directory_size(OVERLAY_MERGED)
copied_size = 0
def copy_with_progress(src, dst, *, follow_symlinks=True):
nonlocal copied_size
if os.path.islink(src):
linkto = os.readlink(src)
os.symlink(linkto, dst)
return dst
result = shutil.copy2(src, dst, follow_symlinks=follow_symlinks)
try:
copied_size += os.lstat(src).st_size
if total_size > 0:
progress = min(int((copied_size / total_size) * 100), 100)
params.put("UpdaterState", f"finalizing update... {progress}%")
except FileNotFoundError:
pass
return result
# Remove the update ready flag and any old updates # Remove the update ready flag and any old updates
cloudlog.info("creating finalized version of the overlay") cloudlog.info("creating finalized version of the overlay")
set_consistent_flag(False) set_consistent_flag(False)
@@ -191,7 +224,12 @@ def finalize_update(params) -> None:
# Copy the merged overlay view and set the update ready flag # Copy the merged overlay view and set the update ready flag
if os.path.exists(FINALIZED): if os.path.exists(FINALIZED):
shutil.rmtree(FINALIZED) shutil.rmtree(FINALIZED)
shutil.copytree(OVERLAY_MERGED, FINALIZED, symlinks=True) if total_size == 0:
shutil.copytree(OVERLAY_MERGED, FINALIZED, symlinks=True)
else:
params.put("UpdaterState", "finalizing update... 0%")
shutil.copytree(OVERLAY_MERGED, FINALIZED, symlinks=True, copy_function=copy_with_progress)
params.put("UpdaterState", "finalizing update... 100%")
run(["git", "reset", "--hard"], FINALIZED) run(["git", "reset", "--hard"], FINALIZED)
run(["git", "submodule", "foreach", "--recursive", "git", "reset", "--hard"], FINALIZED) run(["git", "submodule", "foreach", "--recursive", "git", "reset", "--hard"], FINALIZED)
@@ -377,10 +415,34 @@ class Updater:
else: else:
cloudlog.info(f"up to date on {cur_branch} ({str(cur_commit)[:7]})") cloudlog.info(f"up to date on {cur_branch} ({str(cur_commit)[:7]})")
def _git_fetch_with_progress(self, branch: str) -> str:
fetch_cmd = ["git", "fetch", "--progress", "origin", branch]
progress = 0
output_lines: list[str] = []
with subprocess.Popen(fetch_cmd, cwd=OVERLAY_MERGED, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1) as proc:
assert proc.stdout is not None
for line in proc.stdout:
output_lines.append(line)
matches = re.findall(r"(\d+)%", line)
if matches:
progress = max(progress, int(matches[-1]))
self.params.put("UpdaterState", f"downloading... {progress}%")
proc.wait()
if proc.returncode != 0:
raise subprocess.CalledProcessError(proc.returncode, fetch_cmd, output=''.join(output_lines))
if progress < 100:
self.params.put("UpdaterState", "downloading... 100%")
return ''.join(output_lines)
def fetch_update(self) -> None: def fetch_update(self) -> None:
cloudlog.info("attempting git fetch inside staging overlay") cloudlog.info("attempting git fetch inside staging overlay")
self.params.put("UpdaterState", "downloading...") self.params.put("UpdaterState", "downloading... 0%")
# TODO: cleanly interrupt this and invalidate old update # TODO: cleanly interrupt this and invalidate old update
set_consistent_flag(False) set_consistent_flag(False)
@@ -389,7 +451,7 @@ class Updater:
setup_git_options(OVERLAY_MERGED) setup_git_options(OVERLAY_MERGED)
branch = self.target_branch branch = self.target_branch
git_fetch_output = run(["git", "fetch", "origin", branch], OVERLAY_MERGED) git_fetch_output = self._git_fetch_with_progress(branch)
cloudlog.info("git fetch success: %s", git_fetch_output) cloudlog.info("git fetch success: %s", git_fetch_output)
cloudlog.info("git reset in progress") cloudlog.info("git reset in progress")