mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-24 01:33:46 +08:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b78cb017e6 | |||
| ecaae3f59b | |||
| 8e66bbf953 | |||
| 81924bcca1 | |||
| e849c4ceeb | |||
| 12088b9e53 | |||
| fff9ef7a8e | |||
| 433ef324e1 | |||
| 86d0954cfb | |||
| 63acba85dd | |||
| cfdcad5e54 | |||
| eaf78f7c99 | |||
| 8903857cd5 | |||
| 7da0ff0875 | |||
| 6c20751a0b | |||
| f3c02aaf56 | |||
| 7abcd68c0d | |||
| f5d53574b3 | |||
| 8072a0442b | |||
| b71b07816e | |||
| 5e462fc725 | |||
| a194a7c75e | |||
| 0664be6bd1 | |||
| b33939dd5b | |||
| ac3ed7eece | |||
| 040593d359 | |||
| 20382af75a | |||
| 3359805404 | |||
| 37718176fb | |||
| efd99af71e | |||
| 6cf70ab06c | |||
| 4a06671a14 | |||
| 75f4d24e12 | |||
| 08a9d254ce | |||
| d3afbd4b15 | |||
| 3d3870fbad | |||
| 1acee6ea64 | |||
| 2eb5db43bc | |||
| f184855906 | |||
| ae6ec1595e | |||
| e23776693c | |||
| 56d7ac9704 | |||
| 8e90f233fb | |||
| 1fd0a80836 | |||
| 392333c87b | |||
| f9daea40bd | |||
| 5581ea22e7 | |||
| d72a3996f6 | |||
| 37f65bd382 | |||
| 40dee4661c | |||
| dcb0104208 | |||
| 38d725ea03 | |||
| 5b69250cac | |||
| faf1a6755d | |||
| 2d51ca9538 | |||
| 78223aa635 |
+3
-3
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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.
@@ -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.
@@ -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...")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Regular → Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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))
|
||||
|
||||
@@ -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 ¤t, QWidget *parent)
|
||||
: DialogBase(parent), seriesToModels(seriesToModels) {
|
||||
const QMap<QString, QStringList> &seriesToModels,
|
||||
const QString ¤t, 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 ¤t, QWidget *parent) {
|
||||
ExpandableMultiOptionDialog d = ExpandableMultiOptionDialog(prompt_text, seriesToModels, current, parent);
|
||||
const QMap<QString, QStringList> &seriesToModels,
|
||||
const QString ¤t, 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 ¤t, QWidget *parent);
|
||||
const QString ¤t, 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 ¤t, QWidget *parent);
|
||||
const QString ¤t, 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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 60–0 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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
},
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}, \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>تعديلات Twilsonco المعتمدة على العزم لتنعيم التوجيه في المنعطفات.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>سلوك متابعة يحاكي السائقين البشر</b> عبر تقليل الفجوات خلف المركبات الأسرع لانطلاق أسرع وضبط مسافة المتابعة المطلوبة ديناميكياً لكبح ألطف وأكثر كفاءة.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>EV Tuning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Twilsonco make torque tweak. Steering smooth in curve.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2088,6 +2128,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>Make car follow like human</b>. 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><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Twilsoncos drehmomentbasierte Anpassungen zur Glättung der Lenkung in Kurven.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>Dem Fahrverhalten menschlicher Fahrer nachempfunden</b>, 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><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Quack! Twilsonco’s torque tweaks to smooth out steering in curves, waddle-waddle.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2088,6 +2128,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>Quack! Mimic human drivers</b> 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><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Ajustes basados en par de Twilsonco para suavizar la dirección en curvas.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>Comportamiento de seguimiento que imita a los conductores humanos</b> 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><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Ajustements basés sur le couple de Twilsonco pour adoucir la direction dans les virages.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>Un comportement de suivi qui imite les conducteurs humains</b> 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><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Ribbit! Twilsonco’s torque tweaks smooth steering through curves, croak.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>Croaky follow like humans</b>, 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><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>カーブでのステアリングを滑らかにするためのTwilsoncoのトルクベース調整。</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>人間のドライバーを模倣する追従挙動</b>。より速い車両の後方で車間を詰めて発進を素早くし、望ましい追従距離を動的に調整して、より穏やかで効率的な減速を実現します。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>EV Tuning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>커브에서 조향을 부드럽게 하기 위한 Twilsonco의 토크 기반 조정.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>사람 운전자와 유사한 추종 동작</b>으로, 더 빠른 차량 뒤 간격을 좁혀 신속한 출발을 돕고 원하는 추종 거리를 동적으로 조절해 더욱 부드럽고 효율적인 제동을 수행합니다.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>EV Tuning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Twilsonco’s torque-based tweaks t’ smooth out steerin’ in curves, arr!</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>Behavin’ like real hands at the wheel</b> by closin’ gaps astern o’ swifter wagons fer quicker shove-offs, an’ smartly trimmin’ the wanted followin’ distance fer gentler, more shipshape brak’n.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>EV Tuning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotModelReview</name>
|
||||
@@ -3714,6 +3806,22 @@ Developer - Highly customizable riggin’s 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Ajustes baseados em torque do Twilsonco para suavizar a direção em curvas.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>Comportamento de acompanhamento que imita motoristas humanos</b> 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><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Twilsonco’s torque-wrought tweaks to make steering flow smoother ’midst the curves.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2092,6 +2132,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>Following comportment that doth mirror mortal coachmen</b> 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><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>การปรับตามแรงบิดของ Twilsonco เพื่อทำให้การบังคับเลี้ยวในโค้งนุ่มนวลขึ้น</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>พฤติกรรมการตามที่เลียนแบบผู้ขับขี่มนุษย์</b> โดยปิดช่องว่างด้านหลังรถที่เร็วกว่าเพื่อออกตัวได้เร็วขึ้น และปรับระยะห่างที่ต้องการแบบไดนามิกเพื่อการเบรกที่นุ่มนวลและมีประสิทธิภาพมากขึ้น</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>EV Tuning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Virajlarda direksiyonu yumuşatmak için Twilsonco’nun torka dayalı ayarlamaları.</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>İnsan sürücüleri taklit eden takip davranışı</b>: 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><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Twilsonco 基于扭矩的调整,用于在弯道中平滑转向。</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>模仿人类驾驶员的跟车行为</b>,通过在更快车辆后方缩小车距以更快起步,并动态调整期望跟车距离,以实现更平顺、更高效的制动。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>EV Tuning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
@@ -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><b>Twilsonco's torque-based adjustments to smoothen out steering in curves.</b></source>
|
||||
<translation type="gpt-5-generated"><b>Twilsonco 的扭力式調整,可在轉彎時讓轉向更平順。</b></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><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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset <b>Steer Offset</b> to its default value?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotLongitudinalPanel</name>
|
||||
@@ -2086,6 +2126,42 @@
|
||||
<source><b>Following behavior that mimics human drivers</b> 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"><b>模仿人類駕駛的跟車行為</b>,在後方有更快車輛時縮小間距以更快起步,並動態調整期望跟車距離,以實現更柔順且更高效的制動。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>EV Tuning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Use acceleration profiles tuned for EVs.</b> 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><b>How many seconds openpilot follows behind lead vehicles when using the "Aggressive" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.25 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Standard" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.45 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>How many seconds openpilot follows behind lead vehicles when using the "Relaxed" profile at high speeds.</b> Increase for more space; decrease for tighter gaps.<br><br>Default: 1.75 seconds.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trailer Load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Increase the vehicle mass to account for towing.</b> 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 <b>Stop Distance</b> to the default of 6 meters?</source>
|
||||
<source>Are you sure you want to reset your <b>Recovery Power</b> 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 <b>Stop Distance</b> 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><b>Use the pedal interceptor for longitudinal control</b> 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><b>Does your vehicle support "SASCMs"?</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotVisualsPanel</name>
|
||||
|
||||
Binary file not shown.
@@ -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
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user