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 {
useSteeringAngle @0 :Bool;
kp @1 :Float32;
ki @2 :Float32;
kd @8 : Float32;
friction @3 :Float32;
steeringAngleDeadzoneDeg @5 :Float32;
latAccelFactor @6 :Float32;
latAccelOffset @7 :Float32;
kpDEPRECATED @1 :Float32;
kiDEPRECATED @2 :Float32;
kfDEPRECATED @4 :Float32;
kdDEPRECATED @8 : Float32;
}
struct LongitudinalPIDTuning {
+1 -1
View File
@@ -105,7 +105,7 @@ struct FrogPilotCarParams @0xf35cc4560bbf6ec2 {
isHDA2 @3 :Bool;
openpilotLongitudinalControlDisabled @4 :Bool;
safetyConfigs @5 :List(SafetyConfig);
canUseSASCM @6 :Bool;
struct SafetyConfig {
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
};
#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,
29, 204, 78, 128, 14, 110, 54, 128,
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,
240, 0, 0, 0, 3, 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, 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,
248, 0, 0, 0, 3, 0, 1, 0,
4, 1, 0, 0, 2, 0, 1, 0,
6, 0, 0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0,
4, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 106, 0, 0, 0,
1, 1, 0, 0, 74, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 3, 0, 1, 0,
12, 1, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 3, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
8, 0, 0, 0, 4, 0, 0, 0,
0, 0, 1, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
9, 1, 0, 0, 74, 0, 0, 0,
9, 1, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
8, 1, 0, 0, 3, 0, 1, 0,
20, 1, 0, 0, 2, 0, 1, 0,
7, 0, 0, 0, 4, 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,
5, 0, 0, 0, 5, 0, 0, 0,
0, 0, 1, 0, 5, 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,
32, 1, 0, 0, 3, 0, 1, 0,
44, 1, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 6, 0, 0, 0,
0, 0, 1, 0, 6, 0, 0, 0,
7, 0, 0, 0, 7, 0, 0, 0,
0, 0, 1, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
41, 1, 0, 0, 122, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
40, 1, 0, 0, 3, 0, 1, 0,
52, 1, 0, 0, 2, 0, 1, 0,
4, 0, 0, 0, 7, 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,
3, 0, 0, 0, 8, 0, 0, 0,
0, 0, 1, 0, 8, 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,
56, 1, 0, 0, 3, 0, 1, 0,
68, 1, 0, 0, 2, 0, 1, 0,
44, 1, 0, 0, 3, 0, 1, 0,
56, 1, 0, 0, 2, 0, 1, 0,
117, 115, 101, 83, 116, 101, 101, 114,
105, 110, 103, 65, 110, 103, 108, 101,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -5355,8 +5355,7 @@ static const ::capnp::_::AlignedData<166> b_80366e0e804ecc1d = {
1, 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,
65, 84, 69, 68, 0, 0, 0, 0,
107, 112, 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,
@@ -5364,8 +5363,7 @@ static const ::capnp::_::AlignedData<166> b_80366e0e804ecc1d = {
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
107, 105, 68, 69, 80, 82, 69, 67,
65, 84, 69, 68, 0, 0, 0, 0,
107, 105, 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,
@@ -5420,8 +5418,7 @@ static const ::capnp::_::AlignedData<166> b_80366e0e804ecc1d = {
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
107, 100, 68, 69, 80, 82, 69, 67,
65, 84, 69, 68, 0, 0, 0, 0,
107, 100, 0, 0, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -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 i_80366e0e804ecc1d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
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
};
#endif // !CAPNP_LITE
+18 -18
View File
@@ -3134,9 +3134,9 @@ public:
inline bool getUseSteeringAngle() const;
inline float getKpDEPRECATED() const;
inline float getKp() const;
inline float getKiDEPRECATED() const;
inline float getKi() const;
inline float getFriction() const;
@@ -3148,7 +3148,7 @@ public:
inline float getLatAccelOffset() const;
inline float getKdDEPRECATED() const;
inline float getKd() const;
private:
::capnp::_::StructReader _reader;
@@ -3181,11 +3181,11 @@ public:
inline bool getUseSteeringAngle();
inline void setUseSteeringAngle(bool value);
inline float getKpDEPRECATED();
inline void setKpDEPRECATED(float value);
inline float getKp();
inline void setKp(float value);
inline float getKiDEPRECATED();
inline void setKiDEPRECATED(float value);
inline float getKi();
inline void setKi(float value);
inline float getFriction();
inline void setFriction(float value);
@@ -3202,8 +3202,8 @@ public:
inline float getLatAccelOffset();
inline void setLatAccelOffset(float value);
inline float getKdDEPRECATED();
inline void setKdDEPRECATED(float value);
inline float getKd();
inline void setKd(float value);
private:
::capnp::_::StructBuilder _builder;
@@ -7754,30 +7754,30 @@ inline void CarParams::LateralTorqueTuning::Builder::setUseSteeringAngle(bool va
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
}
inline float CarParams::LateralTorqueTuning::Reader::getKpDEPRECATED() const {
inline float CarParams::LateralTorqueTuning::Reader::getKp() const {
return _reader.getDataField<float>(
::capnp::bounded<1>() * ::capnp::ELEMENTS);
}
inline float CarParams::LateralTorqueTuning::Builder::getKpDEPRECATED() {
inline float CarParams::LateralTorqueTuning::Builder::getKp() {
return _builder.getDataField<float>(
::capnp::bounded<1>() * ::capnp::ELEMENTS);
}
inline void CarParams::LateralTorqueTuning::Builder::setKpDEPRECATED(float value) {
inline void CarParams::LateralTorqueTuning::Builder::setKp(float value) {
_builder.setDataField<float>(
::capnp::bounded<1>() * ::capnp::ELEMENTS, value);
}
inline float CarParams::LateralTorqueTuning::Reader::getKiDEPRECATED() const {
inline float CarParams::LateralTorqueTuning::Reader::getKi() const {
return _reader.getDataField<float>(
::capnp::bounded<2>() * ::capnp::ELEMENTS);
}
inline float CarParams::LateralTorqueTuning::Builder::getKiDEPRECATED() {
inline float CarParams::LateralTorqueTuning::Builder::getKi() {
return _builder.getDataField<float>(
::capnp::bounded<2>() * ::capnp::ELEMENTS);
}
inline void CarParams::LateralTorqueTuning::Builder::setKiDEPRECATED(float value) {
inline void CarParams::LateralTorqueTuning::Builder::setKi(float value) {
_builder.setDataField<float>(
::capnp::bounded<2>() * ::capnp::ELEMENTS, value);
}
@@ -7852,16 +7852,16 @@ inline void CarParams::LateralTorqueTuning::Builder::setLatAccelOffset(float val
::capnp::bounded<7>() * ::capnp::ELEMENTS, value);
}
inline float CarParams::LateralTorqueTuning::Reader::getKdDEPRECATED() const {
inline float CarParams::LateralTorqueTuning::Reader::getKd() const {
return _reader.getDataField<float>(
::capnp::bounded<8>() * ::capnp::ELEMENTS);
}
inline float CarParams::LateralTorqueTuning::Builder::getKdDEPRECATED() {
inline float CarParams::LateralTorqueTuning::Builder::getKd() {
return _builder.getDataField<float>(
::capnp::bounded<8>() * ::capnp::ELEMENTS);
}
inline void CarParams::LateralTorqueTuning::Builder::setKdDEPRECATED(float value) {
inline void CarParams::LateralTorqueTuning::Builder::setKd(float value) {
_builder.setDataField<float>(
::capnp::bounded<8>() * ::capnp::ELEMENTS, value);
}
+51 -35
View File
@@ -642,7 +642,7 @@ const ::capnp::_::RawSchema s_aedffd8f31e7b55d = {
};
#endif // !CAPNP_LITE
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,
194, 110, 191, 11, 86, 196, 92, 243,
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,
33, 0, 0, 0, 23, 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,
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,
83, 97, 102, 101, 116, 121, 67, 111,
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, 1, 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,
152, 0, 0, 0, 3, 0, 1, 0,
164, 0, 0, 0, 2, 0, 1, 0,
180, 0, 0, 0, 3, 0, 1, 0,
192, 0, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
161, 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,
189, 0, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
188, 0, 0, 0, 3, 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,
0, 0, 1, 0, 5, 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,
196, 0, 0, 0, 3, 0, 1, 0,
224, 0, 0, 0, 2, 0, 1, 0,
224, 0, 0, 0, 3, 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,
100, 97, 108, 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,
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, }
};
@@ -772,11 +788,11 @@ static const ::capnp::_::AlignedData<123> b_f35cc4560bbf6ec2 = {
static const ::capnp::_::RawSchema* const d_f35cc4560bbf6ec2[] = {
&s_8d65dd40bad40951,
};
static const uint16_t m_f35cc4560bbf6ec2[] = {0, 1, 2, 3, 4, 5};
static const uint16_t i_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, 6};
const ::capnp::_::RawSchema s_f35cc4560bbf6ec2 = {
0xf35cc4560bbf6ec2, b_f35cc4560bbf6ec2.words, 123, d_f35cc4560bbf6ec2, m_f35cc4560bbf6ec2,
1, 6, i_f35cc4560bbf6ec2, nullptr, nullptr, { &s_f35cc4560bbf6ec2, nullptr, nullptr, 0, 0, nullptr }, false
0xf35cc4560bbf6ec2, b_f35cc4560bbf6ec2.words, 139, d_f35cc4560bbf6ec2, m_f35cc4560bbf6ec2,
1, 7, i_f35cc4560bbf6ec2, nullptr, nullptr, { &s_f35cc4560bbf6ec2, nullptr, nullptr, 0, 0, nullptr }, false
};
#endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<36> b_8d65dd40bad40951 = {
+19
View File
@@ -669,6 +669,8 @@ public:
inline bool hasSafetyConfigs() const;
inline ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>::Reader getSafetyConfigs() const;
inline bool getCanUseSASCM() const;
private:
::capnp::_::StructReader _reader;
template <typename, ::capnp::Kind>
@@ -719,6 +721,9 @@ public:
inline void adoptSafetyConfigs(::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>>&& value);
inline ::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>> disownSafetyConfigs();
inline bool getCanUseSASCM();
inline void setCanUseSASCM(bool value);
private:
::capnp::_::StructBuilder _builder;
template <typename, ::capnp::Kind>
@@ -2220,6 +2225,20 @@ inline ::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfi
::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 {
return _reader.getDataField< ::uint16_t>(
::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},
{"AdvancedLongitudinalTune", PERSISTENT},
{"AggressiveFollow", PERSISTENT},
{"AggressiveFollowHigh", PERSISTENT},
{"AggressiveJerkAcceleration", PERSISTENT},
{"AggressiveJerkDanger", PERSISTENT},
{"AggressiveJerkDeceleration", PERSISTENT},
@@ -321,6 +322,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DynamicPedalsOnUI", PERSISTENT},
{"EngageVolume", PERSISTENT},
{"ExperimentalGMTune", PERSISTENT},
{"EVTuning", PERSISTENT},
{"Fahrenheit", PERSISTENT},
{"FavoriteDestinations", PERSISTENT | DONT_LOG},
{"FlashPanda", CLEAR_ON_MANAGER_START},
@@ -346,6 +348,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"FrogsGoMoosTweak", PERSISTENT},
{"FullMap", PERSISTENT},
{"GasRegenCmd", PERSISTENT},
{"GMPedalLongitudinal", PERSISTENT},
{"GoatScream", PERSISTENT},
{"GreenLightAlert", PERSISTENT},
{"HideAlerts", PERSISTENT},
@@ -406,6 +409,10 @@ std::unordered_map<std::string, uint32_t> keys = {
{"ModelToDownload", CLEAR_ON_MANAGER_START},
{"ModelUI", PERSISTENT},
{"ModelVersions", PERSISTENT},
{"ModelReleasedDates", PERSISTENT},
{"CommunityFavorites", PERSISTENT},
{"UserFavorites", PERSISTENT},
{"SortModelsByDate", PERSISTENT},
{"NavigationUI", PERSISTENT},
{"NextMapSpeedLimit", CLEAR_ON_MANAGER_START},
{"NewLongAPI", PERSISTENT},
@@ -451,6 +458,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"RandomThemes", PERSISTENT},
{"RefuseVolume", PERSISTENT},
{"RelaxedFollow", PERSISTENT},
{"RelaxedFollowHigh", PERSISTENT},
{"RelaxedJerkAcceleration", PERSISTENT},
{"RelaxedJerkDanger", PERSISTENT},
{"RelaxedJerkDeceleration", PERSISTENT},
@@ -510,6 +518,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"SpeedLimitsFiltered", PERSISTENT | DONT_LOG},
{"SpeedLimitSources", PERSISTENT},
{"StandardFollow", PERSISTENT},
{"StandardFollowHigh", PERSISTENT},
{"StandardJerkAcceleration", PERSISTENT},
{"StandardJerkDanger", PERSISTENT},
{"StandardJerkDeceleration", PERSISTENT},
@@ -526,6 +535,8 @@ std::unordered_map<std::string, uint32_t> keys = {
{"SteerDelayStock", PERSISTENT},
{"SteerFriction", PERSISTENT},
{"SteerFrictionStock", PERSISTENT},
{"SteerOffset", PERSISTENT},
{"SteerOffsetStock", PERSISTENT},
{"SteerLatAccel", PERSISTENT},
{"SteerLatAccelStock", PERSISTENT},
{"SteerKP", PERSISTENT},
@@ -540,6 +551,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"StoppedTimer", PERSISTENT},
{"TacoTune", PERSISTENT},
{"TacoTuneHacks", PERSISTENT},
{"TrailerLoad", PERSISTENT},
{"TestAlert", CLEAR_ON_MANAGER_START},
{"TetheringEnabled", PERSISTENT},
{"ThemeDownloadProgress", CLEAR_ON_MANAGER_START},
@@ -579,6 +591,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"WheelIcon", PERSISTENT},
{"WheelSpeed", PERSISTENT},
{"StopDistance", PERSISTENT},
{"RecoveryPower", PERSISTENT},
{"WheelToDownload", CLEAR_ON_MANAGER_START},
};
Binary file not shown.
+27 -4
View File
@@ -107,7 +107,7 @@ class ModelManager:
self.downloading_model = False
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)
filenames = [
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_vision_metadata.pkl",
]
if model_version == "v12":
filenames += [
f"{model_to_download}_driving_off_policy_tinygrad.pkl",
f"{model_to_download}_driving_off_policy_metadata.pkl",
]
for filename in filenames:
model_path = MODELS_PATH / filename
model_url = f"{repo_url}/Models/{filename}"
@@ -208,13 +213,18 @@ class ModelManager:
except Exception:
model_version = None
if model_version in ("v8", "v9", "v10", "v11"):
if model_version in ("v8", "v9", "v10", "v11", "v12"):
v8_v9_files = [
f"{model}_driving_policy_tinygrad.pkl",
f"{model}_driving_vision_tinygrad.pkl",
f"{model}_driving_policy_metadata.pkl",
f"{model}_driving_vision_metadata.pkl",
]
if model_version == "v12":
v8_v9_files += [
f"{model}_driving_off_policy_tinygrad.pkl",
f"{model}_driving_off_policy_metadata.pkl",
]
if all((MODELS_PATH / f).is_file() for f in v8_v9_files):
downloaded_models.add(model)
elif model_version == "v7":
@@ -259,13 +269,18 @@ class ModelManager:
except Exception:
model_version = None
if model_version in ("v8", "v9", "v10", "v11"):
if model_version in ("v8", "v9", "v10", "v11", "v12"):
v8_v9_files = [
f"{model}_driving_policy_tinygrad.pkl",
f"{model}_driving_vision_tinygrad.pkl",
f"{model}_driving_policy_metadata.pkl",
f"{model}_driving_vision_metadata.pkl",
]
if model_version == "v12":
v8_v9_files += [
f"{model}_driving_off_policy_tinygrad.pkl",
f"{model}_driving_off_policy_metadata.pkl",
]
for filename in v8_v9_files:
path = MODELS_PATH / filename
expected_size = model_sizes.get(filename.rsplit(".", 1)[0])
@@ -296,7 +311,10 @@ class ModelManager:
params.put("AvailableModels", ",".join(self.available_models))
params.put("AvailableModelNames", ",".join([model["name"] for model in model_info]))
params.put("AvailableModelSeries", ",".join(self.model_series))
params.put("CommunityFavorites", ",".join([model["id"] for model in model_info if model.get("community_favorite", False)]))
params.put("ModelReleasedDates", ",".join([model.get("released", "2023-01-01") for model in model_info]))
params.put("ModelVersions", ",".join(self.model_versions))
params.put("CommunityFavorites", ",".join([model["id"] for model in model_info if model.get("community_favorite", False)]))
params.put("AvailableModelSeries", ",".join(self.model_series))
print("Models list updated successfully")
@@ -375,13 +393,18 @@ class ModelManager:
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
return
if model_version in ("v8", "v9", "v10", "v11"):
if model_version in ("v8", "v9", "v10", "v11", "v12"):
required_files = [
f"{model}_driving_policy_tinygrad.pkl",
f"{model}_driving_vision_tinygrad.pkl",
f"{model}_driving_policy_metadata.pkl",
f"{model}_driving_vision_metadata.pkl",
]
if model_version == "v12":
required_files += [
f"{model}_driving_off_policy_tinygrad.pkl",
f"{model}_driving_off_policy_metadata.pkl",
]
missing = [f for f in required_files if not (MODELS_PATH / f).is_file()]
if missing:
print(f"Tinygrad model {model} is missing files. Preparing to download...")
+55 -19
View File
@@ -13,13 +13,13 @@ from openpilot.common.conversions import Conversions as CV
from openpilot.common.params import Params
from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.selfdrive.car.car_helpers import interfaces
from openpilot.selfdrive.car.gm.values import GMFlags
from openpilot.selfdrive.car.gm.values import EV_CAR as GM_EV_CAR, GMFlags
from openpilot.selfdrive.car.hyundai.values import EV_CAR as HYUNDAI_EV_CAR
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.mock.interface import CarInterface
from openpilot.selfdrive.car.mock.values import CAR as MOCK
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.latcontrol_torque import KP
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.system.hardware import HARDWARE
from openpilot.system.hardware.power_monitoring import VBATT_PAUSE_CHARGING
@@ -33,6 +33,7 @@ params_memory = Params("/dev/shm/params")
GearShifter = car.CarState.GearShifter
SafetyModel = car.CarParams.SafetyModel
TransmissionType = car.CarParams.TransmissionType
CITY_SPEED_LIMIT = 25 # 55mph is typically the minimum speed for highways
CRUISING_SPEED = 5 # Roughly the speed cars go when not touching the gas while in drive
@@ -95,6 +96,8 @@ EXCLUDED_KEYS = {
}
TINYGRAD_FILES = [
("driving_off_policy_metadata.pkl", "off-policy metadata"),
("driving_off_policy_tinygrad.pkl", "off-policy model"),
("driving_policy_metadata.pkl", "policy metadata"),
("driving_policy_tinygrad.pkl", "policy model"),
("driving_vision_metadata.pkl", "vision metadata"),
@@ -127,9 +130,11 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("AdjacentPath", "0", 3, "0"),
("AdjacentPathMetrics", "0", 3, "0"),
("AdvancedCustomUI", "0", 2, "0"),
("AdvancedLateralTune", "0", 2, "0"),
("AdvancedLateralTune", "1", 2, "0"),
("AdvancedLongitudinalTune", "0", 3, "0"),
("EVTuning", "", 3, "0"),
("AggressiveFollow", "1.25", 2, "1.25"),
("AggressiveFollowHigh", "1.25", 2, "1.25"),
("AggressiveJerkAcceleration", "50", 3, "50"),
("AggressiveJerkDanger", "100", 3, "100"),
("AggressiveJerkDeceleration", "50", 3, "50"),
@@ -148,11 +153,13 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("AvailableModelNames", "", 1, ""),
("AvailableModelSeries", "", 1, ""),
("AvailableModels", "", 1, ""),
("CommunityFavorites", "", 1, ""),
("UserFavorites", "", 0, ""),
("BigMap", "0", 2, "0"),
("BlacklistedModels", "", 2, ""),
("BlindSpotMetrics", "1", 3, "0"),
("BlindSpotPath", "1", 1, "0"),
("BorderMetrics", "0", 3, "0"),
("BorderMetrics", "1", 3, "0"),
("CalibratedLateralAcceleration", str(DEFAULT_LATERAL_ACCELERATION), 2, str(DEFAULT_LATERAL_ACCELERATION)),
("CalibrationProgress", "0", 3, "0"),
("CameraView", "3", 2, "0"),
@@ -191,7 +198,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("CustomUI", "1", 1, "0"),
("DecelerationProfile", "1", 2, "0"),
("DeveloperMetrics", "1", 3, "0"),
("DeveloperSidebar", "1", 3, "0"),
("DeveloperSidebar", "0", 3, "0"),
("DeveloperSidebarMetric1", "1", 3, "0"),
("DeveloperSidebarMetric2", "2", 3, "0"),
("DeveloperSidebarMetric3", "3", 3, "0"),
@@ -200,7 +207,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("DeveloperSidebarMetric6", "6", 3, "0"),
("DeveloperSidebarMetric7", "7", 3, "0"),
("DeveloperWidgets", "1", 3, "0"),
("DeveloperUI", "0", 3, "0"),
("DeveloperUI", "1", 3, "0"),
("DeviceManagement", "1", 1, "0"),
("DeviceShutdown", "9", 1, "33"),
("DisableOnroadUploads", "0", 2, "0"),
@@ -218,7 +225,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("Fahrenheit", "0", 3, "0"),
("FavoriteDestinations", "", 0, ""),
("ForceAutoTune", "0", 2, "0"),
("ForceAutoTuneOff", "0", 2, "0"),
("ForceAutoTuneOff", "1", 2, "0"),
("ForceFingerprint", "0", 2, "0"),
("ForceMPHDashboard", "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"),
("FullMap", "0", 2, "0"),
("GasRegenCmd", "1", 2, "0"),
("GMPedalLongitudinal", "1", 2, "1"),
("GithubSshKeys", "", 0, ""),
("GithubUsername", "", 0, ""),
("GoatScream", "0", 1, "0"),
@@ -268,6 +276,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("LongitudinalActuatorDelay", "", 3, ""),
("LongitudinalActuatorDelayStock", "", 3, ""),
("LongitudinalTune", "1", 0, "0"),
("TrailerLoad", "0", 2, "0"),
("LongPitch", "1", 2, "0"),
("LoudBlindspotAlert", "0", 0, "0"),
("LowVoltageShutdown", str(VBATT_PAUSE_CHARGING), 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),
("ModelDrivesAndScores", "", 2, ""),
("ModelRandomizer", "0", 2, "0"),
("ModelReleasedDates", "", 1, ""),
("ModelUI", "1", 2, "0"),
("ModelVersions", "", 2, ""),
("SortModelsByDate", "0", 2, "0"),
("NavigationUI", "1", 1, "0"),
("NavSettingLeftSide", "0", 0, "0"),
("NavSettingTime24h", "0", 0, "0"),
@@ -325,13 +336,14 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("RecordFront", "0", 0, "0"),
("RefuseVolume", "101", 2, "101"),
("RelaxedFollow", "1.75", 2, "1.75"),
("RelaxedFollowHigh", "1.75", 2, "1.75"),
("RelaxedJerkAcceleration", "50", 3, "50"),
("RelaxedJerkDanger", "100", 3, "100"),
("RelaxedJerkDeceleration", "50", 3, "50"),
("RelaxedJerkSpeed", "50", 3, "50"),
("RelaxedJerkSpeedDecrease", "50", 3, "50"),
("RelaxedPersonalityProfile", "1", 2, "0"),
("ReverseCruise", "0", 1, "0"),
("ReverseCruise", "1", 1, "0"),
("RoadEdgesWidth", "2", 2, "2"),
("RoadNameUI", "1", 2, "0"),
("RotatingWheel", "1", 1, "0"),
@@ -355,7 +367,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("ShownToggleDescriptions", "", 0, ""),
("ShowSLCOffset", "1", 0, "0"),
("ShowSpeedLimits", "1", 1, "0"),
("ShowSteering", "0", 3, "0"),
("ShowSteering", "1", 3, "0"),
("ShowStoppingPoint", "0", 2, "0"),
("ShowStoppingPointMetrics", "0", 2, "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"),
("StartupMessageTop", "Hop in and buckle up!", 0, "Be ready to take over at any time"),
("StandardFollow", "1.45", 2, "1.45"),
("StandardFollowHigh", "1.45", 2, "1.45"),
("StandardJerkAcceleration", "50", 3, "50"),
("StandardJerkDanger", "100", 3, "100"),
("StandardJerkDeceleration", "50", 3, "50"),
@@ -396,6 +409,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("SteerDelayStock", "", 3, ""),
("SteerFriction", "", 3, ""),
("SteerFrictionStock", "", 3, ""),
("SteerOffset", "", 3, ""),
("SteerOffsetStock", "", 3, ""),
("SteerKP", "", 3, ""),
("SteerKPStock", "", 3, ""),
("SteerLatAccel", "", 3, ""),
@@ -418,8 +433,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("TrafficJerkSpeed", "50", 3, "50"),
("TrafficJerkSpeedDecrease", "50", 3, "50"),
("TrafficPersonalityProfile", "1", 2, "0"),
("TuningLevel", "0", 0, "0"),
("TuningLevelConfirmed", "0", 0, "0"),
("TuningLevel", "3", 0, "0"),
("TuningLevelConfirmed", "1", 0, "0"),
("TurnDesires", "0", 2, "0"),
("UnlimitedLength", "1", 2, "0"),
("UnlockDoors", "1", 0, "0"),
@@ -437,7 +452,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("WarningSoftVolume", "101", 2, "101"),
("WheelIcon", "frog", 0, "stock"),
("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]] = [
@@ -557,6 +573,7 @@ class FrogPilotVariables:
toggle.has_pedal = CP.enableGasInterceptor
has_radar = not CP.radarUnavailable
toggle.has_sdsu = toggle.car_make == "toyota" and bool(CP.flags & ToyotaFlags.SMART_DSU.value)
toggle.has_sascm = toggle.car_make == "gm" and bool(CP.flags & GMFlags.SASCM.value)
has_sng = CP.autoResumeSng
toggle.has_zss = toggle.car_make == "toyota" and bool(FPCP.fpFlags & ToyotaFrogPilotFlags.ZSS.value)
is_angle_car = CP.steerControlType == car.CarParams.SteerControlType.angle
@@ -567,7 +584,7 @@ class FrogPilotVariables:
startAccel = CP.startAccel
stopAccel = CP.stopAccel
steerActuatorDelay = CP.steerActuatorDelay
steerKp = CP.lateralTuning.pid.kp if CP.lateralTuning.which() == "pid" else KP
steerKp = CP.lateralTuning.torque.kp
steerRatio = CP.steerRatio
toggle.stoppingDecelRate = CP.stoppingDecelRate
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.use_custom_steerActuatorDelay = bool(round(toggle.steerActuatorDelay, 2) != round(steerActuatorDelay, 2))
toggle.friction = np.clip(params.get_float("SteerFriction"), 0, 0.5) if advanced_lateral_tuning and tuning_level >= level["SteerFriction"] else friction
toggle.steer_offset = np.clip(params.get_float("SteerOffset"), -0.2, 0.2) if advanced_lateral_tuning and tuning_level >= level["SteerOffset"] and toggle.car_make == "gm" else 0.0
toggle.use_custom_friction = bool(round(toggle.friction, 2) != round(friction, 2)) and is_torque_car and not toggle.force_auto_tune or toggle.force_auto_tune_off
toggle.steerKp = [[0], [np.clip(params.get_float("SteerKP"), steerKp * 0.5, steerKp * 1.5) if advanced_lateral_tuning and is_torque_car and tuning_level >= level["SteerKP"] else steerKp]]
toggle.latAccelFactor = np.clip(params.get_float("SteerLatAccel"), latAccelFactor * 0.75, latAccelFactor * 1.25) if advanced_lateral_tuning and tuning_level >= level["SteerLatAccel"] else latAccelFactor
@@ -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
advanced_longitudinal_tuning = params.get_bool("AdvancedLongitudinalTune") if tuning_level >= level["AdvancedLongitudinalTune"] else default.get_bool("AdvancedLongitudinalTune")
ev_vehicle = toggle.car_make == "gm" and toggle.car_model != "CHEVROLET_VOLT" and CP.carFingerprint in GM_EV_CAR or toggle.car_make == "hyundai" and CP.carFingerprint in HYUNDAI_EV_CAR
ev_vehicle |= CP.transmissionType == TransmissionType.direct
if params.get("EVTuning") == b"":
params.put_bool("EVTuning", ev_vehicle)
toggle.ev_tuning = params.get_bool("EVTuning") if advanced_longitudinal_tuning and tuning_level >= level["EVTuning"] else ev_vehicle
toggle.longitudinalActuatorDelay = np.clip(params.get_float("LongitudinalActuatorDelay"), 0, 1) if advanced_longitudinal_tuning and tuning_level >= level["LongitudinalActuatorDelay"] else longitudinalActuatorDelay
toggle.startAccel = np.clip(params.get_float("StartAccel"), 0, 4) if advanced_longitudinal_tuning and tuning_level >= level["StartAccel"] else startAccel
toggle.stopAccel = np.clip(params.get_float("StopAccel"), -4, 0) if advanced_longitudinal_tuning and tuning_level >= level["StopAccel"] else stopAccel
@@ -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.recovery_power = np.clip(params.get_float("RecoveryPower"), 0.5, 2.0) if advanced_longitudinal_tuning and tuning_level >= level["RecoveryPower"] else 1.0
toggle.alert_volume_controller = params.get_bool("AlertVolumeControl") if tuning_level >= level["AlertVolumeControl"] else default.get_bool("AlertVolumeControl")
toggle.disengage_volume = params.get_int("DisengageVolume") if toggle.alert_volume_controller and tuning_level >= level["DisengageVolume"] else default.get_int("DisengageVolume")
toggle.engage_volume = params.get_int("EngageVolume") if toggle.alert_volume_controller and tuning_level >= level["EngageVolume"] else default.get_int("EngageVolume")
@@ -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_speed = np.clip(params.get_int("AggressiveJerkSpeed") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkSpeed"] else default.get_int("AggressiveJerkSpeed") / 100
toggle.aggressive_jerk_speed_decrease = np.clip(params.get_int("AggressiveJerkSpeedDecrease") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkSpeedDecrease"] else default.get_int("AggressiveJerkSpeedDecrease") / 100
toggle.aggressive_follow = np.clip(params.get_float("AggressiveFollow"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollow"] else default.get_float("AggressiveFollow")
toggle.aggressive_follow = [np.clip(params.get_float("AggressiveFollow"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollow"] else default.get_float("AggressiveFollow"),
np.clip(params.get_float("AggressiveFollowHigh"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollowHigh"] else default.get_float("AggressiveFollowHigh")]
standard_profile = toggle.custom_personalities and (params.get_bool("StandardPersonalityProfile") if tuning_level >= level["StandardPersonalityProfile"] else default.get_bool("StandardPersonalityProfile"))
toggle.standard_jerk_acceleration = np.clip(params.get_int("StandardJerkAcceleration") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkAcceleration"] else default.get_int("StandardJerkAcceleration") / 100
toggle.standard_jerk_deceleration = np.clip(params.get_int("StandardJerkDeceleration") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkDeceleration"] else default.get_int("StandardJerkDeceleration") / 100
toggle.standard_jerk_danger = np.clip(params.get_int("StandardJerkDanger") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkDanger"] else default.get_int("StandardJerkDanger") / 100
toggle.standard_jerk_speed = np.clip(params.get_int("StandardJerkSpeed") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkSpeed"] else default.get_int("StandardJerkSpeed") / 100
toggle.standard_jerk_speed_decrease = np.clip(params.get_int("StandardJerkSpeedDecrease") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkSpeedDecrease"] else default.get_int("StandardJerkSpeedDecrease") / 100
toggle.standard_follow = np.clip(params.get_float("StandardFollow"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollow"] else default.get_float("StandardFollow")
toggle.standard_follow = [np.clip(params.get_float("StandardFollow"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollow"] else default.get_float("StandardFollow"),
np.clip(params.get_float("StandardFollowHigh"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollowHigh"] else default.get_float("StandardFollowHigh")]
relaxed_profile = toggle.custom_personalities and (params.get_bool("RelaxedPersonalityProfile") if tuning_level >= level["RelaxedPersonalityProfile"] else default.get_bool("RelaxedPersonalityProfile"))
toggle.relaxed_jerk_acceleration = np.clip(params.get_int("RelaxedJerkAcceleration") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkAcceleration"] else default.get_int("RelaxedJerkAcceleration") / 100
toggle.relaxed_jerk_deceleration = np.clip(params.get_int("RelaxedJerkDeceleration") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkDeceleration"] else default.get_int("RelaxedJerkDeceleration") / 100
toggle.relaxed_jerk_danger = np.clip(params.get_int("RelaxedJerkDanger") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkDanger"] else default.get_int("RelaxedJerkDanger") / 100
toggle.relaxed_jerk_speed = np.clip(params.get_int("RelaxedJerkSpeed") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkSpeed"] else default.get_int("RelaxedJerkSpeed") / 100
toggle.relaxed_jerk_speed_decrease = np.clip(params.get_int("RelaxedJerkSpeedDecrease") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkSpeedDecrease"] else default.get_int("RelaxedJerkSpeedDecrease") / 100
toggle.relaxed_follow = np.clip(params.get_float("RelaxedFollow"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollow"] else default.get_float("RelaxedFollow")
toggle.relaxed_follow = [np.clip(params.get_float("RelaxedFollow"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollow"] else default.get_float("RelaxedFollow"),
np.clip(params.get_float("RelaxedFollowHigh"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollowHigh"] else default.get_float("RelaxedFollowHigh")]
traffic_profile = toggle.custom_personalities and (params.get_bool("TrafficPersonalityProfile") if tuning_level >= level["TrafficPersonalityProfile"] else default.get_bool("TrafficPersonalityProfile"))
toggle.traffic_mode_jerk_acceleration = [np.clip(params.get_int("TrafficJerkAcceleration") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkAcceleration"] else default.get_int("TrafficJerkAcceleration") / 100, toggle.aggressive_jerk_acceleration]
toggle.traffic_mode_jerk_deceleration = [np.clip(params.get_int("TrafficJerkDeceleration") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkDeceleration"] else default.get_int("TrafficJerkDeceleration") / 100, toggle.aggressive_jerk_deceleration]
toggle.traffic_mode_jerk_danger = [np.clip(params.get_int("TrafficJerkDanger") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkDanger"] else default.get_int("TrafficJerkDanger") / 100, toggle.aggressive_jerk_danger]
toggle.traffic_mode_jerk_speed = [np.clip(params.get_int("TrafficJerkSpeed") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkSpeed"] else default.get_int("TrafficJerkSpeed") / 100, toggle.aggressive_jerk_speed]
toggle.traffic_mode_jerk_speed_decrease = [np.clip(params.get_int("TrafficJerkSpeedDecrease") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkSpeedDecrease"] else default.get_int("TrafficJerkSpeedDecrease") / 100, toggle.aggressive_jerk_speed_decrease]
toggle.traffic_mode_follow = [np.clip(params.get_float("TrafficFollow"), 0.5, MAX_T_FOLLOW) if traffic_profile and tuning_level >= level["TrafficFollow"] else default.get_float("TrafficFollow"), toggle.aggressive_follow]
toggle.traffic_mode_follow = [np.clip(params.get_float("TrafficFollow"), 0.5, MAX_T_FOLLOW) if traffic_profile and tuning_level >= level["TrafficFollow"] else default.get_float("TrafficFollow"), toggle.aggressive_follow[0]]
custom_ui = params.get_bool("CustomUI") if tuning_level >= level["CustomUI"] else default.get_bool("CustomUI")
toggle.acceleration_path = toggle.openpilot_longitudinal and (custom_ui and (params.get_bool("AccelerationPath") if tuning_level >= level["AccelerationPath"] else default.get_bool("AccelerationPath")) or toggle.debug_mode)
@@ -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.lead_detection_probability = np.clip(params.get_int("LeadDetectionThreshold") / 100, 0.25, 0.50) if longitudinal_tuning and tuning_level >= level["LeadDetectionThreshold"] else default.get_int("LeadDetectionThreshold") / 100
toggle.max_desired_acceleration = np.clip(params.get_float("MaxDesiredAcceleration"), 0.1, 4.0) if longitudinal_tuning and tuning_level >= level["MaxDesiredAcceleration"] else default.get_float("MaxDesiredAcceleration")
toggle.trailer_load_kg = (np.clip(params.get_int("TrailerLoad"), 0, 15000) if longitudinal_tuning and tuning_level >= level["TrailerLoad"] else default.get_int("TrailerLoad")) * CV.LB_TO_KG
toggle.taco_tune = longitudinal_tuning and (params.get_bool("TacoTune") if tuning_level >= level["TacoTune"] else default.get_bool("TacoTune"))
toggle.available_models = params.get("AvailableModels", encoding="utf-8") or ""
toggle.available_model_names = params.get("AvailableModelNames", encoding="utf-8") or ""
toggle.available_model_series = params.get("AvailableModelSeries", encoding="utf-8") or ""
toggle.community_favorites = params.get("CommunityFavorites", encoding="utf-8") or ""
toggle.model_released_dates = params.get("ModelReleasedDates", encoding="utf-8") or ""
toggle.model_versions = params.get("ModelVersions", encoding="utf-8") or ""
toggle.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}*"))]
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:
@@ -859,7 +893,7 @@ class FrogPilotVariables:
toggle.model_version = DEFAULT_MODEL_VERSION
toggle.classic_longitudinal = toggle.model_version in {"v1", "v2", "v3", "v4"}
toggle.classic_model = toggle.model_version in {"v1", "v2", "v3", "v4"}
toggle.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.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.gm_pedal_longitudinal = params.get_bool("GMPedalLongitudinal") if tuning_level >= level["GMPedalLongitudinal"] else default.get_bool("GMPedalLongitudinal")
params_memory.put("FrogPilotToggles", json.dumps(toggle.__dict__))
params_memory.remove("FrogPilotTogglesUpdated")
+1 -1
View File
@@ -87,7 +87,7 @@ class FrogPilotPlanner:
params_memory.remove("LastGPSPosition")
self.lateral_acceleration = v_ego**2 * (sm["carState"].steeringAngleDeg - sm["liveParameters"].angleOffsetDeg) * CV.DEG_TO_RAD / (self.CP.steerRatio * self.CP.wheelbase)
self.lateral_acceleration = v_ego**2 * sm["controlsState"].curvature
check_lane_width = frogpilot_toggles.adjacent_paths or frogpilot_toggles.adjacent_path_metrics or frogpilot_toggles.blind_spot_path or frogpilot_toggles.lane_detection
if check_lane_width and v_ego >= frogpilot_toggles.minimum_lane_change_speed:
@@ -47,14 +47,18 @@ A_CRUISE_MIN_SPORT = A_CRUISE_MIN * 2
# MPH = [0.0, 11, 22, 34, 45, 56, 89]
A_CRUISE_MAX_BP_CUSTOM = [0.0, 5., 10., 15., 20., 25., 40.]
A_CRUISE_MAX_VALS_ECO = [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_ECO_EV = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
A_CRUISE_MAX_VALS_SPORT_EV = [1.25, 1.25, 1.25, 1.25, 1.5, 1.5, 2.0]
A_CRUISE_MAX_VALS_ECO_GAS = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2]
A_CRUISE_MAX_VALS_SPORT_GAS = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6]
def get_max_accel_eco(v_ego):
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_ECO))
def get_max_accel_eco(v_ego, ev_tuning=True):
cruise_vals = A_CRUISE_MAX_VALS_ECO_EV if ev_tuning else A_CRUISE_MAX_VALS_ECO_GAS
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals))
def get_max_accel_sport(v_ego):
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT))
def get_max_accel_sport(v_ego, ev_tuning=True):
cruise_vals = A_CRUISE_MAX_VALS_SPORT_EV if ev_tuning else A_CRUISE_MAX_VALS_SPORT_GAS
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals))
def get_max_accel_low_speeds(max_accel, v_cruise):
return float(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):
eco_gear = sm["frogpilotCarState"].ecoGear
sport_gear = sm["frogpilotCarState"].sportGear
ev_tuning = frogpilot_toggles.ev_tuning
if sm["frogpilotCarState"].trafficModeEnabled:
self.max_accel = get_max_accel(v_ego)
elif frogpilot_toggles.map_acceleration and (eco_gear or sport_gear):
if eco_gear:
self.max_accel = get_max_accel_eco(v_ego)
self.max_accel = get_max_accel_eco(v_ego, ev_tuning)
else:
if frogpilot_toggles.acceleration_profile == 2:
self.max_accel = get_max_accel_sport(v_ego)
self.max_accel = get_max_accel_sport(v_ego, ev_tuning)
else:
self.max_accel = get_max_allowed_accel(v_ego)
else:
if frogpilot_toggles.acceleration_profile == 1:
self.max_accel = get_max_accel_eco(v_ego)
self.max_accel = get_max_accel_eco(v_ego, ev_tuning)
elif frogpilot_toggles.acceleration_profile == 2:
self.max_accel = get_max_accel_sport(v_ego)
self.max_accel = get_max_accel_sport(v_ego, ev_tuning)
elif frogpilot_toggles.acceleration_profile == 3:
self.max_accel = get_max_allowed_accel(v_ego)
else:
@@ -1,11 +1,13 @@
#!/usr/bin/env python3
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.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT
TRAFFIC_MODE_BP = [0., CITY_SPEED_LIMIT]
PERSONALITY_BP = [20. * CV.KPH_TO_MS, 90. * CV.KPH_TO_MS]
class FrogPilotFollowing:
def __init__(self, FrogPilotPlanner):
@@ -48,12 +50,16 @@ class FrogPilotFollowing:
frogpilot_toggles.custom_personalities, sm["controlsState"].personality
)
self.t_follow = get_T_FOLLOW(
t_follow_param = get_T_FOLLOW(
frogpilot_toggles.aggressive_follow,
frogpilot_toggles.standard_follow,
frogpilot_toggles.relaxed_follow,
frogpilot_toggles.custom_personalities, sm["controlsState"].personality
)
if isinstance(t_follow_param, list):
self.t_follow = float(np.interp(v_ego, PERSONALITY_BP, t_follow_param))
else:
self.t_follow = float(t_follow_param)
else:
self.base_acceleration_jerk = 0
self.base_danger_jerk = 0
+1 -1
View File
@@ -51,7 +51,7 @@ class FrogPilotTracking:
self.sound = FrogPilotAudibleAlert.none
self.state = State.disabled
self.model_name = clean_model_name(dict(zip(frogpilot_toggles.available_models.split(","), frogpilot_toggles.available_model_names.split(",")))[frogpilot_toggles.model])
self.model_name = clean_model_name(frogpilot_toggles.model_name)
def update(self, now, time_validated, sm, frogpilot_toggles):
v_cruise = min(sm["controlsState"].vCruiseCluster, V_CRUISE_MAX) * CV.KPH_TO_MS
@@ -13,7 +13,6 @@ from openpilot.common.numpy_fast import interp
from openpilot.common.params import Params
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
from openpilot.selfdrive.controls.lib.latcontrol_torque import KD, KI, KP
from openpilot.selfdrive.controls.lib.pid import PIDController
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.selfdrive.modeld.constants import ModelConstants
@@ -162,7 +161,7 @@ class LatControlNNFF(LatControl):
self.nnff_loaded = self.lat_torque_nn_model is not None
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)
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
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"
MINIMUM_POPULATION = 100_000
SEARCH_RADIUS_DEGREES = 1.45
def get_population_value(population_str):
if population_str is None:
return None
try:
return int(str(population_str).replace(",", "").split(";")[0].strip())
except Exception:
return None
def search_nearby_major_cities(lat, lon, session, state_name, country_name):
viewbox = f"{lon - SEARCH_RADIUS_DEGREES},{lat + SEARCH_RADIUS_DEGREES},{lon + SEARCH_RADIUS_DEGREES},{lat - SEARCH_RADIUS_DEGREES}"
cities = (session.get(f"{BASE_URL}/search", params={
"addressdetails": 1, "bounded": 1, "extratags": 1, "format": "jsonv2", "limit": 20, "q": "city", "viewbox": viewbox
}, timeout=10).json() or [])
qualifying = [c for c in cities if (get_population_value((c.get("extratags") or {}).get("population")) or 0) >= MINIMUM_POPULATION]
if not qualifying:
return None
nearest = min(qualifying, key=lambda c: (float(c["lat"]) - lat) ** 2 + (float(c["lon"]) - lon) ** 2)
addr = nearest.get("address") or {}
return float(nearest["lat"]), float(nearest["lon"]), addr.get("city") or addr.get("town") or nearest.get("display_name", "").split(",")[0], state_name, country_name
def get_city_center(latitude, longitude):
try:
@@ -44,14 +68,7 @@ def get_city_center(latitude, longitude):
if data:
tags = data[0]
population = (tags.get("extratags") or {}).get("population")
population_value = None
if population is not None:
try:
population_value = int(str(population).replace(",", "").split(";")[0].strip())
except Exception:
population_value = None
population_value = get_population_value((tags.get("extratags") or {}).get("population"))
if population_value is not None and population_value >= MINIMUM_POPULATION:
latitude_value = float(tags["lat"])
@@ -62,6 +79,10 @@ def get_city_center(latitude, longitude):
return latitude_value, longitude_value, city_label, state_name, country_name
nearby_result = search_nearby_major_cities(latitude, longitude, session, state_name, country_name)
if nearby_result:
return nearby_result
query = f"{state_name} state capital" if country_code == "us" else f"capital of {state_name}, {country_name}"
response = session.get(f"{BASE_URL}/search", params={"addressdetails": 1, "extratags": 1, "format": "jsonv2", "limit": 5, "q": query}, timeout=10)
response.raise_for_status()
@@ -99,14 +120,14 @@ def get_city_center(latitude, longitude):
def update_branch_commits(now):
points = []
for branch in ["FrogPilot", "FrogPilot-Staging", "FrogPilot-Testing"]:
try:
response = requests.get(f"https://api.github.com/repos/FrogAi/FrogPilot/commits/{branch}")
response.raise_for_status()
sha = response.json()["sha"]
points.append(Point("branch_commits").field("commit", sha).tag("branch", branch).time(now))
except Exception as e:
print(f"Failed to fetch commit for {branch}: {e}")
branch = get_build_metadata().channel # Current running branch
try:
response = requests.get(f"https://api.github.com/repos/firestar5683/StarPilot/commits/{branch}")
response.raise_for_status()
sha = response.json()["sha"]
points.append(Point("branch_commits").field("commit", sha).tag("branch", branch).time(now))
except Exception as e:
print(f"Failed to fetch commit for {branch}: {e}")
return points
@@ -129,11 +150,9 @@ def send_stats():
if frogpilot_toggles.car_make == "mock":
return
bucket = os.environ.get("STATS_BUCKET", "")
org_ID = os.environ.get("STATS_ORG_ID", "")
token = os.environ.get("STATS_TOKEN", "")
url = os.environ.get("STATS_URL", "")
bucket = "StarPilot"
org_ID = "StarPilot"
url = "https://stats.firestar.link"
frogpilot_stats = json.loads(params.get("FrogPilotStats") or "{}")
location = json.loads(params.get("LastGPSPosition") or "{}")
@@ -161,14 +180,19 @@ def send_stats():
user_point = (
Point("user_stats")
.tag("car_make", "GM" if frogpilot_toggles.car_make == "gm" else frogpilot_toggles.car_make.title())
.tag("car_model", frogpilot_toggles.car_model)
.tag("city", city)
.tag("country", country)
.tag("device", HARDWARE.get_device_type())
.tag("driving_model", clean_model_name(frogpilot_toggles.model_name))
.tag("state", state)
.tag("theme", selected_theme.title())
.tag("branch", build_metadata.channel)
.tag("dongle_id", params.get("FrogPilotDongleId", encoding="utf-8"))
.field("blocked_user", frogpilot_toggles.block_user)
.field("car_make", "GM" if frogpilot_toggles.car_make == "gm" else frogpilot_toggles.car_make.title())
.field("car_model", frogpilot_toggles.car_model)
.field("city", city)
.field("country", country)
.field("current_months_kilometers", int(frogpilot_stats.get("CurrentMonthsKilometers", 0)))
.field("device", HARDWARE.get_device_type())
.field("driving_model", clean_model_name(frogpilot_toggles.model_name))
.field("event", 1)
.field("frogpilot_drives", int(frogpilot_stats.get("FrogPilotDrives", 0)))
.field("frogpilot_hours", float(frogpilot_stats.get("FrogPilotSeconds", 0)) / (60 * 60))
@@ -178,13 +202,12 @@ def send_stats():
.field("has_openpilot_longitudinal", frogpilot_toggles.openpilot_longitudinal)
.field("has_pedal", frogpilot_toggles.has_pedal)
.field("has_sdsu", frogpilot_toggles.has_sdsu)
.field("has_sascm", frogpilot_toggles.has_sascm)
.field("has_zss", frogpilot_toggles.has_zss)
.field("latitude", latitude)
.field("longitude", longitude)
.field("rainbow_path", frogpilot_toggles.rainbow_path)
.field("random_events", frogpilot_toggles.random_events)
.field("state", state)
.field("theme", selected_theme.title())
.field("total_aol_seconds", float(frogpilot_stats.get("AOLTime", 0)))
.field("total_lateral_seconds", float(frogpilot_stats.get("LateralTime", 0)))
.field("total_longitudinal_seconds", float(frogpilot_stats.get("LongitudinalTime", 0)))
@@ -193,15 +216,12 @@ def send_stats():
.field("up_to_date", is_up_to_date(build_metadata))
.field("using_stock_acc", not (frogpilot_toggles.has_cc_long or frogpilot_toggles.openpilot_longitudinal))
.tag("branch", build_metadata.channel)
.tag("dongle_id", params.get("FrogPilotDongleId", encoding="utf-8"))
.time(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)
print("Successfully sent FrogPilot stats!")
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,
# 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:
desired = output.get('desired_curvature')
if desired is not None:
return float(desired[0, 0])
plan_out = output['plan'][0]
# Use yaw (index 2) and yaw_rate (index 2)
theta = plan_out[:, Plan.T_FROM_CURRENT_EULER][:, 2]
theta_dot = plan_out[:, Plan.ORIENTATION_RATE][:, 2]
theta = plan[:, Plan.T_FROM_CURRENT_EULER][:, 2]
theta_dot = plan[:, Plan.ORIENTATION_RATE][:, 2]
return float(get_curvature_from_plan(theta, theta_dot, ModelConstants.T_IDXS, v_ego, lat_action_t))
@@ -97,12 +97,12 @@ class Parser:
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
out_shape=(ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH))
if 'plan' in outs:
if outs['plan'].shape[1] == 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH:
self.parse_mdn('plan', outs, in_N=0, out_N=0,
out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
else:
self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION,
out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
plan_mhp = outs['plan'].shape[1] != 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH
plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0)
self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N,
out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
if 'planplus' in outs:
self.parse_mdn('planplus', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
if 'lane_lines' in outs:
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0,
out_shape=(ModelConstants.NUM_LANE_LINES, ModelConstants.IDX_N, ModelConstants.LANE_LINES_WIDTH))
+111 -32
View File
@@ -35,14 +35,17 @@ PROCESS_NAME = "frogpilot.tinygrad_modeld.tinygrad_modeld"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
LAT_SMOOTH_SECONDS = 0.1
LAT_SMOOTH_SECONDS = 0.0
LONG_SMOOTH_SECONDS = 0.3
MIN_LAT_CONTROL_SPEED = 0.3
def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,
lat_action_t: float, long_action_t: float, v_ego: float, 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]
if 'planplus' in model_output:
plan = plan + frogpilot_toggles.recovery_power*model_output['planplus'][0]
cloudlog.error(f"planplus applied: shape {model_output['planplus'].shape}, RECOVERY_POWER {frogpilot_toggles.recovery_power}")
desired_accel, should_stop = get_accel_from_plan_tomb_raider(plan[:,Plan.VELOCITY][:,0],
plan[:,Plan.ACCELERATION][:,0],
ModelConstants.T_IDXS,
@@ -56,7 +59,7 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.
else:
desired_curvature = prev_action.desiredCurvature
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:
desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS)
else:
@@ -81,6 +84,34 @@ class ModelState:
output: np.ndarray
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):
# Dynamically build paths based on current model ID
params = Params()
@@ -99,13 +130,17 @@ class ModelState:
models_dir = Path(__file__).parent / "models"
VISION_PKL_PATH = models_dir / "driving_vision_tinygrad.pkl"
POLICY_PKL_PATH = models_dir / "driving_policy_tinygrad.pkl"
OFF_POLICY_PKL_PATH = models_dir / "driving_off_policy_tinygrad.pkl"
VISION_METADATA_PATH = models_dir / "driving_vision_metadata.pkl"
POLICY_METADATA_PATH = models_dir / "driving_policy_metadata.pkl"
OFF_POLICY_METADATA_PATH = models_dir / "driving_off_policy_metadata.pkl"
else:
VISION_PKL_PATH = model_dir / f"{model_id}_driving_vision_tinygrad.pkl"
POLICY_PKL_PATH = model_dir / f"{model_id}_driving_policy_tinygrad.pkl"
OFF_POLICY_PKL_PATH = model_dir / f"{model_id}_driving_off_policy_tinygrad.pkl"
VISION_METADATA_PATH = model_dir / f"{model_id}_driving_vision_metadata.pkl"
POLICY_METADATA_PATH = model_dir / f"{model_id}_driving_policy_metadata.pkl"
OFF_POLICY_METADATA_PATH = model_dir / f"{model_id}_driving_off_policy_metadata.pkl"
# If ModelVersion is not set or not available, try to determine it from available model data
if not model_version:
@@ -157,7 +192,7 @@ class ModelState:
self.policy_generation = model_version or "v8"
self.is_v11 = (self.policy_generation == "v11")
self.is_v9 = (self.policy_generation == "v9")
self.mlsim = (self.policy_generation in ("v8", "v10", "v11"))
self.mlsim = (self.policy_generation in ("v8", "v10", "v11", "v12"))
self.frames = {name: DrivingModelFrame(context, ModelConstants.TEMPORAL_SKIP) for name in self.vision_input_names}
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
@@ -168,33 +203,50 @@ class ModelState:
# 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)
desire_key_init = next((k for k in self.policy_input_shapes if k.startswith('desire')), None)
if desire_key_init:
self.numpy_inputs[desire_key_init] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32)
if 'traffic_convention' in self.policy_input_shapes:
self.numpy_inputs['traffic_convention'] = np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32)
if 'features_buffer' in self.policy_input_shapes:
self.numpy_inputs['features_buffer'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
# Off-policy model (optional)
self.off_policy_enabled = False
self.off_policy_input_shapes: dict[str, tuple[int, ...]] = {}
self.off_policy_output_slices: dict[str, slice] = {}
self.off_policy_numpy_inputs: dict[str, np.ndarray] = {}
self.off_policy_prev_desired_curv_key: str | None = None
self.off_policy_desire_key: str | None = None
self.off_policy_inputs: dict[str, Tensor] | None = None
self.off_policy_output: np.ndarray | None = None
# Optional inputs for non-v11 (and some v10/v9 variants)
# Lateral control params
if 'lateral_control_params' in self.policy_input_shapes:
self.numpy_inputs['lateral_control_params'] = np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32)
off_policy_metadata = None
if self.policy_generation == "v12" or OFF_POLICY_METADATA_PATH.is_file() or OFF_POLICY_PKL_PATH.is_file():
try:
with open(OFF_POLICY_METADATA_PATH, 'rb') as f:
off_policy_metadata = pickle.load(f)
except FileNotFoundError:
cloudlog.error(f"Missing metadata {OFF_POLICY_METADATA_PATH}, downloading...")
from openpilot.frogpilot.assets.model_manager import ModelManager
ModelManager().download_model(model_id)
try:
with open(OFF_POLICY_METADATA_PATH, 'rb') as f:
off_policy_metadata = pickle.load(f)
except FileNotFoundError:
cloudlog.warning(f"Off-policy metadata still missing: {OFF_POLICY_METADATA_PATH}")
# Previous desired curvature: handle both singular and plural key names across model versions
self.prev_desired_curv_key = None
if 'prev_desired_curv' in self.policy_input_shapes:
self.prev_desired_curv_key = 'prev_desired_curv'
self.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 self.policy_input_shapes:
self.prev_desired_curv_key = 'prev_desired_curvs'
self.numpy_inputs['prev_desired_curvs'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
if off_policy_metadata is not None:
self.off_policy_input_shapes = off_policy_metadata['input_shapes']
self.off_policy_output_slices = off_policy_metadata['output_slices']
off_policy_output_size = off_policy_metadata['output_shapes']['outputs'][1]
self.off_policy_numpy_inputs, self.off_policy_prev_desired_curv_key = self._build_policy_inputs(self.off_policy_input_shapes)
self.off_policy_desire_key = next((k for k in self.off_policy_numpy_inputs if k.startswith('desire')), None)
self.off_policy_inputs = {k: Tensor(v, device='NPY').realize() for k, v in self.off_policy_numpy_inputs.items()}
self.off_policy_output = np.zeros(off_policy_output_size, dtype=np.float32)
try:
with open(OFF_POLICY_PKL_PATH, "rb") as f:
self.off_policy_run = pickle.load(f)
self.off_policy_enabled = True
except FileNotFoundError:
cloudlog.warning(f"Missing off-policy model {OFF_POLICY_PKL_PATH}, skipping off-policy")
# Optional temporal buffer for previous desired curvature (allocate only if the policy expects it)
if getattr(self, 'prev_desired_curv_key', None) is not None:
# Optional temporal buffer for previous desired curvature (allocate only if any model expects it)
if self.prev_desired_curv_key is not None or self.off_policy_prev_desired_curv_key is not None:
self.full_prev_desired_curv = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
@@ -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_output = np.zeros(policy_output_size, dtype=np.float32)
self.parser = Parser()
self.off_policy_parser = Parser(ignore_missing=True)
with open(VISION_PKL_PATH, "rb") as 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] = new_desire
self.numpy_inputs[self.desire_key][:] = self.full_desire.reshape((1,ModelConstants.INPUT_HISTORY_BUFFER_LEN,ModelConstants.TEMPORAL_SKIP,-1)).max(axis=2)
if self.off_policy_enabled and self.off_policy_desire_key is not None:
self.off_policy_numpy_inputs[self.off_policy_desire_key][:] = self.numpy_inputs[self.desire_key]
if 'traffic_convention' in self.numpy_inputs:
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
if self.off_policy_enabled and 'traffic_convention' in self.off_policy_numpy_inputs:
self.off_policy_numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
if 'lateral_control_params' in self.numpy_inputs:
self.numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
if self.off_policy_enabled and 'lateral_control_params' in self.off_policy_numpy_inputs:
self.off_policy_numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
if prepare_only:
return None
@@ -254,7 +315,10 @@ class ModelState:
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.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()
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:
self.numpy_inputs[self.prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
if self.off_policy_enabled and self.off_policy_prev_desired_curv_key is not None:
if self.is_v9:
self.off_policy_numpy_inputs[self.off_policy_prev_desired_curv_key][:] = 0 * self.full_prev_desired_curv[0, self.temporal_idxs]
else:
self.off_policy_numpy_inputs[self.off_policy_prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict}
if self.off_policy_enabled:
self.off_policy_output = self.off_policy_run(**self.off_policy_inputs).contiguous().realize().uop.base.buffer.numpy()
off_policy_outputs_dict = self.off_policy_parser.parse_policy_outputs(
self.slice_outputs(self.off_policy_output, self.off_policy_output_slices)
)
combined_outputs_dict.update(off_policy_outputs_dict)
if SEND_RAW_PRED:
combined_outputs_dict['raw_pred'] = np.concatenate([self.vision_output.copy(), self.policy_output.copy()])
raw_pred = [self.vision_output.copy(), self.policy_output.copy()]
if self.off_policy_enabled and self.off_policy_output is not None:
raw_pred.append(self.off_policy_output.copy())
combined_outputs_dict['raw_pred'] = np.concatenate(raw_pred)
return combined_outputs_dict
@@ -442,7 +521,7 @@ def main(demo=False):
drivingdata_send = messaging.new_message('drivingModelData')
posenet_send = messaging.new_message('cameraOdometry')
action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego, 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
fill_model_msg(drivingdata_send, modelv2_send, model_output, action,
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
@@ -481,4 +560,4 @@ if __name__ == "__main__":
cloudlog.warning(f"child {PROCESS_NAME} got SIGINT")
except Exception:
sentry.capture_exception()
raise
raise
@@ -1,19 +1,74 @@
#include "frogpilot/ui/qt/offroad/expandable_multi_option_dialog.h"
#include <QPushButton>
#include <QButtonGroup>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QScrollBar>
#include <QTimer>
#include <QHBoxLayout>
#include <QSpacerItem>
#include <QLayout>
#include <QLayoutItem>
#include <QGridLayout>
#include <QPoint>
#include <QSize>
#include <QSizePolicy>
#include <QSet>
#include <QVector>
#include <QSignalBlocker>
#include <QScroller>
#include <QPointer>
#include <QObject>
#include <algorithm>
#include "selfdrive/ui/qt/widgets/scrollview.h"
ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_text,
const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent)
: DialogBase(parent), seriesToModels(seriesToModels) {
const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent,
const QStringList &userFavorites,
const QStringList &communityFavorites,
const QMap<QString, QString> &modelReleasedDates,
const QMap<QString, QString> &modelFileToNameMap,
const QString &initialSortMode)
: DialogBase(parent), seriesToModels(seriesToModels), currentSortMode(initialSortMode.isEmpty() ? QString("alphabetical") : initialSortMode),
userFavorites(userFavorites), communityFavorites(communityFavorites), modelReleasedDates(modelReleasedDates),
modelFileToNameMap(modelFileToNameMap), currentSelection(current) {
baseSeriesToModels = seriesToModels;
for (auto it = this->modelFileToNameMap.constBegin(); it != this->modelFileToNameMap.constEnd(); ++it) {
modelNameToFileMap.insert(it.value(), it.key());
}
for (auto it = seriesToModels.constBegin(); it != seriesToModels.constEnd(); ++it) {
const QStringList &models = it.value();
for (const QString &modelName : models) {
if (modelName.isEmpty() || modelNameToFileMap.contains(modelName)) {
continue;
}
this->modelFileToNameMap.insert(modelName, modelName);
modelNameToFileMap.insert(modelName, modelName);
}
}
currentSelectionKey = modelNameToFileMap.value(currentSelection);
if (!currentSelectionKey.isEmpty()) {
selectionKey = currentSelectionKey;
selection = this->modelFileToNameMap.value(currentSelectionKey, currentSelection);
currentSelection = selection;
} else {
selectionKey.clear();
selection.clear();
currentSelection.clear();
}
if (currentSortMode != "alphabetical" && currentSortMode != "date" &&
currentSortMode != "favorites" && currentSortMode != "date_oldest") {
currentSortMode = "alphabetical";
}
QFrame *container = new QFrame(this);
container->setStyleSheet(R"(
@@ -50,6 +105,43 @@ ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_t
padding-left: 80px;
}
QPushButton.series-header:hover { background-color: #404040; }
QPushButton.favorite-button {
background-color: transparent;
border: none;
font-size: 60px;
padding: 0px;
margin: 0px;
min-width: 80px;
max-width: 80px;
}
QPushButton.favorite-button:hover { background-color: #404040; }
QComboBox {
background-color: #4F4F4F;
border: 2px solid transparent;
border-radius: 10px;
padding: 10px;
font-size: 50px;
color: white;
min-width: 200px;
}
QComboBox:hover { background-color: #5A5A5A; }
QComboBox::drop-down {
border: none;
width: 50px;
}
QComboBox::down-arrow {
image: url("../../frogpilot/assets/toggle_icons/icon_dropdown.png");
width: 30px;
height: 30px;
}
QComboBox QAbstractItemView {
background-color: #4F4F4F;
border: 2px solid #FFFFFF;
border-radius: 10px;
color: white;
selection-background-color: #465BEA;
font-size: 50px;
}
)");
QVBoxLayout *main_layout = new QVBoxLayout(container);
@@ -60,85 +152,103 @@ ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_t
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
main_layout->addSpacing(25);
QWidget *listWidget = new QWidget(this);
QVBoxLayout *listLayout = new QVBoxLayout(listWidget);
listLayout->setSpacing(10);
// Sort controls - simple cycling button
QHBoxLayout *sortLayout = new QHBoxLayout();
sortLayout->setContentsMargins(0, 0, 0, 0);
sortLayout->setSpacing(20);
sortLayout->addStretch(); // Push to the right
QButtonGroup *group = new QButtonGroup(listWidget);
group->setExclusive(true);
QLabel *sortLabel = new QLabel(tr("Sort by:"), this);
sortLabel->setStyleSheet("font-size: 50px; color: white;");
sortLayout->addWidget(sortLabel);
QPushButton *confirm_btn = new QPushButton(tr("Select"));
confirm_btn->setObjectName("confirm_btn");
confirm_btn->setEnabled(false);
ScrollView *scroll_view = new ScrollView(listWidget, this);
scroll_view->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
// Create series headers and their expandable content
for (const QString &series : seriesToModels.keys()) {
// Series header button
QPushButton *seriesHeader = new QPushButton("" + series);
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 *sortButton = new QPushButton(tr("Alphabetical"), this);
sortButton->setStyleSheet(R"(
QPushButton {
background-color: #4F4F4F;
border: 2px solid transparent;
border-radius: 10px;
padding: 10px 20px;
font-size: 50px;
color: white;
min-width: 250px;
text-align: center;
}
QPushButton:hover { background-color: #5A5A5A; }
)");
seriesWidgets[series] = seriesContainer;
listLayout->addWidget(seriesContainer);
// Set initial button text based on sort mode
if (currentSortMode == "date") {
sortButton->setText(tr("Date (Newest)"));
} else if (currentSortMode == "date_oldest") {
sortButton->setText(tr("Date (Oldest)"));
} else if (currentSortMode == "favorites") {
sortButton->setText(tr("Favorites First"));
} else {
sortButton->setText(tr("Alphabetical"));
}
// Add stretch to keep buttons spaced correctly
listLayout->addStretch(1);
QWidget *sortWidget = new QWidget(container);
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);
// Cancel + confirm buttons
@@ -148,18 +258,25 @@ ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_t
QPushButton *cancel_btn = new QPushButton(tr("Cancel"));
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(confirm_btn);
blayout->addWidget(confirmButton);
QVBoxLayout *outer_layout = new QVBoxLayout(this);
outer_layout->setContentsMargins(50, 50, 50, 50);
outer_layout->addWidget(container);
// Initial sorting
updateSorting();
}
void ExpandableMultiOptionDialog::toggleSeries(const QString &series, QPushButton *headerButton, 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];
QWidget *container = seriesWidgets[series];
QString seriesName = series;
if (expanded) {
@@ -171,21 +288,18 @@ void ExpandableMultiOptionDialog::toggleSeries(const QString &series, QPushButto
seriesExpanded[series] = true;
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) {
QTimer::singleShot(50, [container, scrollView]() {
QRect containerRect = container->geometry();
QScrollBar *vScrollBar = scrollView->verticalScrollBar();
if (vScrollBar) {
int currentValue = vScrollBar->value();
int containerBottom = containerRect.bottom();
int viewportHeight = scrollView->viewport()->height();
// If container extends beyond viewport, scroll to show it
if (containerBottom > currentValue + viewportHeight) {
int targetValue = containerBottom - viewportHeight + 50; // Add some padding
vScrollBar->setValue(targetValue);
}
QPointer<QPushButton> headerPtr(headerButton);
QPointer<ScrollView> scrollPtr(scrollView);
QTimer::singleShot(50, [headerPtr, scrollPtr]() {
if (!scrollPtr || !headerPtr) return;
QWidget *contents = scrollPtr->widget();
if (!contents) return;
if (QScrollBar *vScrollBar = scrollPtr->verticalScrollBar()) {
QPoint headerTop = headerPtr->mapTo(contents, QPoint(0, 0));
int targetValue = qMax(headerTop.y() - 20, 0);
vScrollBar->setValue(targetValue);
}
});
}
@@ -196,11 +310,457 @@ void ExpandableMultiOptionDialog::toggleSeries(const QString &series, QPushButto
}
QString ExpandableMultiOptionDialog::getSelection(const QString &prompt_text,
const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent) {
ExpandableMultiOptionDialog d = ExpandableMultiOptionDialog(prompt_text, seriesToModels, current, parent);
const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent,
const QStringList &userFavorites,
const QStringList &communityFavorites,
const QMap<QString, QString> &modelReleasedDates,
const QMap<QString, QString> &modelFileToNameMap,
const QString &initialSortMode) {
ExpandableMultiOptionDialog d(prompt_text, seriesToModels, current, parent,
userFavorites, communityFavorites, modelReleasedDates, modelFileToNameMap, initialSortMode);
if (d.exec()) {
return d.selection;
}
return "";
}
}
QStringList ExpandableMultiOptionDialog::getUserFavorites() const {
QStringList filteredFavorites;
for (const QString &fav : userFavorites) {
if (modelFileToNameMap.contains(fav) && !filteredFavorites.contains(fav)) {
filteredFavorites.append(fav);
}
}
return filteredFavorites;
}
void ExpandableMultiOptionDialog::stopActiveScroll() {
if (!scrollView) {
return;
}
if (QScroller *scroller = QScroller::scroller(scrollView->viewport())) {
if (scroller->state() == QScroller::Scrolling) {
scroller->stop();
}
}
}
void ExpandableMultiOptionDialog::stopActiveScrollForInteraction() {
if (!scrollView) {
return;
}
if (QScroller *scroller = QScroller::scroller(scrollView->viewport())) {
const QScroller::State state = scroller->state();
if (state == QScroller::Scrolling || state == QScroller::Dragging || state == QScroller::Pressed) {
scroller->stop();
}
}
}
void ExpandableMultiOptionDialog::createModelButton(const QString &modelKey, const QString &modelName, const QString &displayName,
QVBoxLayout *layout) {
QString effectiveKey = modelKey.isEmpty() ? modelName : modelKey;
if (effectiveKey.isEmpty()) {
return;
}
if (!modelFileToNameMap.contains(effectiveKey)) {
const QString storedName = !modelName.isEmpty() ? modelName : displayName;
modelFileToNameMap.insert(effectiveKey, storedName);
}
if (!modelName.isEmpty()) {
modelNameToFileMap.insert(modelName, effectiveKey);
}
QWidget *modelWidget = new QWidget();
QHBoxLayout *modelLayout = new QHBoxLayout(modelWidget);
modelLayout->setContentsMargins(0, 0, 0, 0);
modelLayout->setSpacing(10);
// Star button
QPushButton *starButton = new QPushButton();
starButton->setProperty("class", "favorite-button");
starButton->setCheckable(true);
starButton->setCursor(Qt::PointingHandCursor);
starButton->setFocusPolicy(Qt::NoFocus);
// Check if this model is a favorite
bool isCommunityFav = communityFavorites.contains(effectiveKey);
bool isUserFav = userFavorites.contains(effectiveKey);
bool isFavorite = isCommunityFav || isUserFav;
starButton->setChecked(isFavorite);
starButton->setText(isFavorite ? QString::fromUtf16(u"\u2665") : QString::fromUtf16(u"\u2661"));
QObject::connect(starButton, &QPushButton::clicked, [this, effectiveKey]() {
stopActiveScrollForInteraction();
toggleFavorite(effectiveKey);
});
favoriteButtons[effectiveKey].append(starButton);
modelLayout->addWidget(starButton);
// Model button
QPushButton *modelButton = new QPushButton(displayName);
modelButton->setCheckable(true);
modelButton->setProperty("class", "model-option");
modelButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
modelButton->setCursor(Qt::PointingHandCursor);
modelButton->setFocusPolicy(Qt::NoFocus);
modelButton->setProperty("modelKey", effectiveKey);
modelButton->setProperty("modelName", modelName);
modelButtons[effectiveKey].append(modelButton);
if (selectionKey == effectiveKey && currentSelectionButton.isNull()) {
currentSelectionButton = modelButton;
}
modelLayout->addWidget(modelButton);
const QString resolvedSelection = modelFileToNameMap.value(effectiveKey, !modelName.isEmpty() ? modelName : displayName);
QObject::connect(modelButton, &QPushButton::clicked, this, [this, effectiveKey, modelButton, resolvedSelection]() {
stopActiveScrollForInteraction();
selectionKey = effectiveKey;
currentSelectionKey = effectiveKey;
selection = resolvedSelection;
currentSelection = resolvedSelection;
currentSelectionButton = modelButton;
if (confirmButton) {
confirmButton->setEnabled(true);
}
updateButtonStyles();
});
layout->addWidget(modelWidget);
}
void ExpandableMultiOptionDialog::toggleFavorite(const QString &modelKey) {
// Update local state
if (modelKey.isEmpty()) {
return;
}
if (userFavorites.contains(modelKey)) {
userFavorites.removeAll(modelKey);
} else {
userFavorites.append(modelKey);
}
updateSorting();
}
void ExpandableMultiOptionDialog::updateSorting() {
const QString favoritesSeriesName = QStringLiteral("♥ Favorites");
QMap<QString, QStringList> newSeriesToModels;
QStringList orderedSeries;
QSet<QString> validSeries;
QSet<QString> favoriteModelKeys;
QSet<QString> availableModelKeys;
displayOverrides.clear();
const bool sortByDate = (currentSortMode == "date" || currentSortMode == "date_oldest");
const bool sortDateNewestFirst = (currentSortMode == "date");
for (auto it = baseSeriesToModels.constBegin(); it != baseSeriesToModels.constEnd(); ++it) {
const QStringList &models = it.value();
for (const QString &modelName : models) {
const QString modelKey = modelNameToFileMap.value(modelName, modelName);
if (!modelKey.isEmpty()) {
availableModelKeys.insert(modelKey);
}
}
}
if (currentSortMode == "favorites") {
QStringList favoritesList;
for (const QString &modelKey : communityFavorites) {
if (availableModelKeys.contains(modelKey)) {
const QString modelName = modelFileToNameMap.value(modelKey);
favoritesList.append(modelName);
favoriteModelKeys.insert(modelKey);
displayOverrides.insert(modelKey, tr("%1 (Community Fav)").arg(modelName));
}
}
for (const QString &modelKey : userFavorites) {
if (availableModelKeys.contains(modelKey) && !favoriteModelKeys.contains(modelKey)) {
favoritesList.append(modelFileToNameMap.value(modelKey));
favoriteModelKeys.insert(modelKey);
}
}
if (!favoritesList.isEmpty()) {
std::sort(favoritesList.begin(), favoritesList.end());
newSeriesToModels.insert(favoritesSeriesName, favoritesList);
orderedSeries.append(favoritesSeriesName);
validSeries.insert(favoritesSeriesName);
seriesExpanded.insert(favoritesSeriesName, true);
} else {
seriesExpanded.remove(favoritesSeriesName);
}
} else {
seriesExpanded.remove(favoritesSeriesName);
}
struct SeriesInfo {
QString name;
QStringList models;
QString newestDate;
QString oldestDate;
};
QVector<SeriesInfo> seriesInfos;
for (auto it = baseSeriesToModels.constBegin(); it != baseSeriesToModels.constEnd(); ++it) {
QString series = it.key();
QStringList models = it.value();
if (sortByDate) {
std::sort(models.begin(), models.end(), [this, sortDateNewestFirst](const QString &a, const QString &b) {
QString keyA = modelNameToFileMap.value(a, a);
QString keyB = modelNameToFileMap.value(b, b);
QString dateA = modelReleasedDates.value(keyA, QStringLiteral("1970-01-01"));
QString dateB = modelReleasedDates.value(keyB, QStringLiteral("1970-01-01"));
if (dateA == dateB) {
return a < b;
}
return sortDateNewestFirst ? (dateA > dateB) : (dateA < dateB);
});
} else {
std::sort(models.begin(), models.end());
}
if (currentSortMode == "favorites" && !favoriteModelKeys.isEmpty()) {
QStringList filteredModels;
for (const QString &modelName : models) {
QString key = modelNameToFileMap.value(modelName, modelName);
if (!favoriteModelKeys.contains(key)) {
filteredModels.append(modelName);
}
}
models = filteredModels;
}
if (models.isEmpty()) {
continue;
}
QString newestDate = QStringLiteral("1970-01-01");
QString oldestDate = QStringLiteral("1970-01-01");
bool hasDate = false;
for (const QString &modelName : models) {
const QString key = modelNameToFileMap.value(modelName, modelName);
const QString date = modelReleasedDates.value(key, QStringLiteral("1970-01-01"));
if (!hasDate) {
newestDate = date;
oldestDate = date;
hasDate = true;
} else {
if (date > newestDate) {
newestDate = date;
}
if (date < oldestDate) {
oldestDate = date;
}
}
}
if (!hasDate) {
oldestDate = QStringLiteral("1970-01-01");
}
seriesInfos.push_back({series, models, newestDate, oldestDate});
newSeriesToModels.insert(series, models);
}
if (sortByDate) {
std::sort(seriesInfos.begin(), seriesInfos.end(), [sortDateNewestFirst](const SeriesInfo &a, const SeriesInfo &b) {
if (sortDateNewestFirst) {
if (a.newestDate == b.newestDate) {
return a.name < b.name;
}
return a.newestDate > b.newestDate;
} else {
if (a.oldestDate == b.oldestDate) {
return a.name < b.name;
}
return a.oldestDate < b.oldestDate;
}
});
} else {
std::sort(seriesInfos.begin(), seriesInfos.end(), [](const SeriesInfo &a, const SeriesInfo &b) {
return a.name < b.name;
});
}
for (const SeriesInfo &info : seriesInfos) {
orderedSeries.append(info.name);
validSeries.insert(info.name);
}
for (auto it = seriesExpanded.begin(); it != seriesExpanded.end(); ) {
if (!validSeries.contains(it.key())) {
it = seriesExpanded.erase(it);
} else {
++it;
}
}
rebuildModelList(orderedSeries, newSeriesToModels);
refreshFavoriteIcons();
}
void ExpandableMultiOptionDialog::rebuildModelList(const QStringList &orderedSeries, const QMap<QString, QStringList> &newSeriesToModels) {
if (!listLayout) return;
stopActiveScroll();
while (QLayoutItem *item = listLayout->takeAt(0)) {
if (QWidget *w = item->widget()) {
delete w;
} else if (QLayout *layout = item->layout()) {
delete layout;
}
delete item;
}
seriesWidgets.clear();
modelButtons.clear();
favoriteButtons.clear();
currentSelectionButton = nullptr;
for (const QString &series : orderedSeries) {
const QStringList models = newSeriesToModels.value(series);
if (models.isEmpty()) {
continue;
}
QPushButton *seriesHeader = new QPushButton("" + series);
seriesHeader->setProperty("class", "series-header");
seriesHeader->setCheckable(false);
bool expanded = seriesExpanded.value(series, false);
seriesExpanded.insert(series, expanded);
QObject::connect(seriesHeader, &QPushButton::clicked, [this, series, seriesHeader]() {
toggleSeries(series, seriesHeader);
});
QWidget *seriesContainer = new QWidget();
QVBoxLayout *seriesLayout = new QVBoxLayout(seriesContainer);
seriesLayout->setContentsMargins(20, 0, 0, 0);
seriesLayout->setSpacing(10);
for (const QString &modelName : models) {
QString modelKey = modelNameToFileMap.value(modelName, modelName);
if (!modelFileToNameMap.contains(modelKey)) {
modelFileToNameMap.insert(modelKey, modelName);
}
QString displayName = displayOverrides.value(modelKey, modelName);
createModelButton(modelKey, modelName, displayName, seriesLayout);
}
if (expanded) {
seriesContainer->show();
seriesHeader->setText("" + series);
} else {
seriesContainer->hide();
seriesHeader->setText("" + series);
}
seriesWidgets.insert(series, seriesContainer);
listLayout->addWidget(seriesHeader);
listLayout->addWidget(seriesContainer);
}
listLayout->addStretch(1);
seriesToModels = newSeriesToModels;
listWidgetContainer->updateGeometry();
listWidgetContainer->adjustSize();
if (scrollView && scrollView->widget()) {
scrollView->widget()->updateGeometry();
scrollView->widget()->adjustSize();
}
updateButtonStyles();
}
void ExpandableMultiOptionDialog::refreshFavoriteIcons() {
for (auto it = favoriteButtons.begin(); it != favoriteButtons.end(); ++it) {
const QString &modelKey = it.key();
const QList<QPushButton*> &buttons = it.value();
bool isCommunityFav = communityFavorites.contains(modelKey);
bool isUserFav = userFavorites.contains(modelKey);
bool isFavorite = isCommunityFav || isUserFav;
for (QPushButton *button : buttons) {
if (!button) continue;
button->setChecked(isFavorite);
button->setText(isFavorite ? QString::fromUtf16(u"\u2665") : QString::fromUtf16(u"\u2661"));
}
}
if (confirmButton && !selectionKey.isEmpty()) {
confirmButton->setEnabled(true);
}
updateButtonStyles();
}
void ExpandableMultiOptionDialog::updateButtonStyles() {
const QString selectedKey = selectionKey;
const QString selectedStyle = QStringLiteral(
"QPushButton {"
"background-color: #465BEA;"
"border: 3px solid #FFFFFF;"
"color: white;"
"font-weight: 500;"
"height: 135;"
"padding: 0px 50px;"
"text-align: left;"
"font-size: 55px;"
"border-radius: 10px;"
"}");
if (selectedKey.isEmpty()) {
currentSelectionButton = nullptr;
}
QPushButton *explicitButton = currentSelectionButton.data();
if (explicitButton && explicitButton->property("modelKey").toString() != selectedKey) {
explicitButton = nullptr;
}
for (auto it = modelButtons.begin(); it != modelButtons.end(); ++it) {
const QString &modelKey = it.key();
const QList<QPushButton*> &buttons = it.value();
const bool keyMatches = (!selectedKey.isEmpty() && modelKey == selectedKey);
bool activatedForKey = false;
for (QPushButton *button : buttons) {
if (!button) continue;
bool isActive = false;
if (explicitButton) {
isActive = (button == explicitButton);
} else if (keyMatches && !activatedForKey) {
isActive = true;
activatedForKey = true;
currentSelectionButton = button;
}
QSignalBlocker blocker(button);
button->setChecked(isActive);
button->setStyleSheet(isActive ? selectedStyle : QString());
}
}
}
@@ -6,23 +6,71 @@
#include <QWidget>
#include <QMap>
#include <QList>
#include <QPointer>
#include <QComboBox>
#include <QMenu>
#include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
class QPushButton;
class ExpandableMultiOptionDialog : public DialogBase {
Q_OBJECT
public:
explicit ExpandableMultiOptionDialog(const QString &prompt_text, const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent);
const QString &current, QWidget *parent,
const QStringList &userFavorites = QStringList(),
const QStringList &communityFavorites = QStringList(),
const QMap<QString, QString> &modelReleasedDates = QMap<QString, QString>(),
const QMap<QString, QString> &modelFileToNameMap = QMap<QString, QString>(),
const QString &initialSortMode = "alphabetical");
static QString getSelection(const QString &prompt_text, const QMap<QString, QStringList> &seriesToModels,
const QString &current, QWidget *parent);
const QString &current, QWidget *parent,
const QStringList &userFavorites = QStringList(),
const QStringList &communityFavorites = QStringList(),
const QMap<QString, QString> &modelReleasedDates = QMap<QString, QString>(),
const QMap<QString, QString> &modelFileToNameMap = QMap<QString, QString>(),
const QString &initialSortMode = QString());
QString selection;
QString getCurrentSortMode() const { return currentSortMode; }
QStringList getUserFavorites() const;
private:
void toggleSeries(const QString &series, QPushButton *headerButton, 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> baseSeriesToModels;
QMap<QString, QWidget*> seriesWidgets;
QMap<QString, bool> seriesExpanded;
};
QMap<QString, QList<QPushButton*>> modelButtons;
QMap<QString, QList<QPushButton*>> favoriteButtons;
QStringList userFavorites;
QStringList communityFavorites;
QMap<QString, QString> modelReleasedDates;
QMap<QString, QString> modelFileToNameMap;
QMap<QString, QString> modelNameToFileMap;
QMap<QString, QString> displayOverrides;
QString currentSortMode;
QString currentSelection;
QString currentSelectionKey;
QString selectionKey;
ScrollView *scrollView = nullptr;
QVBoxLayout *listLayout = nullptr;
QPushButton *confirmButton = nullptr;
QWidget *listWidgetContainer = nullptr;
QPointer<QPushButton> currentSelectionButton;
};
@@ -269,6 +269,7 @@ void FrogPilotSettingsWindow::updateVariables() {
hasPedal = CP.getEnableGasInterceptor();
hasRadar = !CP.getRadarUnavailable();
hasSDSU = frogpilot_toggles.value("has_sdsu").toBool();
hasSASCM = frogpilot_toggles.value("has_sascm").toBool();
hasSNG = hasOpenpilotLongitudinal && CP.getAutoResumeSng();
hasZSS = frogpilot_toggles.value("has_zss").toBool();
isAngleCar = CP.getSteerControlType() == cereal::CarParams::SteerControlType::ANGLE;
@@ -284,6 +285,7 @@ void FrogPilotSettingsWindow::updateVariables() {
longitudinalActuatorDelay = CP.getLongitudinalActuatorDelay();
startAccel = CP.getStartAccel();
steerActuatorDelay = CP.getSteerActuatorDelay();
steerOffset = 0.0f;
steerKp = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::PID ? CP.getLateralTuning().getPid().getKpV()[0] : 0.6;
steerRatio = CP.getSteerRatio();
stopAccel = CP.getStopAccel();
@@ -295,6 +297,7 @@ void FrogPilotSettingsWindow::updateVariables() {
float currentDelayStock = params.getFloat("SteerDelayStock");
float currentFrictionStock = params.getFloat("SteerFrictionStock");
float currentSteerOffsetStock = params.getFloat("SteerOffsetStock");
float currentKPStock = params.getFloat("SteerKPStock");
float currentLatAccelStock = params.getFloat("SteerLatAccelStock");
float currentLongDelayStock = params.getFloat("LongitudinalActuatorDelayStock");
@@ -319,6 +322,13 @@ void FrogPilotSettingsWindow::updateVariables() {
params.putFloat("SteerFrictionStock", friction);
}
if (currentSteerOffsetStock != steerOffset) {
if (params.getFloat("SteerOffset") == currentSteerOffsetStock) {
params.putFloat("SteerOffset", steerOffset);
}
params.putFloat("SteerOffsetStock", steerOffset);
}
if (currentKPStock != steerKp && steerKp != 0) {
if (params.getFloat("SteerKP") == currentKPStock || currentKPStock == 0) {
params.putFloat("SteerKP", steerKp);
@@ -391,6 +401,7 @@ void FrogPilotSettingsWindow::updateVariables() {
canUsePedal = FPCP.getCanUsePedal();
canUseSDSU = FPCP.getCanUseSDSU();
canUseSASCM = FPCP.getCanUseSASCM();
openpilotLongitudinalControlDisabled = FPCP.getOpenpilotLongitudinalControlDisabled();
}
@@ -13,6 +13,7 @@ public:
bool canUsePedal = false;
bool canUseSDSU = false;
bool canUseSASCM = false;
bool forceOpenDescriptions = false;
bool hasAutoTune = true;
bool hasBSM = true;
@@ -24,6 +25,7 @@ public:
bool hasPedal = false;
bool hasRadar = true;
bool hasSDSU = false;
bool hasSASCM = false;
bool hasSNG = false;
bool hasZSS = false;
bool isAngleCar = false;
@@ -45,6 +47,7 @@ public:
float longitudinalActuatorDelay;
float startAccel;
float steerActuatorDelay;
float steerOffset;
float steerKp;
float steerRatio;
float stopAccel;
+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"},
{"SteerDelay", parent->steerActuatorDelay != 0 ? QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(parent->steerActuatorDelay, 'f', 2)) : tr("Actuator Delay"), tr("<b>The time between openpilot's steering command and the vehicle's response.</b> Increase if the vehicle reacts late; decrease if it feels jumpy. Auto-learned by default."), ""},
{"SteerFriction", parent->friction != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(parent->friction, 'f', 2)) : tr("Friction"), tr("<b>Compensates for steering friction.</b> Increase if the wheel sticks near center; decrease if it jitters. Auto-learned by default."), ""},
{"SteerOffset", parent->steerOffset != 0 ? QString(tr("Steer Offset (Default: %1)")).arg(QString::number(parent->steerOffset, 'f', 3)) : tr("Steer Offset"), tr("<b>Offsets steering torque to help compensate for alignment or tire issues.</b> More negative pulls the car right; more positive pulls it left. Most users should not need to touch this."), ""},
{"SteerKP", parent->steerKp != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2)) : tr("Kp Factor"), tr("<b>How strongly openpilot corrects lane position.</b> Higher is tighter but twitchier; lower is smoother but slower. Auto-learned by default."), ""},
{"SteerLatAccel", parent->latAccelFactor != 0 ? QString(tr("Lateral Acceleration (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2)) : tr("Lateral Acceleration"), tr("<b>Maps steering torque to turning response.</b> Increase for sharper turns; decrease for gentler steering. Auto-learned by default."), ""},
{"SteerRatio", parent->steerRatio != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(parent->steerRatio, 'f', 2)) : tr("Steer Ratio"), tr("<b>The relationship between steering wheel rotation and road wheel angle.</b> Increase if steering feels too quick or twitchy; decrease if it feels too slow or weak. Auto-learned by default."), ""},
@@ -87,6 +88,9 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
} else if (param == "SteerFriction") {
std::vector<QString> steerFrictionButton{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 0.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerFrictionButton, false, false);
} else if (param == "SteerOffset") {
std::vector<QString> steerOffsetButton{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, -0.2, 0.2, QString(), std::map<float, QString>(), 0.005, false, {}, steerOffsetButton, false, false);
} else if (param == "SteerKP") {
std::vector<QString> steerKPButton{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 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"]);
QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, 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)));
steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(parent->friction, 'f', 2)));
steerOffsetToggle->setTitle(QString(tr("Steer Offset (Default: %1)")).arg(QString::number(parent->steerOffset, 'f', 3)));
steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2)));
steerKPToggle->updateControl(parent->steerKp * 0.5, parent->steerKp * 1.5);
steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2)));
@@ -340,7 +353,6 @@ void FrogPilotLateralPanel::updateToggles() {
}
}
bool forcingAutoTune = !parent->hasAutoTune && params.getBool("ForceAutoTune");
bool forcingAutoTuneOff = parent->hasAutoTune && params.getBool("ForceAutoTuneOff");
bool forcingTorqueController = !parent->isAngleCar && params.getBool("ForceTorqueController");
bool usingNNFF = parent->hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
@@ -401,7 +413,13 @@ void FrogPilotLateralPanel::updateToggles() {
else if (key == "SteerFriction") {
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 &= !usingNNFF;
}
@@ -413,14 +431,14 @@ void FrogPilotLateralPanel::updateToggles() {
else if (key == "SteerLatAccel") {
setVisible &= parent->latAccelFactor != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= !usingNNFF;
}
else if (key == "SteerRatio") {
setVisible &= parent->steerRatio != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
}
toggle->setVisible(setVisible);
+3 -1
View File
@@ -31,6 +31,7 @@ private:
float friction;
float latAccelFactor;
float steerActuatorDelay;
float steerOffset;
float steerKp;
float steerRatio;
@@ -38,7 +39,7 @@ private:
std::map<QString, AbstractControl*> toggles;
QSet<QString> advancedLateralTuneKeys = {"ForceAutoTune", "ForceAutoTuneOff", "ForceTorqueController", "SteerDelay", "SteerFriction", "SteerLatAccel", "SteerKP", "SteerRatio"};
QSet<QString> advancedLateralTuneKeys = {"ForceAutoTune", "ForceAutoTuneOff", "ForceTorqueController", "SteerDelay", "SteerFriction", "SteerOffset", "SteerLatAccel", "SteerKP", "SteerRatio"};
QSet<QString> aolKeys = {"AlwaysOnLateralLKAS", "AlwaysOnLateralMain", "PauseAOLOnBrake"};
QSet<QString> laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"};
QSet<QString> lateralTuneKeys = {"NNFF", "NNFFLite", "TurnDesires"};
@@ -48,6 +49,7 @@ private:
FrogPilotParamValueButtonControl *steerDelayToggle;
FrogPilotParamValueButtonControl *steerFrictionToggle;
FrogPilotParamValueButtonControl *steerOffsetToggle;
FrogPilotParamValueButtonControl *steerLatAccelToggle;
FrogPilotParamValueButtonControl *steerKPToggle;
FrogPilotParamValueButtonControl *steerRatioToggle;
File diff suppressed because it is too large Load Diff
@@ -40,19 +40,19 @@ private:
std::map<QString, AbstractControl*> toggles;
QSet<QString> advancedLongitudinalTuneKeys = {"LongitudinalActuatorDelay", "StartAccel", "StopAccel", "StoppingDecelRate", "VEgoStarting", "VEgoStopping"};
QSet<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"};
QSet<QString> advancedLongitudinalTuneKeys = {"EVTuning", "LongitudinalActuatorDelay", "StartAccel", "StopAccel", "StoppingDecelRate", "VEgoStarting", "VEgoStopping"};
QSet<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveFollowHigh", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"};
QSet<QString> conditionalExperimentalKeys = {"CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CENavigation", "CESignalSpeed", "ShowCEMStatus"};
QSet<QString> curveSpeedKeys = {"CalibratedLateralAcceleration", "CalibrationProgress", "ResetCurveData", "ShowCSCStatus"};
QSet<QString> customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"};
QSet<QString> longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TacoTune"};
QSet<QString> longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TrailerLoad", "TacoTune"};
QSet<QString> qolKeys = {"CustomCruise", "CustomCruiseLong", "ForceStops", "IncreasedStoppedDistance", "MapGears", "ReverseCruise", "SetSpeedOffset"};
QSet<QString> relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"};
QSet<QString> relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedFollowHigh", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"};
QSet<QString> speedLimitControllerKeys = {"SLCOffsets", "SLCFallback", "SLCOverride", "SLCPriority", "SLCQOL", "SLCVisuals"};
QSet<QString> speedLimitControllerOffsetsKeys = {"Offset1", "Offset2", "Offset3", "Offset4", "Offset5", "Offset6", "Offset7"};
QSet<QString> speedLimitControllerQOLKeys = {"ForceMPHDashboard", "SetSpeedLimit", "SLCConfirmation", "SLCLookaheadHigher", "SLCLookaheadLower", "SLCMapboxFiller"};
QSet<QString> speedLimitControllerVisualKeys = {"ShowSLCOffset", "SpeedLimitSources"};
QSet<QString> standardPersonalityKeys = {"StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"};
QSet<QString> standardPersonalityKeys = {"StandardFollow", "StandardFollowHigh", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"};
QSet<QString> trafficPersonalityKeys = {"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", "ResetTrafficPersonality"};
QSet<QString> parentKeys;
+319 -222
View File
@@ -1,10 +1,13 @@
#include "frogpilot/ui/qt/offroad/model_settings.h"
#include "frogpilot/ui/qt/offroad/expandable_multi_option_dialog.h"
#include <QFile>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDoubleSpinBox>
#include <QPushButton>
#include <QDialog>
#include <algorithm>
FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
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."), ""},
{"DownloadModel", tr("Download Driving Models"), tr("Download driving models to the device."), ""},
{"ModelRandomizer", tr("Model Randomizer"), tr("Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs."), ""},
{"RecoveryPower", tr("Recovery Power"), tr("Adjust the strength of planplus lane recovery corrections (0.5 to 2.0)."), ""},
{"StopDistance", tr("Stop Distance"), tr("Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6."), ""},
{"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("Add or remove models from the <b>Model Randomizer</b>'s blacklist list."), ""},
{"ManageScores", tr("Manage Model Ratings"), tr("Reset or view the saved ratings for the driving models."), ""},
{"SelectModel", tr("Select Driving Model"), tr("Select the active driving model."), ""},
};
FrogPilotParamValueButtonControl *recoveryPowerToggle = nullptr;
FrogPilotParamValueButtonControl *stopDistanceToggle = nullptr;
for (const auto &[param, title, desc, icon] : modelToggles) {
@@ -41,56 +46,72 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
if (param == "DeleteModel") {
deleteModelButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DELETE"), tr("DELETE ALL")});
QObject::connect(deleteModelButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
QStringList deletableModels;
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
if (base.startsWith(modelKey)) {
QString modelName = modelFileToNameMapProcessed.value(modelKey);
if (!deletableModels.contains(modelName)) {
deletableModels.append(modelName);
}
}
}
QMap<QString, QString> deletableModelsMap = getDeletableModelDisplayNames();
noModelsDownloaded = deletableModelsMap.isEmpty();
if (noModelsDownloaded) {
return;
}
deletableModels.removeAll(processModelName(currentModel));
deletableModels.removeAll(modelFileToNameMapProcessed.value(QString::fromStdString(params_default.get("Model"))));
deletableModels.removeAll("Space Lab");
noModelsDownloaded = deletableModels.isEmpty();
if (id == 0) {
// Group deletable models by series
// Group deletable models by series and keep a lookup for selected names
QMap<QString, QStringList> deletableSeriesToModels;
for (const QString &modelName : deletableModels) {
QString modelKey = modelFileToNameMapProcessed.key(modelName);
QString series = modelSeriesMap.value(modelKey, "Custom Series");
deletableSeriesToModels[series].append(modelName);
QMap<QString, QString> displayNameToKey;
QMap<QString, QString> deletableFileToNameMap;
for (auto it = deletableModelsMap.constBegin(); it != deletableModelsMap.constEnd(); ++it) {
const QString &modelKey = it.key();
const QString &displayName = it.value();
QString series = modelSeriesMap.value(modelKey, tr("Custom Series"));
deletableSeriesToModels[series].append(displayName);
displayNameToKey.insert(displayName, modelKey);
deletableFileToNameMap.insert(modelKey, displayName);
}
// Sort models within each series
for (QString &series : deletableSeriesToModels.keys()) {
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);
if (!modelToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the \"%1\" model?").arg(modelToDelete), tr("Delete"), this)) {
QString modelFile = modelFileToNameMapProcessed.key(modelToDelete);
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
if (base.startsWith(modelFile)) {
QFile::remove(modelDir.filePath(file));
QString savedSortMode = QString::fromStdString(params.get("ModelSortMode"));
if (savedSortMode.isEmpty()) savedSortMode = "alphabetical";
QString modelToDelete = ExpandableMultiOptionDialog::getSelection(tr("Select a driving model to delete"), deletableSeriesToModels, "", this,
QStringList(), QStringList(), QMap<QString, QString>(),
deletableFileToNameMap, savedSortMode);
if (!modelToDelete.isEmpty()) {
QString modelKey = displayNameToKey.value(modelToDelete);
if (modelKey.isEmpty()) {
QString processedName = processModelName(modelToDelete);
for (auto it = deletableModelsMap.constBegin(); it != deletableModelsMap.constEnd(); ++it) {
if (processModelName(it.value()) == processedName) {
modelKey = it.key();
break;
}
}
}
allModelsDownloaded = false;
if (!modelKey.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the \"%1\" model?").arg(modelToDelete), tr("Delete"), this)) {
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
if (base.startsWith(modelKey)) {
QFile::remove(modelDir.filePath(file));
}
}
allModelsDownloaded = false;
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
}
}
} else if (id == 1) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete all of your downloaded driving models?"), tr("Delete"), this)) {
const QList<QString> deletableKeys = deletableModelsMap.keys();
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
QString modelName = modelFileToNameMapProcessed.value(modelKey);
if (deletableModels.contains(modelName) && base.startsWith(modelKey)) {
for (const QString &modelKey : deletableKeys) {
if (base.startsWith(modelKey)) {
QFile::remove(modelDir.filePath(file));
break;
}
@@ -99,6 +120,7 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
allModelsDownloaded = false;
noModelsDownloaded = true;
deleteModelButton->setEnabled(false);
}
}
});
@@ -106,66 +128,70 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
} else if (param == "DownloadModel") {
downloadModelButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DOWNLOAD"), tr("DOWNLOAD ALL")});
QObject::connect(downloadModelButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
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 (modelDownloading) {
params_memory.putBool("CancelModelDownload", true);
cancellingDownload = true;
} else {
QStringList downloadableModels = availableModelNames;
for (const QString &modelKey : modelFileToNameMap.keys()) {
QString modelName = modelFileToNameMap.value(modelKey);
if (isInstalled(modelKey)) {
downloadableModels.removeAll(modelName);
}
}
downloadableModels.removeAll("Space Lab 👀📡");
allModelsDownloaded = downloadableModels.isEmpty();
} else {
QMap<QString, QStringList> downloadableSeriesToModels;
QStringList downloadableModelNames;
// Group downloadable models by series
QMap<QString, QStringList> downloadableSeriesToModels;
for (const QString &modelName : downloadableModels) {
QString modelKey = modelFileToNameMap.key(modelName);
QString series = modelSeriesMap.value(modelKey, "Custom Series");
downloadableSeriesToModels[series].append(modelName);
for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
const QString &modelKey = it.key();
const QString &modelName = it.value();
if (modelName.isEmpty() || isModelInstalled(modelKey)) {
continue;
}
// Sort models within each series
for (QString &series : downloadableSeriesToModels.keys()) {
downloadableSeriesToModels[series].sort();
QString series = modelSeriesMap.value(modelKey, tr("Custom Series"));
downloadableSeriesToModels[series].append(modelName);
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()) {
QString modelKey = modelFileToNameMap.key(modelToDownload);
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) {
@@ -308,57 +335,32 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
} else if (param == "SelectModel") {
selectModelButton = new ButtonControl(title, tr("SELECT"), desc);
QObject::connect(selectModelButton, &ButtonControl::clicked, [this]() {
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;
// Group models by series for the enhanced dialog
QMap<QString, QStringList> seriesToModels;
QMap<QString, QString> installedModelFileToNameMap;
QMap<QString, QString> installedReleasedDates;
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;
}
// Add all available models by series
for (const QString &modelKey : modelFileToNameMap.keys()) {
if (!isModelInstalled(modelKey)) {
continue;
}
// 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);
if (modelName.contains("(Default)")) {
continue;
}
if (isInstalled(modelKey)) {
QString series = modelSeriesMap.value(modelKey, "Dom Forgot To Label Me");
seriesToModels[series].append(modelName);
installedModelFileToNameMap.insert(modelKey, modelName);
if (modelReleasedDates.contains(modelKey)) {
installedReleasedDates.insert(modelKey, modelReleasedDates.value(modelKey));
}
QString series = modelSeriesMap.value(modelKey, "Custom Series");
seriesToModels[series].append(modelName);
}
// Add Space Lab to Custom Series
QString spaceLabName = modelFileToNameMap.value("space-lab");
if (isInstalled("space-lab")) {
seriesToModels["Custom Series"].append(spaceLabName);
}
// Sort models within each series
// Sort models alphabetically within each series
for (QString &series : seriesToModels.keys()) {
seriesToModels[series].sort();
}
@@ -371,54 +373,70 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
seriesToModels[defaultSeries].prepend(defaultModelName);
}
QString modelToSelect = ExpandableMultiOptionDialog::getSelection(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"), seriesToModels, currentModel, this);
if (!modelToSelect.isEmpty()) {
currentModel = modelToSelect;
// Prepare favorites and dates for the enhanced dialog
QStringList userFavs = QString::fromStdString(params.get("UserFavorites")).split(",");
userFavs.removeAll("");
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());
QStringList communityFavs = QString::fromStdString(params.get("CommunityFavorites")).split(",");
communityFavs.removeAll("");
// Create dialog instance to access sort mode and favorites after selection
QString savedSortMode = QString::fromStdString(params.get("ModelSortMode"));
if (savedSortMode.isEmpty()) savedSortMode = "alphabetical";
ExpandableMultiOptionDialog dialog(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"),
seriesToModels, currentModel, this,
userFavs, communityFavs, installedReleasedDates, installedModelFileToNameMap, savedSortMode);
int dialogResult = dialog.exec();
// Persist sort mode and user favorites even if no selection was made
QString sortMode = dialog.getCurrentSortMode();
QStringList newUserFavs = dialog.getUserFavorites();
params.put("ModelSortMode", sortMode.toStdString());
params.put("UserFavorites", newUserFavs.join(",").toStdString());
if (dialogResult == QDialog::Accepted) {
QString modelToSelect = dialog.selection;
if (!modelToSelect.isEmpty()) {
currentModel = modelToSelect;
params.put("Model", modelFileToNameMap.key(modelToSelect).toStdString());
// Sync ModelVersion with the selected model if known
{
QString modelKey = modelFileToNameMap.key(modelToSelect);
QFile vf("/data/models/.model_versions.json");
if (vf.open(QIODevice::ReadOnly)) {
auto doc = QJsonDocument::fromJson(vf.readAll());
if (doc.isObject()) {
auto obj = doc.object();
if (obj.contains(modelKey)) {
params.put("ModelVersion", obj.value(modelKey).toString().toStdString());
}
}
}
}
}
updateFrogPilotToggles();
updateFrogPilotToggles();
if (started) {
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
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);
}
if (started) {
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
Hardware::reboot();
}
}
selectModelButton->setValue(modelToSelect);
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
}
deletableModels.removeAll(processModelName(currentModel));
deletableModels.removeAll(modelFileToNameMapProcessed.value(QString::fromStdString(params_default.get("Model"))));
noModelsDownloaded = deletableModels.isEmpty();
}
});
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") {
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);
@@ -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) {
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)) {
@@ -465,6 +493,96 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotModelPanel::updateState);
}
bool FrogPilotModelPanel::isModelInstalled(const QString &key) const {
if (key.isEmpty()) {
return false;
}
bool has_thneed = false;
bool has_policy_meta = false;
bool has_policy_tg = false;
bool has_vision_meta = false;
bool has_vision_tg = false;
bool has_off_policy_meta = false;
bool has_off_policy_tg = false;
bool foundAny = false;
for (const QString &file : modelDir.entryList(QDir::Files)) {
QFileInfo fi(modelDir.filePath(file));
const QString base = fi.baseName();
const QString ext = fi.suffix();
if (!(base.startsWith(key) || base.startsWith(key + "_"))) continue;
foundAny = true;
if (ext == "thneed") {
has_thneed = true;
} else if (ext == "pkl") {
if (base.contains("_driving_policy_metadata")) {
has_policy_meta = true;
} else if (base.contains("_driving_policy_tinygrad")) {
has_policy_tg = true;
} else if (base.contains("_driving_off_policy_metadata")) {
has_off_policy_meta = true;
} else if (base.contains("_driving_off_policy_tinygrad")) {
has_off_policy_tg = true;
} else if (base.contains("_driving_vision_metadata")) {
has_vision_meta = true;
} else if (base.contains("_driving_vision_tinygrad")) {
has_vision_tg = true;
}
}
}
if (has_thneed) {
return true;
}
if (has_policy_meta && has_policy_tg && has_vision_meta && has_vision_tg) {
if (has_off_policy_meta || has_off_policy_tg) {
return has_off_policy_meta && has_off_policy_tg;
}
return true;
}
return foundAny;
}
QMap<QString, QString> FrogPilotModelPanel::getDeletableModelDisplayNames() {
QMap<QString, QString> deletable;
QString defaultModelKey = QString::fromStdString(params_default.get("Model"));
QString defaultModelName = modelFileToNameMap.value(defaultModelKey);
QString processedDefault = processModelName(defaultModelName);
QString processedCurrent = processModelName(currentModel);
for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
const QString &modelKey = it.key();
const QString &displayName = it.value();
if (displayName.isEmpty()) {
continue;
}
if (!isModelInstalled(modelKey)) {
continue;
}
QString processedName = processModelName(displayName);
if (!processedCurrent.isEmpty() && processedName == processedCurrent) {
continue;
}
if (!processedDefault.isEmpty() && processedName == processedDefault) {
continue;
}
deletable.insert(modelKey, displayName);
}
return deletable;
}
void FrogPilotModelPanel::showEvent(QShowEvent *event) {
FrogPilotUIState &fs = *frogpilotUIState();
UIState &s = *uiState();
@@ -478,6 +596,9 @@ void FrogPilotModelPanel::showEvent(QShowEvent *event) {
QStringList availableModels = QString::fromStdString(params.get("AvailableModels")).split(",");
availableModelNames = QString::fromStdString(params.get("AvailableModelNames")).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
{
@@ -497,78 +618,54 @@ void FrogPilotModelPanel::showEvent(QShowEvent *event) {
modelFileToNameMap.clear();
modelFileToNameMapProcessed.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) {
modelFileToNameMap.insert(availableModels[i], availableModelNames[i]);
modelFileToNameMapProcessed.insert(availableModels[i], processModelName(availableModelNames[i]));
modelSeriesMap.insert(availableModels[i], availableModelSeries[i]);
}
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;
}
const QString modelKey = availableModels[i].trimmed();
const QString modelName = availableModelNames[i].trimmed();
if (modelKey.isEmpty() || modelName.isEmpty()) {
continue;
}
// 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;
};
QStringList downloadableModels = availableModelNames;
for (const QString &modelKey : modelFileToNameMap.keys()) {
QString modelName = modelFileToNameMap.value(modelKey);
if (isInstalled(modelKey)) {
downloadableModels.removeAll(modelName);
QString series;
if (i < availableModelSeries.size()) {
series = availableModelSeries[i].trimmed();
}
if (series.isEmpty()) {
series = tr("Custom Series");
}
}
allModelsDownloaded = downloadableModels.isEmpty();
QStringList deletableModels;
for (const QString &file : modelDir.entryList(QDir::Files)) {
QString base = QFileInfo(file).baseName();
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
if (base.startsWith(modelKey)) {
QString modelName = modelFileToNameMapProcessed.value(modelKey);
if (!deletableModels.contains(modelName)) {
deletableModels.append(modelName);
}
modelFileToNameMap.insert(modelKey, modelName);
modelFileToNameMapProcessed.insert(modelKey, processModelName(modelName));
modelSeriesMap.insert(modelKey, series);
if (i < releasedDatesParam.size()) {
const QString released = releasedDatesParam[i].trimmed();
if (!released.isEmpty()) {
this->modelReleasedDates.insert(modelKey, released);
}
}
}
deletableModels.removeAll(processModelName(currentModel));
deletableModels.removeAll(modelFileToNameMapProcessed.value(QString::fromStdString(params_default.get("Model"))));
noModelsDownloaded = deletableModels.isEmpty();
allModelsDownloaded = true;
for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
if (it.value().isEmpty()) {
continue;
}
if (!isModelInstalled(it.key())) {
allModelsDownloaded = false;
break;
}
}
QString modelKey = QString::fromStdString(params.get("Model"));
if (!isInstalled(modelKey)) {
if (!isModelInstalled(modelKey)) {
modelKey = QString::fromStdString(params_default.get("Model"));
}
currentModel = modelFileToNameMap.value(modelKey);
selectModelButton->setValue(currentModel);
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
bool parked = !s.scene.started || fs.frogpilot_scene.parked || fs.frogpilot_toggles.value("frogs_go_moo").toBool();
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
@@ -666,10 +763,10 @@ void FrogPilotModelPanel::updateToggles() {
if (key == "ManageBlacklistedModels" || key == "ManageScores") {
setVisible &= params.getBool("ModelRandomizer");
}
else if (key == "SelectModel") {
} else if (key == "SelectModel") {
setVisible &= !params.getBool("ModelRandomizer");
} else if (key == "RecoveryPower") {
setVisible &= (tuningLevel == 3); // Only visible in developer tuning level
} else if (key == "StopDistance") {
setVisible &= (tuningLevel == 3); // Only visible in developer tuning level
}
+4
View File
@@ -20,6 +20,8 @@ private:
void updateModelLabels(FrogPilotListWidget *labelsList);
void updateState(const UIState &s, const FrogPilotUIState &fs);
void updateToggles();
bool isModelInstalled(const QString &key) const;
QMap<QString, QString> getDeletableModelDisplayNames();
bool allModelsDownloaded;
bool allModelsDownloading;
@@ -55,10 +57,12 @@ private:
QMap<QString, QString> modelFileToNameMap;
QMap<QString, QString> modelFileToNameMapProcessed;
QMap<QString, QString> modelReleasedDates;
QMap<QString, QString> modelSeriesMap;
QString currentModel;
QStringList availableModelNames;
QStringList availableModelSeries;
};
@@ -168,6 +168,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
std::vector<std::tuple<QString, QString, QString, QString>> vehicleToggles {
{"GMToggles", tr("General Motors Settings"), tr("<b>FrogPilot features for General Motors vehicles.</b>"), ""},
{"ExperimentalGMTune", tr("FrogsGoMoo's Experimental Tune"), tr("<b>Experimental GM tune by FrogsGoMoo</b> that attempts to smoothen stopping and takeoff control. Use at your own risk!"), ""},
{"GMPedalLongitudinal", tr("Use Pedal for Longitudinal Control"), tr("<b>Use the pedal interceptor for longitudinal control</b> instead of camera ACC/Redneck when available."), ""},
{"LongPitch", tr("Smooth Pedal Response on Hills"), tr("<b>Smoothen acceleration and braking</b> when driving downhill/uphill."), ""},
{"VoltSNG", tr("Stop-and-Go Hack"), tr("<b>Force stop-and-go</b> on the 2017 Chevy Volt."), ""},
@@ -188,6 +189,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
{"PedalSupport", tr("comma Pedal Support"), tr("<b>Does your vehicle support the \"comma pedal\"?</b>"), ""},
{"OpenpilotLongitudinal", tr("openpilot Longitudinal Support"), tr("<b>Can openpilot control the vehicle's acceleration and braking?</b>"), ""},
{"RadarSupport", tr("Radar Support"), tr("<b>Does openpilot use the vehicle's radar data</b> alongside the device's camera for tracking lead vehicles?"), ""},
{"SASCMSupport", tr("SASCM Support"), tr("<b>Does your vehicle support \"SASCMs\"?</b>"), ""},
{"SDSUSupport", tr("SDSU Support"), tr("<b>Does your vehicle support \"SDSUs\"?</b>"), ""},
{"SNGSupport", tr("Stop-and-Go Support"), tr("<b>Does your vehicle support stop-and-go driving?</b>"), ""}
};
@@ -341,6 +343,7 @@ void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
QStringList detected;
if (hasPedal) detected << "comma Pedal";
if (parent->hasSASCM) detected << "SASCM";
if (parent->hasSDSU) detected << "SDSU";
if (parent->hasZSS) detected << "ZSS";
static_cast<LabelControl*>(toggles["HardwareDetected"])->setText(detected.isEmpty() ? tr("None") : detected.join(", "));
@@ -349,6 +352,7 @@ void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
static_cast<LabelControl*>(toggles["OpenpilotLongitudinal"])->setText(hasOpenpilotLongitudinal ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["PedalSupport"])->setText(parent->canUsePedal ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["RadarSupport"])->setText(parent->hasRadar ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["SASCMSupport"])->setText(parent->canUseSASCM ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["SDSUSupport"])->setText(parent->canUseSDSU ? tr("Yes") : tr("No"));
static_cast<LabelControl*>(toggles["SNGSupport"])->setText(hasSNG ? tr("Yes") : tr("No"));
@@ -403,6 +407,10 @@ void FrogPilotVehiclesPanel::updateToggles() {
setVisible &= isHKGCanFd;
}
else if (key == "GMPedalLongitudinal") {
setVisible &= hasPedal;
}
else if (key == "VoltSNG") {
setVisible &= isVolt && !hasSNG;
}
+2 -2
View File
@@ -36,11 +36,11 @@ private:
std::map<QString, AbstractControl*> toggles;
QSet<QString> gmKeys = {"ExperimentalGMTune", "LongPitch", "VoltSNG"};
QSet<QString> gmKeys = {"ExperimentalGMTune", "GMPedalLongitudinal", "LongPitch", "VoltSNG"};
QSet<QString> hkgKeys = {"NewLongAPI", "TacoTuneHacks"};
QSet<QString> longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "SNGHack", "VoltSNG"};
QSet<QString> toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"};
QSet<QString> vehicleInfoKeys = {"BlindSpotSupport", "HardwareDetected", "OpenpilotLongitudinal", "PedalSupport", "RadarSupport", "SDSUSupport", "SNGSupport"};
QSet<QString> vehicleInfoKeys = {"BlindSpotSupport", "HardwareDetected", "OpenpilotLongitudinal", "PedalSupport", "RadarSupport", "SASCMSupport", "SDSUSupport", "SNGSupport"};
QSet<QString> parentKeys;
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 = {
.max_gas = 7168,
.max_gas = 8191,
.min_gas = 5500,
.inactive_gas = 5500,
.max_brake = 400,
};
const LongitudinalLimits GM_CAM_LONG_LIMITS = {
.max_gas = 7496,
.max_gas = 8848,
.min_gas = 5610,
.inactive_gas = 5650,
.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) {
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);
}
// 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) {
@@ -246,6 +259,11 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U;
bool allowed_btn = (button == GM_BTN_CANCEL) && cruise_engaged_prev;
// For 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
if (gm_cc_long) {
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) {
// 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_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);
if (!block_msg) {
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_cc_long = GET_FLAG(param, GM_PARAM_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_skip_relay_check = GET_FLAG(param, GM_PARAM_NO_CAMERA);
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.
# libusb will try to send it again, with a max timeout.
# Timeout is in ms. If set to 0, the timeout is infinite.
CAN_SEND_TIMEOUT_MS = 10
CAN_SEND_TIMEOUT_MS = 5
CAN_MAX_RETRIES = 3
def can_reset_communications(self):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xc0, 0, 0, b'')
@@ -812,18 +813,18 @@ class Panda:
@ensure_can_packet_version
def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS):
snds = pack_can_buffer(arr)
while True:
try:
for tx in snds:
while True:
bs = self._handle.bulkWrite(3, tx, timeout=timeout)
tx = tx[bs:]
if len(tx) == 0:
break
logging.error("CAN: PARTIAL SEND MANY, RETRYING")
break
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
logging.error("CAN: BAD SEND MANY, RETRYING")
for tx in snds:
retries = 0
while len(tx) > 0:
bs = self._handle.bulkWrite(3, tx, timeout=timeout)
if bs == 0:
retries += 1
if retries > self.CAN_MAX_RETRIES:
logging.warning("CAN send: no progress after retries, dropping")
break
else:
retries = 0
tx = tx[bs:]
def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS):
self.can_send_many([[addr, None, dat, bus]], timeout=timeout)
@@ -831,13 +832,16 @@ class Panda:
@ensure_can_packet_version
def can_recv(self):
dat = bytearray()
while True:
for _ in range(self.CAN_MAX_RETRIES):
try:
dat = self._handle.bulkRead(1, 16384) # Max receive batch size + 2 extra reserve frames
break
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
logging.error("CAN: BAD RECV, RETRYING")
time.sleep(0.1)
time.sleep(0.01)
else:
logging.error("CAN: recv failed after retries")
return []
msgs, self.can_rx_overflow_buffer = unpack_can_buffer(self.can_rx_overflow_buffer + dat)
return msgs
+12 -1
View File
@@ -27,7 +27,10 @@ NACK = 0x1F
CHECKSUM_START = 0xAB
MIN_ACK_TIMEOUT_MS = 100
MAX_ACK_TIMEOUT_MS = 500 # like C++ SPI_ACK_TIMEOUT
DEFAULT_TIMEOUT_MS = 500 # default when timeout=0
MAX_XFER_RETRY_COUNT = 5
MAX_TIMEOUT_RETRIES = 5 # like C++
XFER_SIZE = 0x40*31
@@ -152,6 +155,8 @@ class PandaSpiHandle(BaseHandle):
return cksum
def _wait_for_ack(self, spi, ack_val: int, timeout: int, tx: int, length: int = 1) -> bytes:
# Original behavior preserved - timeout=0 means wait forever within this function
# The caller (_transfer) handles the overall timeout
timeout_s = max(MIN_ACK_TIMEOUT_MS, timeout) * 1e-3
start = time.monotonic()
@@ -225,10 +230,15 @@ class PandaSpiHandle(BaseHandle):
logging.debug("starting transfer: endpoint=%d, max_rx_len=%d", endpoint, max_rx_len)
logging.debug("==============================================")
# Fix timeout=0 infinite loop: default to DEFAULT_TIMEOUT_MS
if timeout == 0:
timeout = DEFAULT_TIMEOUT_MS
n = 0
start_time = time.monotonic()
exc = PandaSpiException()
while (timeout == 0) or (time.monotonic() - start_time) < timeout*1e-3:
# Use the timeout for the overall loop, matching original behavior but with timeout=0 fixed
while (time.monotonic() - start_time) < timeout * 1e-3:
n += 1
logging.debug("\ntry #%d", n)
with self.dev.acquire() as spi:
@@ -238,6 +248,7 @@ class PandaSpiHandle(BaseHandle):
exc = e
logging.debug("SPI transfer failed, retrying", exc_info=True)
logging.error("SPI transfer failed after %d tries, %.2fms", n, (time.monotonic() - start_time) * 1000)
raise exc
def get_protocol_version(self) -> bytes:
+24 -6
View File
@@ -85,6 +85,7 @@ class CarController(CarControllerBase):
self.regen_paddle_pressed = False
self.aego = 0.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]:
if not long_active:
self.planner_regen_hold = False
return 0., False
# Regen paddle hysteresis (frame-based): hold 10 frames, with decrement dead-zone
@@ -111,7 +113,7 @@ class CarController(CarControllerBase):
# Base paddle press hysteresis
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
@@ -144,6 +146,21 @@ class CarController(CarControllerBase):
actuators = CC.actuators
accel = brake_accel = actuators.accel
press_regen_paddle = False
# Planner-driven regen hold: gate by car support and OP long active, use commanded accel thresholds
if (self.CP.enableGasInterceptor and self.CP.carFingerprint in CC_REGEN_PADDLE_CAR
and self.CP.openpilotLongitudinalControl and CC.longActive):
# Match original hysteresis intent: vehicle can usually stop without paddle up to ~1.0 m/s^2
# Use the same thresholds as the aEgo-based hysteresis, but on commanded accel for preemption
planner_press_threshold = -0.7
planner_release_threshold = -0.3
if accel <= planner_press_threshold:
self.planner_regen_hold = True
elif accel >= planner_release_threshold:
self.planner_regen_hold = False
else:
self.planner_regen_hold = False
hud_control = CC.hudControl
hud_alert = hud_control.visualAlert
hud_v_cruise = hud_control.setSpeed
@@ -159,7 +176,7 @@ class CarController(CarControllerBase):
self.CP.openpilotLongitudinalControl and
CC.longActive 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
@@ -326,7 +343,7 @@ class CarController(CarControllerBase):
# Update regen_active state and last_regen_paddle_pressed for next loop
self.last_regen_active = regen_active
self.last_regen_paddle_pressed = self.regen_paddle_pressed
self.last_regen_paddle_pressed = self.regen_paddle_pressed or self.planner_regen_hold
if paddle_sends:
interval_ns = self.last_steer_ts_ns - self.prev_steer_ts_ns
@@ -390,10 +407,11 @@ class CarController(CarControllerBase):
idx = (self.frame // 4) % 4
if self.CP.flags & GMFlags.CC_LONG.value:
if CC.longActive and CS.out.vEgo > self.CP.minEnableSpeed:
if CC.longActive and CS.out.cruiseState.enabled and CS.out.vEgo > self.CP.minEnableSpeed:
# Using extend instead of append since the message is only sent intermittently
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, 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))
if self.CP.enableGasInterceptor:
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:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
self.last_button_frame = self.frame
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
else:
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
+3 -3
View File
@@ -163,11 +163,11 @@ class CarState(CarStateBase):
if self.CP.carFingerprint in CC_ONLY_CAR:
ret.accFaulted = False
ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS
# Try ECM first for cars that might have it (like most GMs), fall back to ASCM
# Try ASCM first for cars that might have it (like misfingerprinted Bolts), fall back to ECM
try:
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
except:
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.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.gm.radar_interface import RADAR_HEADER_MSG
from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR, SDGM_CAR
from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LateralAccelFromTorqueCallbackType
from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LateralAccelFromTorqueCallbackType, get_friction_threshold
from openpilot.selfdrive.controls.lib.drive_helpers import get_friction
ButtonType = car.CarState.ButtonEvent.Type
@@ -27,14 +27,30 @@ CAM_MSG = 0x320 # AEBCmd
ACCELERATOR_POS_MSG = 0xbe
NON_LINEAR_TORQUE_PARAMS = {
CAR.CHEVROLET_BOLT_EUV: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178],
CAR.CHEVROLET_BOLT_CC: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178],
CAR.GMC_ACADIA: [4.78003305, 1.0, 0.3122, 0.05591772],
CAR.CHEVROLET_SILVERADO: [3.29974374, 1.0, 0.25571356, 0.0465122]
CAR.CHEVROLET_BOLT_EUV: {
"left": [2.6531724862969748, 1.1, 0.1719764879840985, 0.0],
"right": [2.8531724862969748, 1.0, 0.1469764879840985, 0.0],
},
CAR.CHEVROLET_BOLT_CC: {
"left": [2.6531724862969748, 1.1, 0.1719764879840985, 0.0],
"right": [2.8531724862969748, 1.0, 0.1469764879840985, 0.0],
},
CAR.GMC_ACADIA: {
"left": [4.78003305, 1.0, 0.3122, 0.05591772],
"right": [4.78003305, 1.0, 0.3122, 0.05591772],
},
CAR.CHEVROLET_SILVERADO: {
"left": [3.29974374, 1.0, 0.25571356, 0.0465122],
"right": [3.29974374, 1.0, 0.25571356, 0.0465122],
},
}
class CarInterface(CarInterfaceBase):
def __init__(self, CP, FPCP, CarController, CarState):
super().__init__(CP, FPCP, CarController, CarState)
self.steer_offset = 0.0
@staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed):
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
@@ -60,7 +76,9 @@ class CarInterface(CarInterfaceBase):
# This has big effect on the stability about 0 (noise when going straight)
non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint)
assert non_linear_torque_params, "The params are not defined"
a, b, c, 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 = np.sign(sig_input) * (1 / (1 + exp(-fabs(sig_input))) - 0.5)
steer_torque = (sig * b) + (lateral_acceleration * c) + d
@@ -76,20 +94,28 @@ class CarInterface(CarInterfaceBase):
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
def torque_from_lateral_accel_siglin(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning):
return np.interp(lateral_acceleration, lataccel_values, torque_values)
return float(np.interp(lateral_acceleration, lataccel_values, torque_values) + self.steer_offset)
return torque_from_lateral_accel_siglin
else:
return self.torque_from_lateral_accel_linear
def torque_from_lateral_accel_linear(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning):
return self.torque_from_lateral_accel_linear(lateral_acceleration, torque_params) + self.steer_offset
return torque_from_lateral_accel_linear
def lateral_accel_from_torque(self) -> LateralAccelFromTorqueCallbackType:
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
def lateral_accel_from_torque_siglin(torque: float, torque_params: car.CarParams.LateralTorqueTuning):
return np.interp(torque, torque_values, lataccel_values)
return np.interp(torque - self.steer_offset, torque_values, lataccel_values)
return lateral_accel_from_torque_siglin
else:
return self.lateral_accel_from_torque_linear
def lateral_accel_from_torque_linear(torque: float, torque_params: car.CarParams.LateralTorqueTuning):
return self.lateral_accel_from_torque_linear(torque - self.steer_offset, torque_params)
return lateral_accel_from_torque_linear
def update(self, c: car.CarControl, can_strings: list[bytes], frogpilot_toggles) -> car.CarState:
self.steer_offset = float(getattr(frogpilot_toggles, "steer_offset", 0.0))
return super().update(c, can_strings, frogpilot_toggles)
@staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles):
@@ -97,6 +123,11 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)]
ret.autoResumeSng = False
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN]
# Detect Beartech SASCM allows openpilot longitudinal control on SDGM and ASCM_INT vehicles
if 0x2FF in fingerprint[0]:
ret.flags |= GMFlags.SASCM.value
if PEDAL_MSG in fingerprint[0]:
ret.enableGasInterceptor = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR
@@ -210,6 +241,12 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# Bolt-only lateral tuning overrides
ret.lateralTuning.torque.kp = 1.03
ret.lateralTuning.torque.ki = 1.07
ret.lateralTuning.torque.kd = 0.93
ret.lateralTuning.torque.kfDEPRECATED = 0.02
if ret.enableGasInterceptor:
# ACC Bolts use pedal for full longitudinal control, not just sng
ret.flags |= GMFlags.PEDAL_LONG.value
@@ -265,7 +302,7 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.CHEVROLET_TRAX:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if ret.enableGasInterceptor:
if ret.enableGasInterceptor and frogpilot_toggles.gm_pedal_longitudinal:
ret.networkLocation = NetworkLocation.fwdCamera
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
ret.minEnableSpeed = -1
@@ -354,8 +391,9 @@ class CarInterface(CarInterfaceBase):
if ret.vEgo < self.CP.minSteerSpeed:
events.add(EventName.belowSteerSpeed)
if (self.CP.flags & GMFlags.CC_LONG.value) and ret.vEgo < self.CP.minEnableSpeed and ret.cruiseState.enabled:
events.add(EventName.speedTooLow)
if (self.CP.flags & GMFlags.CC_LONG.value) and ret.vEgo < self.CP.minEnableSpeed:
if ret.cruiseState.enabled or self.CS.out.cruiseState.enabled:
events.add(EventName.speedTooLow)
if (self.CP.flags & GMFlags.PEDAL_LONG.value) and \
self.CP.transmissionType == TransmissionType.direct and \
+11 -9
View File
@@ -42,8 +42,8 @@ class CarControllerParams:
self.MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen
if CP.carFingerprint in CAMERA_ACC_CAR and CP.carFingerprint not in CC_ONLY_CAR:
self.MAX_GAS = 7496
self.MAX_GAS_PLUS = 7496
self.MAX_GAS = 8848
self.MAX_GAS_PLUS = 8848
self.MAX_ACC_REGEN = 5610
self.INACTIVE_REGEN = 5650
# Camera ACC vehicles have no regen while enabled.
@@ -52,17 +52,17 @@ class CarControllerParams:
self.BRAKE_SWITCH_MAX = self.MAX_ACC_REGEN if CP.carFingerprint in EV_CAR else self.ZERO_GAS
elif CP.carFingerprint in SDGM_CAR:
self.MAX_GAS = 7168
self.MAX_GAS_PLUS = 7168
self.MAX_ACC_REGEN = 5500
self.INACTIVE_REGEN = 5500
self.MAX_GAS = 7496
self.MAX_GAS_PLUS = 7496
self.MAX_ACC_REGEN = 5610
self.INACTIVE_REGEN = 5650
max_regen_acceleration = 0.
self.BRAKE_SWITCH = self.ZERO_GAS
else:
self.MAX_GAS = 7168 # Safety limit, not ACC max. Stock ACC >8192 from standstill.
self.MAX_GAS_PLUS = 7168 # 8292 uses new bit, possible but not tested. Matches Twilsonco tw-main max
self.MAX_ACC_REGEN = 5500 # Max ACC regen is slightly less than max paddle regen
self.MAX_GAS = 8191 # Safety limit, not ACC max. Stock ACC >8192 from standstill.
self.MAX_GAS_PLUS = 8191 # 8292 uses new bit, possible but not tested. Matches Twilsonco tw-main max
self.MAX_ACC_REGEN = 7110 # Max ACC regen is slightly less than max paddle regen
self.INACTIVE_REGEN = 5500
# ICE has much less engine braking force compared to regen in EVs,
# lower threshold removes some braking deadzone
@@ -275,6 +275,8 @@ class GMFlags(IntFlag):
CC_LONG = 2
NO_CAMERA = 4
NO_ACCELERATOR_POS_MSG = 8
FORCE_BRAKE_C9 = 16
SASCM = 32
# In a Data Module, an identifier is a string used to recognize an object,
+14 -1
View File
@@ -37,7 +37,12 @@ EventName = car.CarEvent.EventName
MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS
ACCEL_MAX = 2.0
ACCEL_MIN = -3.5
FRICTION_THRESHOLD = 0.3
FRICTION_THRESHOLD = 0.12
def get_friction_threshold(v_ego):
# Interpolate friction threshold
from openpilot.common.numpy_fast import interp
return interp(v_ego, [1 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.12, 0.3])
TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.toml')
TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.toml')
@@ -147,8 +152,11 @@ class CarInterfaceBase(ABC):
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles)
trailer_load_kg = getattr(frogpilot_toggles, "trailer_load_kg", 0)
# Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
if not ret.notCar:
ret.mass = ret.mass + trailer_load_kg
ret.mass = ret.mass + STD_CARGO_KG
# Set params dependent on values set by the car interface
@@ -179,6 +187,7 @@ class CarInterfaceBase(ABC):
elif platform in GMCAR:
fp_ret.canUsePedal = True
fp_ret.canUseSASCM = True
elif platform in HondaCAR:
if candidate == HondaCAR.HONDA_CLARITY:
@@ -283,6 +292,7 @@ class CarInterfaceBase(ABC):
ret.vEgoStopping = 0.5
ret.vEgoStarting = 0.5
ret.stoppingControl = True
ret.longitudinalTuning.kfDEPRECATED = 1.
ret.longitudinalTuning.kpBP = [0.]
ret.longitudinalTuning.kpV = [0.]
ret.longitudinalTuning.kiBP = [0.]
@@ -298,6 +308,9 @@ class CarInterfaceBase(ABC):
tune.init('torque')
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.latAccelFactor = params['LAT_ACCEL_FACTOR']
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_ESV_2019" = [1.15, 1.3, 0.2]
"CADILLAC_XT4" = [1.45, 1.6, 0.2]
"CHEVROLET_BOLT_EUV" = [2.0, 2.0, 0.05]
"CHEVROLET_BOLT_EUV" = [2.0, 2.0, 0.09]
"CHEVROLET_MALIBU_CC" = [1.85, 1.85, 0.075]
"CHEVROLET_SILVERADO" = [1.9, 1.9, 0.112]
"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.has_menu = self.CP.carName == "gm" and not (self.CP.flags & GMFlags.NO_CAMERA.value or self.CP.carFingerprint in CC_ONLY_CAR)
self.has_menu = self.CP.carName == "gm" and not (self.CP.flags & GMFlags.NO_CAMERA.value)
self.frogpilot_AM = AlertManager()
self.frogpilot_events = Events(frogpilot=True)
@@ -623,9 +623,31 @@ class Controls:
# Update Torque Params
if self.CP.lateralTuning.which() == 'torque':
torque_params = self.sm['liveTorqueParameters']
if self.sm.all_checks(['liveTorqueParameters']) and (torque_params.useParams or self.frogpilot_toggles.force_auto_tune):
self.LaC.update_live_torque_params(torque_params.latAccelFactorFiltered, torque_params.latAccelOffsetFiltered,
torque_params.frictionCoefficientFiltered)
allow_lat_accel_learning = self.CP.carName in ['toyota', 'hyundai']
allow_friction_learning = (allow_lat_accel_learning or self.CP.carName in ['gm'])
use_live_params = self.sm.all_checks(['liveTorqueParameters']) and (torque_params.useParams or self.frogpilot_toggles.force_auto_tune)
# Defaults pulled from manual tuning values
lat_accel_factor = self.params.get_float("SteerLatAccel")
friction = self.params.get_float("SteerFriction")
lat_accel_offset = self.CP.lateralTuning.torque.latAccelOffset
# Apply user overrides first
if self.frogpilot_toggles.use_custom_latAccelFactor:
lat_accel_factor = self.frogpilot_toggles.latAccelFactor
if self.frogpilot_toggles.use_custom_friction:
friction = self.frogpilot_toggles.friction
# Layer in live values only for parameters the platform allows to learn and only when not overridden
if use_live_params:
if allow_lat_accel_learning and not self.frogpilot_toggles.use_custom_latAccelFactor:
lat_accel_factor = torque_params.latAccelFactorFiltered
lat_accel_offset = torque_params.latAccelOffsetFiltered
if allow_friction_learning and not self.frogpilot_toggles.use_custom_friction:
friction = torque_params.frictionCoefficientFiltered
self.LaC.update_live_torque_params(lat_accel_factor, lat_accel_offset, friction)
if self.sm.updated['liveDelay'] and hasattr(self.LaC, "update_live_delay"):
self.LaC.update_live_delay(self.sm['liveDelay'].lateralDelay)
@@ -773,7 +795,7 @@ class Controls:
if self.frogpilot_toggles.conditional_experimental_mode or self.frogpilot_toggles.slc_fallback_experimental_mode:
self.experimental_mode = self.sm['frogpilotPlan'].experimentalMode
if hasattr(self.LaC, "pid"):
if hasattr(self.LaC, "pid") and self.CP.lateralTuning.which() != "pid":
self.LaC.pid._k_p = self.frogpilot_toggles.steerKp
# Update FrogPilot variables
+1 -1
View File
@@ -982,7 +982,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
"",
AlertStatus.normal, AlertSize.full,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5),
ET.USER_DISABLE: ImmediateDisableAlert("Reverse Gear"),
ET.USER_DISABLE: ImmediateDisableAlert("Wrong Gear"),
ET.NO_ENTRY: NoEntryAlert("Reverse Gear"),
},
+13 -2
View File
@@ -1,18 +1,21 @@
import math
from cereal import log
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
from openpilot.selfdrive.controls.lib.pid import PIDController
class LatControlPID(LatControl):
def __init__(self, CP, CI, dt):
super().__init__(CP, CI, dt)
self.steer_release_i_decay = 0.8
self.prev_steering_pressed = False
self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
(CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
pos_limit=self.steer_max, neg_limit=-self.steer_max)
self.ff_factor = CP.lateralTuning.pid.kf
self.get_steer_feedforward = CI.get_steer_feedforward_function()
self.low_speed_reset_threshold = max(CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED)
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay, llk, model_data, frogpilot_toggles):
pid_log = log.ControlsState.LateralPIDState.new_message()
@@ -28,11 +31,17 @@ class LatControlPID(LatControl):
if not active:
output_torque = 0.0
pid_log.active = False
self.pid.reset()
else:
if self.prev_steering_pressed and not CS.steeringPressed:
self.pid.i *= self.steer_release_i_decay
# offset does not contribute to resistive torque
ff = self.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,
feedforward=ff,
@@ -46,4 +55,6 @@ class LatControlPID(LatControl):
pid_log.output = float(output_torque)
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
self.prev_steering_pressed = CS.steeringPressed
return output_torque, angle_steers_des, pid_log
+90 -22
View File
@@ -3,10 +3,11 @@ import numpy as np
from collections import deque
from cereal import log
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD
from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD, get_friction_threshold
from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED, get_friction
from openpilot.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.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
# to be overcome to move it at all, this is compensated for too.
KP = 0.6
KI = 0.3
KD = 0.0
KP = 0.7
KI = 0.35
INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30]
KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP]
LOW_SPEED_X = [0, 10, 20, 30]
LOW_SPEED_Y = [12, 10.5, 8, 5]
MAX_LAT_JERK_UP = 2.5 # m/s^3
LP_FILTER_CUTOFF_HZ = 1.2
JERK_LOOKAHEAD_SECONDS = 0.19
JERK_GAIN = 0.22
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
VERSION = 0
VERSION = 2
DEBUG_TORQUE_TUNE = False
FF_SCALE_BLEND_LAT_ACCEL = 0.05
DEADZONE_BOOST_LAT_ACCEL = 0.08
UNWIND_D_DES_THRESHOLD = -1.0
UNWIND_LAT_ACCEL_NEAR_ZERO = 0.3
BOLT_CARS = (GM_CAR.CHEVROLET_BOLT_EUV, GM_CAR.CHEVROLET_BOLT_CC)
class LatControlTorque(LatControl):
def __init__(self, CP, CI, dt):
super().__init__(CP, CI, dt)
self.steer_release_i_decay = 0.8
self.prev_steering_pressed = False
self.torque_params = CP.lateralTuning.torque
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
self.lateral_accel_from_torque = CI.lateral_accel_from_torque()
self.pid = PIDController([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.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 = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
self.lookahead_frames = int(JERK_LOOKAHEAD_SECONDS / self.dt)
self.jerk_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt)
self.previous_measurement = 0.0
self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * 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):
self.torque_params.latAccelFactor = latAccelFactor
@@ -61,7 +95,15 @@ class LatControlTorque(LatControl):
if not active:
output_torque = 0.0
pid_log.active = False
self.pid.reset()
self.previous_measurement = 0.0
self.measurement_rate_filter.x = 0.0
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
self.prev_desired_lateral_accel = 0.0
else:
if self.prev_steering_pressed and not CS.steeringPressed:
self.pid.i *= self.steer_release_i_decay
measured_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
@@ -69,33 +111,51 @@ class LatControlTorque(LatControl):
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]
# TODO factor out lateral jerk from error to later replace it with delay independent alternative
future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2
self.lat_accel_request_buffer.append(future_desired_lateral_accel)
raw_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / max(lat_delay, self.dt)
raw_lateral_jerk = np.clip(raw_lateral_jerk, -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
desired_lateral_jerk = np.clip(self.jerk_filter.update(raw_lateral_jerk), -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
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_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
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_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
pid_log.error = float(error)
pid_log.error = float(error_with_lsf)
ff = gravity_adjusted_future_lateral_accel
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
ff -= self.torque_params.latAccelOffset
# TODO jerk is weighted by lat_delay for legacy reasons, but should be made independent of it
ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)
ff_scale = 1.0
if self.is_bolt:
ff_scale = np.interp(ff, [-FF_SCALE_BLEND_LAT_ACCEL, 0.0, FF_SCALE_BLEND_LAT_ACCEL],
[self.torque_ff_scale_neg, 1.0, self.torque_ff_scale_pos])
ff *= ff_scale
ff += get_friction(error_with_lsf + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, get_friction_threshold(CS.vEgo), self.torque_params)
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
output_lataccel = self.pid.update(pid_log.error,
-measurement_rate,
feedforward=ff,
speed=CS.vEgo,
freeze_integrator=freeze_integrator)
if CS.vEgo < self.low_speed_reset_threshold:
self.pid.reset()
freeze_integrator = (steer_limited_by_safety or CS.steeringPressed or
CS.vEgo < self.low_speed_reset_threshold or unwind_detected)
output_lataccel = self.pid.update(pid_log.error, error_rate=-measurement_rate, speed=CS.vEgo, feedforward=ff, freeze_integrator=freeze_integrator)
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
pid_log.active = True
@@ -103,11 +163,19 @@ class LatControlTorque(LatControl):
pid_log.i = float(self.pid.i)
pid_log.d = float(self.pid.d)
pid_log.f = float(self.pid.f)
pid_log.output = float(-output_torque) # TODO: log lat accel?
pid_log.output = float(-output_torque) # TODO: log lat accel?
pid_log.actualLateralAccel = float(measurement)
pid_log.desiredLateralAccel = float(setpoint)
pid_log.desiredLateralJerk = float(desired_lateral_jerk)
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
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
return -output_torque, 0.0, pid_log
+20
View File
@@ -179,6 +179,26 @@ class LongControl:
else:
output_accel = raw_output_accel
if self.long_control_state == LongCtrlState.pid:
# Smooth acceleration and deceleration with urgency-based rate limiting
base_rate = 1.0
if output_accel < self.last_output_accel: # Deceleration requested
decel_needed = self.last_output_accel - output_accel
# Use a safe default for ACCEL_MIN if not available, to prevent division by zero
max_decel = abs(CarControllerParams.ACCEL_MIN) if CarControllerParams.ACCEL_MIN != 0 else 4.0
urgency = min(1.0, decel_needed / max_decel)
# Adjust rate based on urgency (1.0 m/s^3 for low urgency, up to 4.0 m/s^3 for high urgency)
max_rate = 1.0 + 3.0 * urgency
else:
max_rate = base_rate # Acceleration is always smooth
max_accel_change = max_rate * DT_CTRL
output_accel = clip(output_accel,
self.last_output_accel - max_accel_change,
self.last_output_accel + max_accel_change)
self.last_output_accel = clip(output_accel, accel_limits[0], accel_limits[1])
return self.last_output_accel
@@ -5,7 +5,6 @@ import numpy as np
from cereal import log
from openpilot.common.numpy_fast import clip, interp
from openpilot.common.realtime import DT_MDL
from openpilot.common.swaglog import cloudlog
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.conversions import Conversions as CV
# WARNING: imports outside of constants will not trigger a rebuild
@@ -422,9 +421,6 @@ class LongitudinalMpc:
scale = float(np.interp(uncertainty, [0.45, 0.60], [1.2, 1.5]))
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':
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]
@@ -627,11 +623,7 @@ class LongitudinalMpc:
self.prev_a = np.interp(T_IDXS + self.dt, T_IDXS, self.a_solution)
t = time.monotonic()
if self.solution_status != 0:
if t > self.last_cloudlog_t + 5.0:
self.last_cloudlog_t = t
cloudlog.warning(f"Long mpc reset, solution_status: {self.solution_status}")
self.reset()
# reset = 1
# print(f"long_mpc timings: total internal {self.solve_time:.2e}, external: {(time.monotonic() - t0):.2e} qp {self.time_qp_solution:.2e}, \
+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
LON_MPC_STEP = 0.2 # first step is 0.2s
A_CRUISE_MIN = -1.2
A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6]
A_CRUISE_MAX_BP = [0., 10.0, 25., 40.]
A_CRUISE_MIN = -1.0
A_CRUISE_MAX_BP = [0.0, 5., 10., 15., 20., 25., 40.]
A_CRUISE_MAX_VALS = [1.125, 1.125, 1.125, 1.125, 1.25, 1.25, 1.5]
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
ALLOW_THROTTLE_THRESHOLD = 0.5
MIN_ALLOW_THROTTLE_SPEED = 2.5
@@ -111,9 +111,6 @@ class LongitudinalPlanner:
self.a_desired_trajectory = np.zeros(CONTROL_N)
self.j_desired_trajectory = np.zeros(CONTROL_N)
self.solverExecutionTime = 0.0
# logging cadence & state
self.last_uncert_log_t = 0.0
self.prev_uncert_over = False
# ---- Rubberband mitigation state ----
# Two uncertainty tracks (slow/fast) for asymmetric gating
@@ -138,7 +135,7 @@ class LongitudinalPlanner:
@property
def mlsim(self):
return self.generation in ("v8", "v10", "v11")
return self.generation in ("v8", "v10", "v11", "v12")
def get_mpc_mode(self) -> str:
"""
@@ -344,19 +341,6 @@ class LongitudinalPlanner:
# now_t defined earlier
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
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
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):
@@ -72,7 +73,10 @@ class TorqueEstimator(ParameterEstimator):
self.offline_friction = 0.0
self.offline_latAccelFactor = 0.0
self.resets = 0.0
self.use_params = CP.carName in ALLOWED_CARS and 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':
self.offline_friction = CP.lateralTuning.torque.friction
@@ -104,11 +108,11 @@ class TorqueEstimator(ParameterEstimator):
cache_CP = msg
if self.get_restore_key(cache_CP, cache_ltp.version) == self.get_restore_key(CP, VERSION):
if cache_ltp.liveValid:
initial_params = {
'latAccelFactor': cache_ltp.latAccelFactorFiltered,
'latAccelOffset': cache_ltp.latAccelOffsetFiltered,
'frictionCoefficient': cache_ltp.frictionCoefficientFiltered
}
if self.allow_lat_accel_learning:
initial_params['latAccelFactor'] = cache_ltp.latAccelFactorFiltered
initial_params['latAccelOffset'] = cache_ltp.latAccelOffsetFiltered
if self.allow_friction_learning:
initial_params['frictionCoefficient'] = cache_ltp.frictionCoefficientFiltered
initial_params['points'] = cache_ltp.points
self.decay = cache_ltp.decay
self.filtered_points.load_points(initial_params['points'])
@@ -155,6 +159,10 @@ class TorqueEstimator(ParameterEstimator):
def update_params(self, params):
self.decay = min(self.decay + DT_MDL, MAX_FILTER_DECAY)
for param, value in params.items():
if param.startswith('latAccel') and not self.allow_lat_accel_learning:
continue
if param == 'frictionCoefficient' and not self.allow_friction_learning:
continue
self.filtered_params[param].update(value)
self.filtered_params[param].update_alpha(self.decay)
+3 -3
View File
@@ -33,7 +33,7 @@ class DRIVER_MONITOR_SETTINGS:
self._SG_THRESHOLD = 0.9
self._BLINK_THRESHOLD = 0.865
self._EE_THRESH11 = 0.4
self._EE_THRESH11 = 0.6
self._EE_THRESH12 = 15.0
self._EE_MAX_OFFSET1 = 0.06
self._EE_MIN_OFFSET1 = 0.025
@@ -44,9 +44,9 @@ class DRIVER_MONITOR_SETTINGS:
self._POSE_YAW_THRESHOLD = 0.4020
self._POSE_YAW_THRESHOLD_SLACK = 0.5042
self._POSE_YAW_THRESHOLD_STRICT = self._POSE_YAW_THRESHOLD
self._PITCH_NATURAL_OFFSET = 0.029 # initial value before offset is learned
self._PITCH_NATURAL_OFFSET = 0.011 # initial value before offset is learned
self._PITCH_NATURAL_THRESHOLD = 0.449
self._YAW_NATURAL_OFFSET = 0.097 # initial value before offset is learned
self._YAW_NATURAL_OFFSET = 0.075 # initial value before offset is learned
self._PITCH_MAX_OFFSET = 0.124
self._PITCH_MIN_OFFSET = -0.0881
self._YAW_MAX_OFFSET = 0.289
Binary file not shown.
Binary file not shown.
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">اختيار</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished">إلغاء</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<translation type="gpt-5-generated">&lt;b&gt;تعديلات Twilsonco المعتمدة على العزم لتنعيم التوجيه في المنعطفات.&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<translation type="gpt-5-generated">&lt;b&gt;سلوك متابعة يحاكي السائقين البشر&lt;/b&gt; عبر تقليل الفجوات خلف المركبات الأسرع لانطلاق أسرع وضبط مسافة المتابعة المطلوبة ديناميكياً لكبح ألطف وأكثر كفاءة.</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">دعم comma Pedal</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -434,6 +434,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Pick</translation>
@@ -442,6 +462,10 @@
<source>Cancel</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ExperimentalModeButton</name>
@@ -1176,6 +1200,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source>
<translation type="gpt-5-generated">&lt;b&gt;Twilsonco make torque tweak. Steering smooth in curve.&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<translation type="gpt-5-generated">&lt;b&gt;Make car follow like human&lt;/b&gt;. Close gap behind fast car for quick takeoff. Change follow distance on the fly for soft, smart brake.</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2525,6 +2609,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2554,13 +2642,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished">Reset</translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3718,6 +3810,22 @@ Developer - Many custom setting for seasoned enthusiast</translation>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal Help</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Auswählen</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished">Abbrechen</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ExperimentalModeButton</name>
@@ -1175,6 +1199,22 @@
<source>&lt;b&gt;Twilsonco's torque-based adjustments to smoothen out steering in curves.&lt;/b&gt;</source>
<translation type="gpt-5-generated">&lt;b&gt;Twilsoncos drehmomentbasierte Anpassungen zur Glättung der Lenkung in Kurven.&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<translation type="gpt-5-generated">&lt;b&gt;Dem Fahrverhalten menschlicher Fahrer nachempfunden&lt;/b&gt;, indem Lücken hinter schnelleren Fahrzeugen geschlossen werden, um schneller anzufahren, und der gewünschte Folgeabstand dynamisch angepasst wird, um sanfteres und effizienteres Bremsen zu ermöglichen.</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished">Zurücksetzen</translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Entwickler Hochgradig anpassbare Einstellungen für versierte Enthusiasten</
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">Unterstützung für comma Pedal</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Quack-select!</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<translation type="gpt-5-generated">&lt;b&gt;Quack! Twilsoncos torque tweaks to smooth out steering in curves, waddle-waddle.&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<translation type="gpt-5-generated">&lt;b&gt;Quack! Mimic human drivers&lt;/b&gt; by waddling up to close gaps behind faster cars for zippy takeoffs, and duckily tweak the following distance for smoother, more efficient braking.</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2525,6 +2609,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2554,13 +2642,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3716,6 +3808,22 @@ Developer - Ultra-custom settings for seasoned duckthusiasts</translation>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">Quack! comma Pedal Support, waddle on!</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Seleccione</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished">Cancelar</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<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>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<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>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3713,6 +3805,22 @@ Desarrollador: configuración altamente personalizable para entusiastas veterano
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">Compatibilidad con comma Pedal</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Sélectionner</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished">Annuler</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<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>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<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>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished">Réinitialiser</translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3713,6 +3805,22 @@ Développeur Paramètres hautement personnalisables pour passionnés chevron
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">Prise en charge de comma Pedal</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Ribbit! Pick it, croak!</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished">Ribbit! Cancel croak!</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<translation type="gpt-5-generated">&lt;b&gt;Ribbit! Twilsoncos torque tweaks smooth steering through curves, croak.&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<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>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - Highly customizable settings for seasoned swamp pros</translation>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">Ribbit! comma Pedal Support croak!</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished"></translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<translation type="gpt-5-generated">&lt;b&gt;Twilsoncoのトルクベース調整&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<translation type="gpt-5-generated">&lt;b&gt;&lt;/b&gt;調</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3712,6 +3804,22 @@ Developer - こだわりのある上級者向けの高度にカスタマイズ
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal </translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished"></translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<translation type="gpt-5-generated">&lt;b&gt; Twilsonco의 .&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<translation type="gpt-5-generated">&lt;b&gt; &lt;/b&gt;, .</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3713,6 +3805,22 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal </translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Be pickin'</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished">Belay</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<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>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<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>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - Highly customizable riggins fer seasoned enthusiasts</translation
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal Support, arrr</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Selecione</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished">Cancelar</translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<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>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<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>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Desenvolvedor - Configurações altamente personalizáveis para entusiastas expe
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">Compatibilidade com comma Pedal</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Select</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<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>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<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>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2529,6 +2613,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2558,13 +2646,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3724,6 +3816,22 @@ Developer - Most customizable settings for well-tried enthusiasts</translation>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal Support</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished"></translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<translation type="gpt-5-generated">&lt;b&gt; Twilsonco &lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<translation type="gpt-5-generated">&lt;b&gt;&lt;/b&gt; </translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated"> comma Pedal</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished">Seç</translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<translation type="gpt-5-generated">&lt;b&gt;Virajlarda direksiyonu yumuşatmak için Twilsonconun torka dayalı ayarlamaları.&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<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>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished">Sıfırla</translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3713,6 +3805,22 @@ Geliştirici - Tecrübeli meraklılar için yüksek özelleştirilebilir ayarlar
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal Desteği</translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished"></translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<translation type="gpt-5-generated">&lt;b&gt;Twilsonco &lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<translation type="gpt-5-generated">&lt;b&gt;仿&lt;/b&gt;</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal </translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
+109 -1
View File
@@ -433,6 +433,26 @@
</context>
<context>
<name>ExpandableMultiOptionDialog</name>
<message>
<source>Sort by:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Alphabetical</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Newest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Date (Oldest)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Favorites First</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select</source>
<translation type="unfinished"></translation>
@@ -441,6 +461,10 @@
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 (Community Fav)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<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>
<translation type="gpt-5-generated">&lt;b&gt;Twilsonco 調&lt;/b&gt;</translation>
</message>
<message>
<source>Steer Offset (Default: %1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Steer Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Offsets steering torque to help compensate for alignment or tire issues.&lt;/b&gt; More negative pulls the car right; more positive pulls it left. Most users should not need to touch this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset &lt;b&gt;Steer Offset&lt;/b&gt; to its default value?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotLongitudinalPanel</name>
@@ -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>
<translation type="gpt-5-generated">&lt;b&gt;仿&lt;/b&gt;調</translation>
</message>
<message>
<source>EV Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use acceleration profiles tuned for EVs.&lt;/b&gt; Defaults to the vehicle's detected powertrain type but can be overridden if the automatic choice doesn't match.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>High Speed Following Distance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.25 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.45 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.&lt;/b&gt; Increase for more space; decrease for tighter gaps.&lt;br&gt;&lt;br&gt;Default: 1.75 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trailer Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Increase the vehicle mass to account for towing.&lt;/b&gt; Adjust in 500 lb steps up to 15,000 lbs to fine-tune gas and brake behavior when pulling a trailer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> lbs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotManageControl</name>
@@ -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>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recovery Power</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stop Distance</source>
<translation type="unfinished"></translation>
@@ -2523,6 +2607,10 @@
<source>Select the active driving model.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Series</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>There are no more models to blacklist! The only available model is "%1"!</source>
<translation type="unfinished"></translation>
@@ -2552,13 +2640,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<source>Are you sure you want to reset your &lt;b&gt;Recovery Power&lt;/b&gt; to the default of 1.0?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to reset your &lt;b&gt;Stop Distance&lt;/b&gt; to the default of 6 meters?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotModelReview</name>
@@ -3714,6 +3806,22 @@ Developer - 為資深愛好者提供高度自訂的設定</translation>
<source>comma Pedal Support</source>
<translation type="gpt-5-generated">comma Pedal </translation>
</message>
<message>
<source>Use Pedal for Longitudinal Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Use the pedal interceptor for longitudinal control&lt;/b&gt; instead of camera ACC/Redneck when available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SASCM Support</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;b&gt;Does your vehicle support "SASCMs"?&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FrogPilotVisualsPanel</name>
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("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
migration_flag_file = "/data/media/0/frogpilot_human_toggles_migrated.flag"
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:
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
reg_res = register(show_spinner=True)
if reg_res:
+23 -19
View File
@@ -1,5 +1,6 @@
"""Install exception handler for process crash."""
import glob
import os
import re
import sentry_sdk
import traceback
from datetime import datetime
@@ -9,15 +10,16 @@ from sentry_sdk.integrations.threading import ThreadingIntegration
from openpilot.common.params import Params
from openpilot.system.hardware import HARDWARE, PC
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.frogpilot.common.frogpilot_variables import ERROR_LOGS_PATH, params
class SentryProject(Enum):
# python project
SELFDRIVE = os.environ.get("SENTRY_DSN", "")
SELFDRIVE = "https://7305139359a548fcb348ec09497dc389@bugsink.firestar.link/1"
# 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:
@@ -26,6 +28,12 @@ def report_tombstone(fn: str, message: str, contents: str) -> None:
with sentry_sdk.configure_scope() as scope:
scope.set_extra("tombstone_fn", fn)
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.flush()
@@ -51,8 +59,14 @@ def capture_exception(*args, crash_log=True, **kwargs) -> None:
cloudlog.error("crash", exc_info=kwargs.get('exc_info', 1))
try:
sentry_sdk.capture_exception(*args, **kwargs)
sentry_sdk.flush() # https://github.com/getsentry/sentry-python/issues/291
with sentry_sdk.push_scope() as scope:
# 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:
cloudlog.exception("sentry exception")
@@ -90,25 +104,15 @@ def save_exception(exc_text: str, crash_log) -> None:
def init(project: SentryProject) -> bool:
build_metadata = get_build_metadata()
FrogPilot = "frogai" in build_metadata.openpilot.git_origin.lower()
if not FrogPilot or PC:
if PC:
return False
build_metadata = get_build_metadata()
short_branch = build_metadata.channel
if short_branch in ["COMMA", "HEAD"]:
return
elif short_branch == "FrogPilot-Development":
env = "Development"
elif build_metadata.release_channel:
env = "Release"
elif short_branch == "FrogPilot-Testing":
env = short_branch
if re.search("test", short_branch, re.IGNORECASE):
env = "Testing"
elif build_metadata.tested_channel:
env = "Staging"
else:
env = short_branch
dongle_id = params.get("DongleId", 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
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
cloudlog.info("creating finalized version of the overlay")
set_consistent_flag(False)
@@ -191,7 +224,12 @@ def finalize_update(params) -> None:
# Copy the merged overlay view and set the update ready flag
if os.path.exists(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", "submodule", "foreach", "--recursive", "git", "reset", "--hard"], FINALIZED)
@@ -377,10 +415,34 @@ class Updater:
else:
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:
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
set_consistent_flag(False)
@@ -389,7 +451,7 @@ class Updater:
setup_git_options(OVERLAY_MERGED)
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 reset in progress")