mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-24 01:33:46 +08:00
Compare commits
76 Commits
Kaofui
...
TRX-Backup
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d1a99f2af | |||
| a4d61232c6 | |||
| 5881c17c59 | |||
| d5e971c32b | |||
| 3024ed42f5 | |||
| 09a073c242 | |||
| 231ef85bed | |||
| 740668c973 | |||
| b343c6aa98 | |||
| cc1a0e4697 | |||
| c32a5fda39 | |||
| af14724e78 | |||
| c85a34f324 | |||
| 70260c0ed2 | |||
| 0110bec1af | |||
| 94d881211c | |||
| cea5b9cada | |||
| 4269bb2309 | |||
| 5cc94240cd | |||
| 201458c3d6 | |||
| 1af7a5ba65 | |||
| fb3d1a1392 | |||
| d650757ff9 | |||
| 91e273e489 | |||
| d620145cdc | |||
| 23a5bedabc | |||
| ea45cbf382 | |||
| a1f7b451a6 | |||
| 1fabfd04bd | |||
| 8630693f7a | |||
| 6df0333380 | |||
| 8708ab2496 | |||
| 01ed483036 | |||
| 23fb048ba6 | |||
| bee7628900 | |||
| 9b84afc5a8 | |||
| 5f94a5b9e0 | |||
| 41d69c31e7 | |||
| f6c89beebd | |||
| 62c1345b2a | |||
| 97bfbfbb0d | |||
| 67c9a8b52e | |||
| 2f253174e9 | |||
| 5a39fa4ac1 | |||
| 8a54eb5733 | |||
| a1edc6d6e7 | |||
| 05ded9d10b | |||
| d387c7934f | |||
| 4c9d393e37 | |||
| c14680edcb | |||
| 95ea985218 | |||
| 376aeff9e3 | |||
| 680c3acaf0 | |||
| 9cc2c20cdc | |||
| 8c81eadf5d | |||
| 88a29c67d0 | |||
| 6fe8f8b722 | |||
| b5b942a108 | |||
| 5e88e5beff | |||
| 8f27c4937c | |||
| c95f8fa8fc | |||
| 8b55472a90 | |||
| 9bcfeb1cf3 | |||
| 93713aaacf | |||
| 602641856d | |||
| 1f0d2092a3 | |||
| 587dc140b7 | |||
| 63ee52b4aa | |||
| eb19b336e2 | |||
| 06f95d0dcd | |||
| cd7f2f8bc6 | |||
| 82af86e04c | |||
| 6934f375d5 | |||
| 09afc383fd | |||
| 19f7685744 | |||
| 6477ba6a5e |
+3
-2
@@ -532,11 +532,12 @@ struct CarParams {
|
||||
useSteeringAngle @0 :Bool;
|
||||
kp @1 :Float32;
|
||||
ki @2 :Float32;
|
||||
kd @8 : Float32;
|
||||
friction @3 :Float32;
|
||||
kf @4 :Float32;
|
||||
steeringAngleDeadzoneDeg @5 :Float32;
|
||||
latAccelFactor @6 :Float32;
|
||||
latAccelOffset @7 :Float32;
|
||||
kfDEPRECATED @4 :Float32;
|
||||
}
|
||||
|
||||
struct LongitudinalPIDTuning {
|
||||
@@ -544,7 +545,7 @@ struct CarParams {
|
||||
kpV @1 :List(Float32);
|
||||
kiBP @2 :List(Float32);
|
||||
kiV @3 :List(Float32);
|
||||
kf @6 :Float32;
|
||||
kfDEPRECATED @6 :Float32;
|
||||
deadzoneBP @4 :List(Float32);
|
||||
deadzoneV @5 :List(Float32);
|
||||
}
|
||||
|
||||
+30
-33
@@ -105,11 +105,7 @@ struct FrogPilotCarParams @0xf35cc4560bbf6ec2 {
|
||||
isHDA2 @3 :Bool;
|
||||
openpilotLongitudinalControlDisabled @4 :Bool;
|
||||
safetyConfigs @5 :List(SafetyConfig);
|
||||
|
||||
lateralTuning :union {
|
||||
pid @6 :Car.CarParams.LateralPIDTuning;
|
||||
torque @7 :Car.CarParams.LateralTorqueTuning;
|
||||
}
|
||||
canUseSASCM @6 :Bool;
|
||||
|
||||
struct SafetyConfig {
|
||||
safetyParam @0 :UInt16;
|
||||
@@ -193,34 +189,35 @@ struct FrogPilotPlan @0xa1680744031fdb2d {
|
||||
cscTraining @4 :Bool;
|
||||
dangerJerk @5 :Float32;
|
||||
desiredFollowDistance @6 :Int64;
|
||||
experimentalMode @7 :Bool;
|
||||
forcingStop @8 :Bool;
|
||||
forcingStopLength @9 :Float32;
|
||||
frogpilotEvents @10 :List(FrogPilotCarEvent);
|
||||
increasedStoppedDistance @11 :Float32;
|
||||
lateralCheck @12 :Bool;
|
||||
laneWidthLeft @13 :Float32;
|
||||
laneWidthRight @14 :Float32;
|
||||
maxAcceleration @15 :Float32;
|
||||
minAcceleration @16 :Float32;
|
||||
redLight @17 :Bool;
|
||||
roadCurvature @18 :Float32;
|
||||
slcMapSpeedLimit @19 :Float32;
|
||||
slcMapboxSpeedLimit @20 :Float32;
|
||||
slcNextSpeedLimit @21 :Float32;
|
||||
slcOverriddenSpeed @22 :Float32;
|
||||
slcSpeedLimit @23 :Float32;
|
||||
slcSpeedLimitOffset @24 :Float32;
|
||||
slcSpeedLimitSource @25 :Text;
|
||||
speedJerk @26 :Float32;
|
||||
speedJerkStock @27 :Float32;
|
||||
speedLimitChanged @28 :Bool;
|
||||
tFollow @29 :Float32;
|
||||
themeUpdated @30 :Bool;
|
||||
togglesUpdated @31 :Bool;
|
||||
trackingLead @32 :Bool;
|
||||
unconfirmedSlcSpeedLimit @33 :Float32;
|
||||
vCruise @34 :Float32;
|
||||
disableThrottle @7 :Bool;
|
||||
experimentalMode @8 :Bool;
|
||||
forcingStop @9 :Bool;
|
||||
forcingStopLength @10 :Float32;
|
||||
frogpilotEvents @11 :List(FrogPilotCarEvent);
|
||||
increasedStoppedDistance @12 :Float32;
|
||||
lateralCheck @13 :Bool;
|
||||
laneWidthLeft @14 :Float32;
|
||||
laneWidthRight @15 :Float32;
|
||||
maxAcceleration @16 :Float32;
|
||||
minAcceleration @17 :Float32;
|
||||
redLight @18 :Bool;
|
||||
roadCurvature @19 :Float32;
|
||||
slcMapSpeedLimit @20 :Float32;
|
||||
slcMapboxSpeedLimit @21 :Float32;
|
||||
slcNextSpeedLimit @22 :Float32;
|
||||
slcOverriddenSpeed @23 :Float32;
|
||||
slcSpeedLimit @24 :Float32;
|
||||
slcSpeedLimitOffset @25 :Float32;
|
||||
slcSpeedLimitSource @26 :Text;
|
||||
speedJerk @27 :Float32;
|
||||
speedJerkStock @28 :Float32;
|
||||
speedLimitChanged @29 :Bool;
|
||||
tFollow @30 :Float32;
|
||||
themeUpdated @31 :Bool;
|
||||
togglesUpdated @32 :Bool;
|
||||
trackingLead @33 :Bool;
|
||||
unconfirmedSlcSpeedLimit @34 :Float32;
|
||||
vCruise @35 :Float32;
|
||||
}
|
||||
|
||||
struct FrogPilotRadarState @0xcb9fd56c7057593a {
|
||||
|
||||
@@ -5262,17 +5262,17 @@ const ::capnp::_::RawSchema s_9622723fcbd14c2e = {
|
||||
0, 5, i_9622723fcbd14c2e, nullptr, nullptr, { &s_9622723fcbd14c2e, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<147> b_80366e0e804ecc1d = {
|
||||
static const ::capnp::_::AlignedData<163> b_80366e0e804ecc1d = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
29, 204, 78, 128, 14, 110, 54, 128,
|
||||
20, 0, 0, 0, 1, 0, 4, 0,
|
||||
20, 0, 0, 0, 1, 0, 5, 0,
|
||||
218, 169, 170, 144, 36, 55, 105, 140,
|
||||
0, 0, 7, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 66, 1, 0, 0,
|
||||
37, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 0, 0, 199, 1, 0, 0,
|
||||
33, 0, 0, 0, 255, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 97, 114, 46, 99, 97, 112, 110,
|
||||
@@ -5281,63 +5281,70 @@ static const ::capnp::_::AlignedData<147> b_80366e0e804ecc1d = {
|
||||
114, 97, 108, 84, 111, 114, 113, 117,
|
||||
101, 84, 117, 110, 105, 110, 103, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
32, 0, 0, 0, 3, 0, 4, 0,
|
||||
36, 0, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
209, 0, 0, 0, 138, 0, 0, 0,
|
||||
237, 0, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
212, 0, 0, 0, 3, 0, 1, 0,
|
||||
224, 0, 0, 0, 2, 0, 1, 0,
|
||||
240, 0, 0, 0, 3, 0, 1, 0,
|
||||
252, 0, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
221, 0, 0, 0, 26, 0, 0, 0,
|
||||
249, 0, 0, 0, 26, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
216, 0, 0, 0, 3, 0, 1, 0,
|
||||
228, 0, 0, 0, 2, 0, 1, 0,
|
||||
244, 0, 0, 0, 3, 0, 1, 0,
|
||||
0, 1, 0, 0, 2, 0, 1, 0,
|
||||
2, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 1, 0, 2, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
225, 0, 0, 0, 26, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
220, 0, 0, 0, 3, 0, 1, 0,
|
||||
232, 0, 0, 0, 2, 0, 1, 0,
|
||||
3, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
229, 0, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
228, 0, 0, 0, 3, 0, 1, 0,
|
||||
240, 0, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
237, 0, 0, 0, 26, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
232, 0, 0, 0, 3, 0, 1, 0,
|
||||
244, 0, 0, 0, 2, 0, 1, 0,
|
||||
5, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
241, 0, 0, 0, 202, 0, 0, 0,
|
||||
253, 0, 0, 0, 26, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
248, 0, 0, 0, 3, 0, 1, 0,
|
||||
4, 1, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 0, 0, 6, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
4, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 0, 0, 122, 0, 0, 0,
|
||||
1, 1, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 3, 0, 1, 0,
|
||||
12, 1, 0, 0, 2, 0, 1, 0,
|
||||
7, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 7, 0, 0, 0,
|
||||
8, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 1, 0, 0, 122, 0, 0, 0,
|
||||
9, 1, 0, 0, 106, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
8, 1, 0, 0, 3, 0, 1, 0,
|
||||
20, 1, 0, 0, 2, 0, 1, 0,
|
||||
5, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 1, 0, 0, 202, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
24, 1, 0, 0, 3, 0, 1, 0,
|
||||
36, 1, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 0, 0, 6, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 1, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 1, 0, 0, 3, 0, 1, 0,
|
||||
44, 1, 0, 0, 2, 0, 1, 0,
|
||||
7, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 1, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
40, 1, 0, 0, 3, 0, 1, 0,
|
||||
52, 1, 0, 0, 2, 0, 1, 0,
|
||||
3, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 8, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 1, 0, 0, 26, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
44, 1, 0, 0, 3, 0, 1, 0,
|
||||
56, 1, 0, 0, 2, 0, 1, 0,
|
||||
117, 115, 101, 83, 116, 101, 101, 114,
|
||||
105, 110, 103, 65, 110, 103, 108, 101,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -5373,7 +5380,8 @@ static const ::capnp::_::AlignedData<147> b_80366e0e804ecc1d = {
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
107, 102, 0, 0, 0, 0, 0, 0,
|
||||
107, 102, 68, 69, 80, 82, 69, 67,
|
||||
65, 84, 69, 68, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -5403,6 +5411,14 @@ static const ::capnp::_::AlignedData<147> b_80366e0e804ecc1d = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
108, 97, 116, 65, 99, 99, 101, 108,
|
||||
79, 102, 102, 115, 101, 116, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
107, 100, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -5413,14 +5429,14 @@ static const ::capnp::_::AlignedData<147> b_80366e0e804ecc1d = {
|
||||
};
|
||||
::capnp::word const* const bp_80366e0e804ecc1d = b_80366e0e804ecc1d.words;
|
||||
#if !CAPNP_LITE
|
||||
static const uint16_t m_80366e0e804ecc1d[] = {3, 4, 2, 1, 6, 7, 5, 0};
|
||||
static const uint16_t i_80366e0e804ecc1d[] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
static const uint16_t m_80366e0e804ecc1d[] = {3, 8, 4, 2, 1, 6, 7, 5, 0};
|
||||
static const uint16_t i_80366e0e804ecc1d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
const ::capnp::_::RawSchema s_80366e0e804ecc1d = {
|
||||
0x80366e0e804ecc1d, b_80366e0e804ecc1d.words, 147, nullptr, m_80366e0e804ecc1d,
|
||||
0, 8, i_80366e0e804ecc1d, nullptr, nullptr, { &s_80366e0e804ecc1d, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
0x80366e0e804ecc1d, b_80366e0e804ecc1d.words, 163, nullptr, m_80366e0e804ecc1d,
|
||||
0, 9, i_80366e0e804ecc1d, nullptr, nullptr, { &s_80366e0e804ecc1d, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<151> b_c342cefc303e9b8e = {
|
||||
static const ::capnp::_::AlignedData<152> b_c342cefc303e9b8e = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
142, 155, 62, 48, 252, 206, 66, 195,
|
||||
20, 0, 0, 0, 1, 0, 1, 0,
|
||||
@@ -5486,10 +5502,10 @@ static const ::capnp::_::AlignedData<151> b_c342cefc303e9b8e = {
|
||||
4, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
53, 1, 0, 0, 26, 0, 0, 0,
|
||||
53, 1, 0, 0, 106, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
48, 1, 0, 0, 3, 0, 1, 0,
|
||||
60, 1, 0, 0, 2, 0, 1, 0,
|
||||
52, 1, 0, 0, 3, 0, 1, 0,
|
||||
64, 1, 0, 0, 2, 0, 1, 0,
|
||||
107, 112, 66, 80, 0, 0, 0, 0,
|
||||
14, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -5564,7 +5580,8 @@ static const ::capnp::_::AlignedData<151> b_c342cefc303e9b8e = {
|
||||
14, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
107, 102, 0, 0, 0, 0, 0, 0,
|
||||
107, 102, 68, 69, 80, 82, 69, 67,
|
||||
65, 84, 69, 68, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -5578,7 +5595,7 @@ static const ::capnp::_::AlignedData<151> b_c342cefc303e9b8e = {
|
||||
static const uint16_t m_c342cefc303e9b8e[] = {4, 5, 6, 2, 3, 0, 1};
|
||||
static const uint16_t i_c342cefc303e9b8e[] = {0, 1, 2, 3, 4, 5, 6};
|
||||
const ::capnp::_::RawSchema s_c342cefc303e9b8e = {
|
||||
0xc342cefc303e9b8e, b_c342cefc303e9b8e.words, 151, nullptr, m_c342cefc303e9b8e,
|
||||
0xc342cefc303e9b8e, b_c342cefc303e9b8e.words, 152, nullptr, m_c342cefc303e9b8e,
|
||||
0, 7, i_c342cefc303e9b8e, nullptr, nullptr, { &s_c342cefc303e9b8e, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
+32
-13
@@ -626,7 +626,7 @@ struct CarParams::LateralTorqueTuning {
|
||||
class Pipeline;
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(80366e0e804ecc1d, 4, 0)
|
||||
CAPNP_DECLARE_STRUCT_HEADER(80366e0e804ecc1d, 5, 0)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -3140,7 +3140,7 @@ public:
|
||||
|
||||
inline float getFriction() const;
|
||||
|
||||
inline float getKf() const;
|
||||
inline float getKfDEPRECATED() const;
|
||||
|
||||
inline float getSteeringAngleDeadzoneDeg() const;
|
||||
|
||||
@@ -3148,6 +3148,8 @@ public:
|
||||
|
||||
inline float getLatAccelOffset() const;
|
||||
|
||||
inline float getKd() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -3188,8 +3190,8 @@ public:
|
||||
inline float getFriction();
|
||||
inline void setFriction(float value);
|
||||
|
||||
inline float getKf();
|
||||
inline void setKf(float value);
|
||||
inline float getKfDEPRECATED();
|
||||
inline void setKfDEPRECATED(float value);
|
||||
|
||||
inline float getSteeringAngleDeadzoneDeg();
|
||||
inline void setSteeringAngleDeadzoneDeg(float value);
|
||||
@@ -3200,6 +3202,9 @@ public:
|
||||
inline float getLatAccelOffset();
|
||||
inline void setLatAccelOffset(float value);
|
||||
|
||||
inline float getKd();
|
||||
inline void setKd(float value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -3261,7 +3266,7 @@ public:
|
||||
inline bool hasDeadzoneV() const;
|
||||
inline ::capnp::List<float, ::capnp::Kind::PRIMITIVE>::Reader getDeadzoneV() const;
|
||||
|
||||
inline float getKf() const;
|
||||
inline float getKfDEPRECATED() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
@@ -3339,8 +3344,8 @@ public:
|
||||
inline void adoptDeadzoneV(::capnp::Orphan< ::capnp::List<float, ::capnp::Kind::PRIMITIVE>>&& value);
|
||||
inline ::capnp::Orphan< ::capnp::List<float, ::capnp::Kind::PRIMITIVE>> disownDeadzoneV();
|
||||
|
||||
inline float getKf();
|
||||
inline void setKf(float value);
|
||||
inline float getKfDEPRECATED();
|
||||
inline void setKfDEPRECATED(float value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
@@ -7791,16 +7796,16 @@ inline void CarParams::LateralTorqueTuning::Builder::setFriction(float value) {
|
||||
::capnp::bounded<3>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float CarParams::LateralTorqueTuning::Reader::getKf() const {
|
||||
inline float CarParams::LateralTorqueTuning::Reader::getKfDEPRECATED() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float CarParams::LateralTorqueTuning::Builder::getKf() {
|
||||
inline float CarParams::LateralTorqueTuning::Builder::getKfDEPRECATED() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void CarParams::LateralTorqueTuning::Builder::setKf(float value) {
|
||||
inline void CarParams::LateralTorqueTuning::Builder::setKfDEPRECATED(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
@@ -7847,6 +7852,20 @@ inline void CarParams::LateralTorqueTuning::Builder::setLatAccelOffset(float val
|
||||
::capnp::bounded<7>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float CarParams::LateralTorqueTuning::Reader::getKd() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<8>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float CarParams::LateralTorqueTuning::Builder::getKd() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<8>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void CarParams::LateralTorqueTuning::Builder::setKd(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<8>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool CarParams::LongitudinalPIDTuning::Reader::hasKpBP() const {
|
||||
return !_reader.getPointerField(
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
|
||||
@@ -8075,16 +8094,16 @@ inline ::capnp::Orphan< ::capnp::List<float, ::capnp::Kind::PRIMITIVE>> CarPara
|
||||
::capnp::bounded<5>() * ::capnp::POINTERS));
|
||||
}
|
||||
|
||||
inline float CarParams::LongitudinalPIDTuning::Reader::getKf() const {
|
||||
inline float CarParams::LongitudinalPIDTuning::Reader::getKfDEPRECATED() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float CarParams::LongitudinalPIDTuning::Builder::getKf() {
|
||||
inline float CarParams::LongitudinalPIDTuning::Builder::getKfDEPRECATED() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void CarParams::LongitudinalPIDTuning::Builder::setKf(float value) {
|
||||
inline void CarParams::LongitudinalPIDTuning::Builder::setKfDEPRECATED(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
+197
-252
@@ -642,12 +642,12 @@ const ::capnp::_::RawSchema s_aedffd8f31e7b55d = {
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
CAPNP_DEFINE_ENUM(EventName_aedffd8f31e7b55d, aedffd8f31e7b55d);
|
||||
static const ::capnp::_::AlignedData<132> b_f35cc4560bbf6ec2 = {
|
||||
static const ::capnp::_::AlignedData<139> b_f35cc4560bbf6ec2 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
194, 110, 191, 11, 86, 196, 92, 243,
|
||||
13, 0, 0, 0, 1, 0, 1, 0,
|
||||
89, 10, 85, 29, 102, 186, 38, 181,
|
||||
2, 0, 7, 0, 0, 0, 0, 0,
|
||||
1, 0, 7, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 2, 1, 0, 0,
|
||||
33, 0, 0, 0, 23, 0, 0, 0,
|
||||
@@ -707,13 +707,13 @@ static const ::capnp::_::AlignedData<132> b_f35cc4560bbf6ec2 = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
224, 0, 0, 0, 3, 0, 1, 0,
|
||||
252, 0, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
73, 118, 234, 203, 210, 73, 201, 252,
|
||||
249, 0, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
6, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
249, 0, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
248, 0, 0, 0, 3, 0, 1, 0,
|
||||
4, 1, 0, 0, 2, 0, 1, 0,
|
||||
99, 97, 110, 85, 115, 101, 80, 101,
|
||||
100, 97, 108, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -773,20 +773,26 @@ static const ::capnp::_::AlignedData<132> b_f35cc4560bbf6ec2 = {
|
||||
14, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
108, 97, 116, 101, 114, 97, 108, 84,
|
||||
117, 110, 105, 110, 103, 0, 0, 0, }
|
||||
99, 97, 110, 85, 115, 101, 83, 65,
|
||||
83, 67, 77, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_f35cc4560bbf6ec2 = b_f35cc4560bbf6ec2.words;
|
||||
#if !CAPNP_LITE
|
||||
static const ::capnp::_::RawSchema* const d_f35cc4560bbf6ec2[] = {
|
||||
&s_8d65dd40bad40951,
|
||||
&s_fcc949d2cbea7649,
|
||||
};
|
||||
static const uint16_t m_f35cc4560bbf6ec2[] = {0, 1, 2, 3, 6, 4, 5};
|
||||
static const uint16_t m_f35cc4560bbf6ec2[] = {0, 6, 1, 2, 3, 4, 5};
|
||||
static const uint16_t i_f35cc4560bbf6ec2[] = {0, 1, 2, 3, 4, 5, 6};
|
||||
const ::capnp::_::RawSchema s_f35cc4560bbf6ec2 = {
|
||||
0xf35cc4560bbf6ec2, b_f35cc4560bbf6ec2.words, 132, d_f35cc4560bbf6ec2, m_f35cc4560bbf6ec2,
|
||||
2, 7, i_f35cc4560bbf6ec2, nullptr, nullptr, { &s_f35cc4560bbf6ec2, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
0xf35cc4560bbf6ec2, b_f35cc4560bbf6ec2.words, 139, d_f35cc4560bbf6ec2, m_f35cc4560bbf6ec2,
|
||||
1, 7, i_f35cc4560bbf6ec2, nullptr, nullptr, { &s_f35cc4560bbf6ec2, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<36> b_8d65dd40bad40951 = {
|
||||
@@ -836,71 +842,6 @@ const ::capnp::_::RawSchema s_8d65dd40bad40951 = {
|
||||
0, 1, i_8d65dd40bad40951, nullptr, nullptr, { &s_8d65dd40bad40951, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<49> b_fcc949d2cbea7649 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
73, 118, 234, 203, 210, 73, 201, 252,
|
||||
32, 0, 0, 0, 1, 0, 1, 0,
|
||||
194, 110, 191, 11, 86, 196, 92, 243,
|
||||
2, 0, 7, 0, 1, 0, 2, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 114, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 0, 0, 0, 119, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 117, 115, 116, 111, 109, 46, 99,
|
||||
97, 112, 110, 112, 58, 70, 114, 111,
|
||||
103, 80, 105, 108, 111, 116, 67, 97,
|
||||
114, 80, 97, 114, 97, 109, 115, 46,
|
||||
108, 97, 116, 101, 114, 97, 108, 84,
|
||||
117, 110, 105, 110, 103, 0, 0, 0,
|
||||
8, 0, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 255, 255, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 0, 0, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
36, 0, 0, 0, 3, 0, 1, 0,
|
||||
48, 0, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 254, 255, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
45, 0, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
40, 0, 0, 0, 3, 0, 1, 0,
|
||||
52, 0, 0, 0, 2, 0, 1, 0,
|
||||
112, 105, 100, 0, 0, 0, 0, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
46, 76, 209, 203, 63, 114, 34, 150,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
116, 111, 114, 113, 117, 101, 0, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 204, 78, 128, 14, 110, 54, 128,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
16, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_fcc949d2cbea7649 = b_fcc949d2cbea7649.words;
|
||||
#if !CAPNP_LITE
|
||||
static const ::capnp::_::RawSchema* const d_fcc949d2cbea7649[] = {
|
||||
&s_80366e0e804ecc1d,
|
||||
&s_9622723fcbd14c2e,
|
||||
&s_f35cc4560bbf6ec2,
|
||||
};
|
||||
static const uint16_t m_fcc949d2cbea7649[] = {0, 1};
|
||||
static const uint16_t i_fcc949d2cbea7649[] = {0, 1};
|
||||
const ::capnp::_::RawSchema s_fcc949d2cbea7649 = {
|
||||
0xfcc949d2cbea7649, b_fcc949d2cbea7649.words, 49, d_fcc949d2cbea7649, m_fcc949d2cbea7649,
|
||||
3, 2, i_fcc949d2cbea7649, nullptr, nullptr, { &s_fcc949d2cbea7649, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<268> b_da96579883444c35 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
53, 76, 68, 131, 152, 87, 150, 218,
|
||||
@@ -1739,7 +1680,7 @@ const ::capnp::_::RawSchema s_f416ec09499d9d19 = {
|
||||
0, 3, i_f416ec09499d9d19, nullptr, nullptr, { &s_f416ec09499d9d19, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<597> b_a1680744031fdb2d = {
|
||||
static const ::capnp::_::AlignedData<613> b_a1680744031fdb2d = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
45, 219, 31, 3, 68, 7, 104, 161,
|
||||
13, 0, 0, 0, 1, 0, 13, 0,
|
||||
@@ -1749,7 +1690,7 @@ static const ::capnp::_::AlignedData<597> b_a1680744031fdb2d = {
|
||||
21, 0, 0, 0, 218, 0, 0, 0,
|
||||
33, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 0, 0, 0, 175, 7, 0, 0,
|
||||
29, 0, 0, 0, 231, 7, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
99, 117, 115, 116, 111, 109, 46, 99,
|
||||
@@ -1757,252 +1698,259 @@ static const ::capnp::_::AlignedData<597> b_a1680744031fdb2d = {
|
||||
103, 80, 105, 108, 111, 116, 80, 108,
|
||||
97, 110, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
140, 0, 0, 0, 3, 0, 4, 0,
|
||||
144, 0, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
197, 3, 0, 0, 138, 0, 0, 0,
|
||||
225, 3, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
200, 3, 0, 0, 3, 0, 1, 0,
|
||||
212, 3, 0, 0, 2, 0, 1, 0,
|
||||
228, 3, 0, 0, 3, 0, 1, 0,
|
||||
240, 3, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
209, 3, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
212, 3, 0, 0, 3, 0, 1, 0,
|
||||
224, 3, 0, 0, 2, 0, 1, 0,
|
||||
2, 0, 0, 0, 64, 0, 0, 0,
|
||||
0, 0, 1, 0, 2, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
221, 3, 0, 0, 162, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
224, 3, 0, 0, 3, 0, 1, 0,
|
||||
236, 3, 0, 0, 2, 0, 1, 0,
|
||||
3, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
233, 3, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
232, 3, 0, 0, 3, 0, 1, 0,
|
||||
244, 3, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 65, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
241, 3, 0, 0, 98, 0, 0, 0,
|
||||
237, 3, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
240, 3, 0, 0, 3, 0, 1, 0,
|
||||
252, 3, 0, 0, 2, 0, 1, 0,
|
||||
5, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
2, 0, 0, 0, 64, 0, 0, 0,
|
||||
0, 0, 1, 0, 2, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
249, 3, 0, 0, 90, 0, 0, 0,
|
||||
249, 3, 0, 0, 162, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
248, 3, 0, 0, 3, 0, 1, 0,
|
||||
4, 4, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
252, 3, 0, 0, 3, 0, 1, 0,
|
||||
8, 4, 0, 0, 2, 0, 1, 0,
|
||||
3, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 4, 0, 0, 178, 0, 0, 0,
|
||||
5, 4, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
4, 4, 0, 0, 3, 0, 1, 0,
|
||||
16, 4, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 65, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
13, 4, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 4, 0, 0, 3, 0, 1, 0,
|
||||
24, 4, 0, 0, 2, 0, 1, 0,
|
||||
5, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 4, 0, 0, 90, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
20, 4, 0, 0, 3, 0, 1, 0,
|
||||
32, 4, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 4, 0, 0, 178, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 4, 0, 0, 3, 0, 1, 0,
|
||||
44, 4, 0, 0, 2, 0, 1, 0,
|
||||
7, 0, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 1, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
13, 4, 0, 0, 138, 0, 0, 0,
|
||||
41, 4, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
16, 4, 0, 0, 3, 0, 1, 0,
|
||||
28, 4, 0, 0, 2, 0, 1, 0,
|
||||
40, 4, 0, 0, 3, 0, 1, 0,
|
||||
52, 4, 0, 0, 2, 0, 1, 0,
|
||||
8, 0, 0, 0, 67, 0, 0, 0,
|
||||
0, 0, 1, 0, 8, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
25, 4, 0, 0, 98, 0, 0, 0,
|
||||
49, 4, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
24, 4, 0, 0, 3, 0, 1, 0,
|
||||
36, 4, 0, 0, 2, 0, 1, 0,
|
||||
9, 0, 0, 0, 5, 0, 0, 0,
|
||||
52, 4, 0, 0, 3, 0, 1, 0,
|
||||
64, 4, 0, 0, 2, 0, 1, 0,
|
||||
9, 0, 0, 0, 68, 0, 0, 0,
|
||||
0, 0, 1, 0, 9, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
33, 4, 0, 0, 146, 0, 0, 0,
|
||||
61, 4, 0, 0, 98, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
36, 4, 0, 0, 3, 0, 1, 0,
|
||||
48, 4, 0, 0, 2, 0, 1, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
60, 4, 0, 0, 3, 0, 1, 0,
|
||||
72, 4, 0, 0, 2, 0, 1, 0,
|
||||
10, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 10, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
45, 4, 0, 0, 130, 0, 0, 0,
|
||||
69, 4, 0, 0, 146, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
44, 4, 0, 0, 3, 0, 1, 0,
|
||||
72, 4, 0, 0, 2, 0, 1, 0,
|
||||
11, 0, 0, 0, 8, 0, 0, 0,
|
||||
72, 4, 0, 0, 3, 0, 1, 0,
|
||||
84, 4, 0, 0, 2, 0, 1, 0,
|
||||
11, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 11, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
69, 4, 0, 0, 202, 0, 0, 0,
|
||||
81, 4, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
76, 4, 0, 0, 3, 0, 1, 0,
|
||||
88, 4, 0, 0, 2, 0, 1, 0,
|
||||
12, 0, 0, 0, 68, 0, 0, 0,
|
||||
80, 4, 0, 0, 3, 0, 1, 0,
|
||||
108, 4, 0, 0, 2, 0, 1, 0,
|
||||
12, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 12, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
85, 4, 0, 0, 106, 0, 0, 0,
|
||||
105, 4, 0, 0, 202, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
84, 4, 0, 0, 3, 0, 1, 0,
|
||||
96, 4, 0, 0, 2, 0, 1, 0,
|
||||
13, 0, 0, 0, 9, 0, 0, 0,
|
||||
112, 4, 0, 0, 3, 0, 1, 0,
|
||||
124, 4, 0, 0, 2, 0, 1, 0,
|
||||
13, 0, 0, 0, 69, 0, 0, 0,
|
||||
0, 0, 1, 0, 13, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
93, 4, 0, 0, 114, 0, 0, 0,
|
||||
121, 4, 0, 0, 106, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
92, 4, 0, 0, 3, 0, 1, 0,
|
||||
104, 4, 0, 0, 2, 0, 1, 0,
|
||||
14, 0, 0, 0, 10, 0, 0, 0,
|
||||
120, 4, 0, 0, 3, 0, 1, 0,
|
||||
132, 4, 0, 0, 2, 0, 1, 0,
|
||||
14, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 14, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
101, 4, 0, 0, 122, 0, 0, 0,
|
||||
129, 4, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
100, 4, 0, 0, 3, 0, 1, 0,
|
||||
112, 4, 0, 0, 2, 0, 1, 0,
|
||||
15, 0, 0, 0, 11, 0, 0, 0,
|
||||
128, 4, 0, 0, 3, 0, 1, 0,
|
||||
140, 4, 0, 0, 2, 0, 1, 0,
|
||||
15, 0, 0, 0, 10, 0, 0, 0,
|
||||
0, 0, 1, 0, 15, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
109, 4, 0, 0, 130, 0, 0, 0,
|
||||
137, 4, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
108, 4, 0, 0, 3, 0, 1, 0,
|
||||
120, 4, 0, 0, 2, 0, 1, 0,
|
||||
16, 0, 0, 0, 12, 0, 0, 0,
|
||||
136, 4, 0, 0, 3, 0, 1, 0,
|
||||
148, 4, 0, 0, 2, 0, 1, 0,
|
||||
16, 0, 0, 0, 11, 0, 0, 0,
|
||||
0, 0, 1, 0, 16, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
117, 4, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
116, 4, 0, 0, 3, 0, 1, 0,
|
||||
128, 4, 0, 0, 2, 0, 1, 0,
|
||||
17, 0, 0, 0, 69, 0, 0, 0,
|
||||
0, 0, 1, 0, 17, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
125, 4, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
124, 4, 0, 0, 3, 0, 1, 0,
|
||||
136, 4, 0, 0, 2, 0, 1, 0,
|
||||
18, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 1, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
133, 4, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
132, 4, 0, 0, 3, 0, 1, 0,
|
||||
144, 4, 0, 0, 2, 0, 1, 0,
|
||||
19, 0, 0, 0, 14, 0, 0, 0,
|
||||
0, 0, 1, 0, 19, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
141, 4, 0, 0, 138, 0, 0, 0,
|
||||
145, 4, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
144, 4, 0, 0, 3, 0, 1, 0,
|
||||
156, 4, 0, 0, 2, 0, 1, 0,
|
||||
20, 0, 0, 0, 15, 0, 0, 0,
|
||||
0, 0, 1, 0, 20, 0, 0, 0,
|
||||
17, 0, 0, 0, 12, 0, 0, 0,
|
||||
0, 0, 1, 0, 17, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
153, 4, 0, 0, 162, 0, 0, 0,
|
||||
153, 4, 0, 0, 130, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
156, 4, 0, 0, 3, 0, 1, 0,
|
||||
168, 4, 0, 0, 2, 0, 1, 0,
|
||||
21, 0, 0, 0, 16, 0, 0, 0,
|
||||
0, 0, 1, 0, 21, 0, 0, 0,
|
||||
152, 4, 0, 0, 3, 0, 1, 0,
|
||||
164, 4, 0, 0, 2, 0, 1, 0,
|
||||
18, 0, 0, 0, 70, 0, 0, 0,
|
||||
0, 0, 1, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
165, 4, 0, 0, 146, 0, 0, 0,
|
||||
161, 4, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
160, 4, 0, 0, 3, 0, 1, 0,
|
||||
172, 4, 0, 0, 2, 0, 1, 0,
|
||||
19, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 1, 0, 19, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
169, 4, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
168, 4, 0, 0, 3, 0, 1, 0,
|
||||
180, 4, 0, 0, 2, 0, 1, 0,
|
||||
22, 0, 0, 0, 17, 0, 0, 0,
|
||||
0, 0, 1, 0, 22, 0, 0, 0,
|
||||
20, 0, 0, 0, 14, 0, 0, 0,
|
||||
0, 0, 1, 0, 20, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
177, 4, 0, 0, 154, 0, 0, 0,
|
||||
177, 4, 0, 0, 138, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
180, 4, 0, 0, 3, 0, 1, 0,
|
||||
192, 4, 0, 0, 2, 0, 1, 0,
|
||||
23, 0, 0, 0, 18, 0, 0, 0,
|
||||
21, 0, 0, 0, 15, 0, 0, 0,
|
||||
0, 0, 1, 0, 21, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
189, 4, 0, 0, 162, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
192, 4, 0, 0, 3, 0, 1, 0,
|
||||
204, 4, 0, 0, 2, 0, 1, 0,
|
||||
22, 0, 0, 0, 16, 0, 0, 0,
|
||||
0, 0, 1, 0, 22, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
201, 4, 0, 0, 146, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
204, 4, 0, 0, 3, 0, 1, 0,
|
||||
216, 4, 0, 0, 2, 0, 1, 0,
|
||||
23, 0, 0, 0, 17, 0, 0, 0,
|
||||
0, 0, 1, 0, 23, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
189, 4, 0, 0, 114, 0, 0, 0,
|
||||
213, 4, 0, 0, 154, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
188, 4, 0, 0, 3, 0, 1, 0,
|
||||
200, 4, 0, 0, 2, 0, 1, 0,
|
||||
24, 0, 0, 0, 19, 0, 0, 0,
|
||||
216, 4, 0, 0, 3, 0, 1, 0,
|
||||
228, 4, 0, 0, 2, 0, 1, 0,
|
||||
24, 0, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 1, 0, 24, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
197, 4, 0, 0, 162, 0, 0, 0,
|
||||
225, 4, 0, 0, 114, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
200, 4, 0, 0, 3, 0, 1, 0,
|
||||
212, 4, 0, 0, 2, 0, 1, 0,
|
||||
25, 0, 0, 0, 1, 0, 0, 0,
|
||||
224, 4, 0, 0, 3, 0, 1, 0,
|
||||
236, 4, 0, 0, 2, 0, 1, 0,
|
||||
25, 0, 0, 0, 19, 0, 0, 0,
|
||||
0, 0, 1, 0, 25, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
209, 4, 0, 0, 162, 0, 0, 0,
|
||||
233, 4, 0, 0, 162, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
212, 4, 0, 0, 3, 0, 1, 0,
|
||||
224, 4, 0, 0, 2, 0, 1, 0,
|
||||
26, 0, 0, 0, 20, 0, 0, 0,
|
||||
236, 4, 0, 0, 3, 0, 1, 0,
|
||||
248, 4, 0, 0, 2, 0, 1, 0,
|
||||
26, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 26, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
221, 4, 0, 0, 82, 0, 0, 0,
|
||||
245, 4, 0, 0, 162, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
220, 4, 0, 0, 3, 0, 1, 0,
|
||||
232, 4, 0, 0, 2, 0, 1, 0,
|
||||
27, 0, 0, 0, 21, 0, 0, 0,
|
||||
248, 4, 0, 0, 3, 0, 1, 0,
|
||||
4, 5, 0, 0, 2, 0, 1, 0,
|
||||
27, 0, 0, 0, 20, 0, 0, 0,
|
||||
0, 0, 1, 0, 27, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
229, 4, 0, 0, 122, 0, 0, 0,
|
||||
1, 5, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
228, 4, 0, 0, 3, 0, 1, 0,
|
||||
240, 4, 0, 0, 2, 0, 1, 0,
|
||||
28, 0, 0, 0, 70, 0, 0, 0,
|
||||
0, 5, 0, 0, 3, 0, 1, 0,
|
||||
12, 5, 0, 0, 2, 0, 1, 0,
|
||||
28, 0, 0, 0, 21, 0, 0, 0,
|
||||
0, 0, 1, 0, 28, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
237, 4, 0, 0, 146, 0, 0, 0,
|
||||
9, 5, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
240, 4, 0, 0, 3, 0, 1, 0,
|
||||
252, 4, 0, 0, 2, 0, 1, 0,
|
||||
29, 0, 0, 0, 22, 0, 0, 0,
|
||||
8, 5, 0, 0, 3, 0, 1, 0,
|
||||
20, 5, 0, 0, 2, 0, 1, 0,
|
||||
29, 0, 0, 0, 71, 0, 0, 0,
|
||||
0, 0, 1, 0, 29, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
249, 4, 0, 0, 66, 0, 0, 0,
|
||||
17, 5, 0, 0, 146, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
244, 4, 0, 0, 3, 0, 1, 0,
|
||||
0, 5, 0, 0, 2, 0, 1, 0,
|
||||
30, 0, 0, 0, 71, 0, 0, 0,
|
||||
20, 5, 0, 0, 3, 0, 1, 0,
|
||||
32, 5, 0, 0, 2, 0, 1, 0,
|
||||
30, 0, 0, 0, 22, 0, 0, 0,
|
||||
0, 0, 1, 0, 30, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
253, 4, 0, 0, 106, 0, 0, 0,
|
||||
29, 5, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
252, 4, 0, 0, 3, 0, 1, 0,
|
||||
8, 5, 0, 0, 2, 0, 1, 0,
|
||||
24, 5, 0, 0, 3, 0, 1, 0,
|
||||
36, 5, 0, 0, 2, 0, 1, 0,
|
||||
31, 0, 0, 0, 72, 0, 0, 0,
|
||||
0, 0, 1, 0, 31, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 5, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
4, 5, 0, 0, 3, 0, 1, 0,
|
||||
16, 5, 0, 0, 2, 0, 1, 0,
|
||||
32, 0, 0, 0, 73, 0, 0, 0,
|
||||
0, 0, 1, 0, 32, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
13, 5, 0, 0, 106, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 5, 0, 0, 3, 0, 1, 0,
|
||||
24, 5, 0, 0, 2, 0, 1, 0,
|
||||
33, 0, 0, 0, 23, 0, 0, 0,
|
||||
0, 0, 1, 0, 33, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 5, 0, 0, 202, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
28, 5, 0, 0, 3, 0, 1, 0,
|
||||
40, 5, 0, 0, 2, 0, 1, 0,
|
||||
34, 0, 0, 0, 24, 0, 0, 0,
|
||||
0, 0, 1, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
37, 5, 0, 0, 66, 0, 0, 0,
|
||||
33, 5, 0, 0, 106, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 5, 0, 0, 3, 0, 1, 0,
|
||||
44, 5, 0, 0, 2, 0, 1, 0,
|
||||
32, 0, 0, 0, 73, 0, 0, 0,
|
||||
0, 0, 1, 0, 32, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 5, 0, 0, 122, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
40, 5, 0, 0, 3, 0, 1, 0,
|
||||
52, 5, 0, 0, 2, 0, 1, 0,
|
||||
33, 0, 0, 0, 74, 0, 0, 0,
|
||||
0, 0, 1, 0, 33, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 5, 0, 0, 106, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
48, 5, 0, 0, 3, 0, 1, 0,
|
||||
60, 5, 0, 0, 2, 0, 1, 0,
|
||||
34, 0, 0, 0, 23, 0, 0, 0,
|
||||
0, 0, 1, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 5, 0, 0, 202, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
64, 5, 0, 0, 3, 0, 1, 0,
|
||||
76, 5, 0, 0, 2, 0, 1, 0,
|
||||
35, 0, 0, 0, 24, 0, 0, 0,
|
||||
0, 0, 1, 0, 35, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
73, 5, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
68, 5, 0, 0, 3, 0, 1, 0,
|
||||
80, 5, 0, 0, 2, 0, 1, 0,
|
||||
97, 99, 99, 101, 108, 101, 114, 97,
|
||||
116, 105, 111, 110, 74, 101, 114, 107,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -2070,6 +2018,15 @@ static const ::capnp::_::AlignedData<597> b_a1680744031fdb2d = {
|
||||
5, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
100, 105, 115, 97, 98, 108, 101, 84,
|
||||
104, 114, 111, 116, 116, 108, 101, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
101, 120, 112, 101, 114, 105, 109, 101,
|
||||
110, 116, 97, 108, 77, 111, 100, 101,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -2343,11 +2300,11 @@ static const ::capnp::_::AlignedData<597> b_a1680744031fdb2d = {
|
||||
static const ::capnp::_::RawSchema* const d_a1680744031fdb2d[] = {
|
||||
&s_81c2f05a394cf4af,
|
||||
};
|
||||
static const uint16_t m_a1680744031fdb2d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34};
|
||||
static const uint16_t i_a1680744031fdb2d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34};
|
||||
static const uint16_t m_a1680744031fdb2d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35};
|
||||
static const uint16_t i_a1680744031fdb2d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35};
|
||||
const ::capnp::_::RawSchema s_a1680744031fdb2d = {
|
||||
0xa1680744031fdb2d, b_a1680744031fdb2d.words, 597, d_a1680744031fdb2d, m_a1680744031fdb2d,
|
||||
1, 35, i_a1680744031fdb2d, nullptr, nullptr, { &s_a1680744031fdb2d, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
0xa1680744031fdb2d, b_a1680744031fdb2d.words, 613, d_a1680744031fdb2d, m_a1680744031fdb2d,
|
||||
1, 36, i_a1680744031fdb2d, nullptr, nullptr, { &s_a1680744031fdb2d, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<55> b_cb9fd56c7057593a = {
|
||||
@@ -2761,18 +2718,6 @@ constexpr ::capnp::_::RawSchema const* FrogPilotCarParams::SafetyConfig::_capnpP
|
||||
#endif // !CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
// FrogPilotCarParams::LateralTuning
|
||||
#if CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
|
||||
constexpr uint16_t FrogPilotCarParams::LateralTuning::_capnpPrivate::dataWordSize;
|
||||
constexpr uint16_t FrogPilotCarParams::LateralTuning::_capnpPrivate::pointerCount;
|
||||
#endif // !CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
|
||||
#if !CAPNP_LITE
|
||||
#if CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
|
||||
constexpr ::capnp::Kind FrogPilotCarParams::LateralTuning::_capnpPrivate::kind;
|
||||
constexpr ::capnp::_::RawSchema const* FrogPilotCarParams::LateralTuning::_capnpPrivate::schema;
|
||||
#endif // !CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
// FrogPilotCarState
|
||||
#if CAPNP_NEED_REDUNDANT_CONSTEXPR_DECL
|
||||
constexpr uint16_t FrogPilotCarState::_capnpPrivate::dataWordSize;
|
||||
|
||||
+68
-287
@@ -84,7 +84,6 @@ enum class EventName_aedffd8f31e7b55d: uint16_t {
|
||||
CAPNP_DECLARE_ENUM(EventName, aedffd8f31e7b55d);
|
||||
CAPNP_DECLARE_SCHEMA(f35cc4560bbf6ec2);
|
||||
CAPNP_DECLARE_SCHEMA(8d65dd40bad40951);
|
||||
CAPNP_DECLARE_SCHEMA(fcc949d2cbea7649);
|
||||
CAPNP_DECLARE_SCHEMA(da96579883444c35);
|
||||
CAPNP_DECLARE_SCHEMA(ccb4d6b0dc102d40);
|
||||
CAPNP_DECLARE_SCHEMA(8033e8e60d6a0edb);
|
||||
@@ -185,10 +184,9 @@ struct FrogPilotCarParams {
|
||||
class Builder;
|
||||
class Pipeline;
|
||||
struct SafetyConfig;
|
||||
struct LateralTuning;
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(f35cc4560bbf6ec2, 1, 2)
|
||||
CAPNP_DECLARE_STRUCT_HEADER(f35cc4560bbf6ec2, 1, 1)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -210,25 +208,6 @@ struct FrogPilotCarParams::SafetyConfig {
|
||||
};
|
||||
};
|
||||
|
||||
struct FrogPilotCarParams::LateralTuning {
|
||||
LateralTuning() = delete;
|
||||
|
||||
class Reader;
|
||||
class Builder;
|
||||
class Pipeline;
|
||||
enum Which: uint16_t {
|
||||
PID,
|
||||
TORQUE,
|
||||
};
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(fcc949d2cbea7649, 1, 2)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
};
|
||||
};
|
||||
|
||||
struct FrogPilotCarState {
|
||||
FrogPilotCarState() = delete;
|
||||
|
||||
@@ -690,7 +669,7 @@ public:
|
||||
inline bool hasSafetyConfigs() const;
|
||||
inline ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>::Reader getSafetyConfigs() const;
|
||||
|
||||
inline typename LateralTuning::Reader getLateralTuning() const;
|
||||
inline bool getCanUseSASCM() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
@@ -742,8 +721,8 @@ public:
|
||||
inline void adoptSafetyConfigs(::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>>&& value);
|
||||
inline ::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfig, ::capnp::Kind::STRUCT>> disownSafetyConfigs();
|
||||
|
||||
inline typename LateralTuning::Builder getLateralTuning();
|
||||
inline typename LateralTuning::Builder initLateralTuning();
|
||||
inline bool getCanUseSASCM();
|
||||
inline void setCanUseSASCM(bool value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
@@ -763,7 +742,6 @@ public:
|
||||
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
|
||||
: _typeless(kj::mv(typeless)) {}
|
||||
|
||||
inline typename LateralTuning::Pipeline getLateralTuning();
|
||||
private:
|
||||
::capnp::AnyPointer::Pipeline _typeless;
|
||||
friend class ::capnp::PipelineHook;
|
||||
@@ -848,103 +826,6 @@ private:
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
class FrogPilotCarParams::LateralTuning::Reader {
|
||||
public:
|
||||
typedef LateralTuning Reads;
|
||||
|
||||
Reader() = default;
|
||||
inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
|
||||
|
||||
inline ::capnp::MessageSize totalSize() const {
|
||||
return _reader.totalSize().asPublic();
|
||||
}
|
||||
|
||||
#if !CAPNP_LITE
|
||||
inline ::kj::StringTree toString() const {
|
||||
return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
|
||||
}
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
inline Which which() const;
|
||||
inline bool isPid() const;
|
||||
inline bool hasPid() const;
|
||||
inline ::cereal::CarParams::LateralPIDTuning::Reader getPid() const;
|
||||
|
||||
inline bool isTorque() const;
|
||||
inline bool hasTorque() const;
|
||||
inline ::cereal::CarParams::LateralTorqueTuning::Reader getTorque() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
friend struct ::capnp::ToDynamic_;
|
||||
template <typename, ::capnp::Kind>
|
||||
friend struct ::capnp::_::PointerHelpers;
|
||||
template <typename, ::capnp::Kind>
|
||||
friend struct ::capnp::List;
|
||||
friend class ::capnp::MessageBuilder;
|
||||
friend class ::capnp::Orphanage;
|
||||
};
|
||||
|
||||
class FrogPilotCarParams::LateralTuning::Builder {
|
||||
public:
|
||||
typedef LateralTuning Builds;
|
||||
|
||||
Builder() = delete; // Deleted to discourage incorrect usage.
|
||||
// You can explicitly initialize to nullptr instead.
|
||||
inline Builder(decltype(nullptr)) {}
|
||||
inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
|
||||
inline operator Reader() const { return Reader(_builder.asReader()); }
|
||||
inline Reader asReader() const { return *this; }
|
||||
|
||||
inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
|
||||
#if !CAPNP_LITE
|
||||
inline ::kj::StringTree toString() const { return asReader().toString(); }
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
inline Which which();
|
||||
inline bool isPid();
|
||||
inline bool hasPid();
|
||||
inline ::cereal::CarParams::LateralPIDTuning::Builder getPid();
|
||||
inline void setPid( ::cereal::CarParams::LateralPIDTuning::Reader value);
|
||||
inline ::cereal::CarParams::LateralPIDTuning::Builder initPid();
|
||||
inline void adoptPid(::capnp::Orphan< ::cereal::CarParams::LateralPIDTuning>&& value);
|
||||
inline ::capnp::Orphan< ::cereal::CarParams::LateralPIDTuning> disownPid();
|
||||
|
||||
inline bool isTorque();
|
||||
inline bool hasTorque();
|
||||
inline ::cereal::CarParams::LateralTorqueTuning::Builder getTorque();
|
||||
inline void setTorque( ::cereal::CarParams::LateralTorqueTuning::Reader value);
|
||||
inline ::cereal::CarParams::LateralTorqueTuning::Builder initTorque();
|
||||
inline void adoptTorque(::capnp::Orphan< ::cereal::CarParams::LateralTorqueTuning>&& value);
|
||||
inline ::capnp::Orphan< ::cereal::CarParams::LateralTorqueTuning> disownTorque();
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
friend struct ::capnp::ToDynamic_;
|
||||
friend class ::capnp::Orphanage;
|
||||
template <typename, ::capnp::Kind>
|
||||
friend struct ::capnp::_::PointerHelpers;
|
||||
};
|
||||
|
||||
#if !CAPNP_LITE
|
||||
class FrogPilotCarParams::LateralTuning::Pipeline {
|
||||
public:
|
||||
typedef LateralTuning Pipelines;
|
||||
|
||||
inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
|
||||
inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
|
||||
: _typeless(kj::mv(typeless)) {}
|
||||
|
||||
private:
|
||||
::capnp::AnyPointer::Pipeline _typeless;
|
||||
friend class ::capnp::PipelineHook;
|
||||
template <typename, ::capnp::Kind>
|
||||
friend struct ::capnp::ToDynamic_;
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
|
||||
class FrogPilotCarState::Reader {
|
||||
public:
|
||||
typedef FrogPilotCarState Reads;
|
||||
@@ -1557,6 +1438,8 @@ public:
|
||||
|
||||
inline ::int64_t getDesiredFollowDistance() const;
|
||||
|
||||
inline bool getDisableThrottle() const;
|
||||
|
||||
inline bool getExperimentalMode() const;
|
||||
|
||||
inline bool getForcingStop() const;
|
||||
@@ -1664,6 +1547,9 @@ public:
|
||||
inline ::int64_t getDesiredFollowDistance();
|
||||
inline void setDesiredFollowDistance( ::int64_t value);
|
||||
|
||||
inline bool getDisableThrottle();
|
||||
inline void setDisableThrottle(bool value);
|
||||
|
||||
inline bool getExperimentalMode();
|
||||
inline void setExperimentalMode(bool value);
|
||||
|
||||
@@ -2339,22 +2225,20 @@ inline ::capnp::Orphan< ::capnp::List< ::cereal::FrogPilotCarParams::SafetyConfi
|
||||
::capnp::bounded<0>() * ::capnp::POINTERS));
|
||||
}
|
||||
|
||||
inline typename FrogPilotCarParams::LateralTuning::Reader FrogPilotCarParams::Reader::getLateralTuning() const {
|
||||
return typename FrogPilotCarParams::LateralTuning::Reader(_reader);
|
||||
inline bool FrogPilotCarParams::Reader::getCanUseSASCM() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline typename FrogPilotCarParams::LateralTuning::Builder FrogPilotCarParams::Builder::getLateralTuning() {
|
||||
return typename FrogPilotCarParams::LateralTuning::Builder(_builder);
|
||||
|
||||
inline bool FrogPilotCarParams::Builder::getCanUseSASCM() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
#if !CAPNP_LITE
|
||||
inline typename FrogPilotCarParams::LateralTuning::Pipeline FrogPilotCarParams::Pipeline::getLateralTuning() {
|
||||
return typename FrogPilotCarParams::LateralTuning::Pipeline(_typeless.noop());
|
||||
}
|
||||
#endif // !CAPNP_LITE
|
||||
inline typename FrogPilotCarParams::LateralTuning::Builder FrogPilotCarParams::Builder::initLateralTuning() {
|
||||
_builder.setDataField< ::uint16_t>(::capnp::bounded<1>() * ::capnp::ELEMENTS, 0);
|
||||
_builder.getPointerField(::capnp::bounded<1>() * ::capnp::POINTERS).clear();
|
||||
return typename FrogPilotCarParams::LateralTuning::Builder(_builder);
|
||||
inline void FrogPilotCarParams::Builder::setCanUseSASCM(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<4>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::uint16_t FrogPilotCarParams::SafetyConfig::Reader::getSafetyParam() const {
|
||||
return _reader.getDataField< ::uint16_t>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
@@ -2369,123 +2253,6 @@ inline void FrogPilotCarParams::SafetyConfig::Builder::setSafetyParam( ::uint16_
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::cereal::FrogPilotCarParams::LateralTuning::Which FrogPilotCarParams::LateralTuning::Reader::which() const {
|
||||
return _reader.getDataField<Which>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline ::cereal::FrogPilotCarParams::LateralTuning::Which FrogPilotCarParams::LateralTuning::Builder::which() {
|
||||
return _builder.getDataField<Which>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotCarParams::LateralTuning::Reader::isPid() const {
|
||||
return which() == FrogPilotCarParams::LateralTuning::PID;
|
||||
}
|
||||
inline bool FrogPilotCarParams::LateralTuning::Builder::isPid() {
|
||||
return which() == FrogPilotCarParams::LateralTuning::PID;
|
||||
}
|
||||
inline bool FrogPilotCarParams::LateralTuning::Reader::hasPid() const {
|
||||
if (which() != FrogPilotCarParams::LateralTuning::PID) return false;
|
||||
return !_reader.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
|
||||
}
|
||||
inline bool FrogPilotCarParams::LateralTuning::Builder::hasPid() {
|
||||
if (which() != FrogPilotCarParams::LateralTuning::PID) return false;
|
||||
return !_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
|
||||
}
|
||||
inline ::cereal::CarParams::LateralPIDTuning::Reader FrogPilotCarParams::LateralTuning::Reader::getPid() const {
|
||||
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::PID),
|
||||
"Must check which() before get()ing a union member.");
|
||||
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::get(_reader.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS));
|
||||
}
|
||||
inline ::cereal::CarParams::LateralPIDTuning::Builder FrogPilotCarParams::LateralTuning::Builder::getPid() {
|
||||
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::PID),
|
||||
"Must check which() before get()ing a union member.");
|
||||
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::get(_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS));
|
||||
}
|
||||
inline void FrogPilotCarParams::LateralTuning::Builder::setPid( ::cereal::CarParams::LateralPIDTuning::Reader value) {
|
||||
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::PID);
|
||||
::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::set(_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS), value);
|
||||
}
|
||||
inline ::cereal::CarParams::LateralPIDTuning::Builder FrogPilotCarParams::LateralTuning::Builder::initPid() {
|
||||
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::PID);
|
||||
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::init(_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS));
|
||||
}
|
||||
inline void FrogPilotCarParams::LateralTuning::Builder::adoptPid(
|
||||
::capnp::Orphan< ::cereal::CarParams::LateralPIDTuning>&& value) {
|
||||
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::PID);
|
||||
::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::adopt(_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS), kj::mv(value));
|
||||
}
|
||||
inline ::capnp::Orphan< ::cereal::CarParams::LateralPIDTuning> FrogPilotCarParams::LateralTuning::Builder::disownPid() {
|
||||
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::PID),
|
||||
"Must check which() before get()ing a union member.");
|
||||
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralPIDTuning>::disown(_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS));
|
||||
}
|
||||
|
||||
inline bool FrogPilotCarParams::LateralTuning::Reader::isTorque() const {
|
||||
return which() == FrogPilotCarParams::LateralTuning::TORQUE;
|
||||
}
|
||||
inline bool FrogPilotCarParams::LateralTuning::Builder::isTorque() {
|
||||
return which() == FrogPilotCarParams::LateralTuning::TORQUE;
|
||||
}
|
||||
inline bool FrogPilotCarParams::LateralTuning::Reader::hasTorque() const {
|
||||
if (which() != FrogPilotCarParams::LateralTuning::TORQUE) return false;
|
||||
return !_reader.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
|
||||
}
|
||||
inline bool FrogPilotCarParams::LateralTuning::Builder::hasTorque() {
|
||||
if (which() != FrogPilotCarParams::LateralTuning::TORQUE) return false;
|
||||
return !_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
|
||||
}
|
||||
inline ::cereal::CarParams::LateralTorqueTuning::Reader FrogPilotCarParams::LateralTuning::Reader::getTorque() const {
|
||||
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::TORQUE),
|
||||
"Must check which() before get()ing a union member.");
|
||||
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::get(_reader.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS));
|
||||
}
|
||||
inline ::cereal::CarParams::LateralTorqueTuning::Builder FrogPilotCarParams::LateralTuning::Builder::getTorque() {
|
||||
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::TORQUE),
|
||||
"Must check which() before get()ing a union member.");
|
||||
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::get(_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS));
|
||||
}
|
||||
inline void FrogPilotCarParams::LateralTuning::Builder::setTorque( ::cereal::CarParams::LateralTorqueTuning::Reader value) {
|
||||
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::TORQUE);
|
||||
::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::set(_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS), value);
|
||||
}
|
||||
inline ::cereal::CarParams::LateralTorqueTuning::Builder FrogPilotCarParams::LateralTuning::Builder::initTorque() {
|
||||
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::TORQUE);
|
||||
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::init(_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS));
|
||||
}
|
||||
inline void FrogPilotCarParams::LateralTuning::Builder::adoptTorque(
|
||||
::capnp::Orphan< ::cereal::CarParams::LateralTorqueTuning>&& value) {
|
||||
_builder.setDataField<FrogPilotCarParams::LateralTuning::Which>(
|
||||
::capnp::bounded<1>() * ::capnp::ELEMENTS, FrogPilotCarParams::LateralTuning::TORQUE);
|
||||
::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::adopt(_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS), kj::mv(value));
|
||||
}
|
||||
inline ::capnp::Orphan< ::cereal::CarParams::LateralTorqueTuning> FrogPilotCarParams::LateralTuning::Builder::disownTorque() {
|
||||
KJ_IREQUIRE((which() == FrogPilotCarParams::LateralTuning::TORQUE),
|
||||
"Must check which() before get()ing a union member.");
|
||||
return ::capnp::_::PointerHelpers< ::cereal::CarParams::LateralTorqueTuning>::disown(_builder.getPointerField(
|
||||
::capnp::bounded<1>() * ::capnp::POINTERS));
|
||||
}
|
||||
|
||||
inline bool FrogPilotCarState::Reader::getAccelPressed() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
@@ -3036,34 +2803,48 @@ inline void FrogPilotPlan::Builder::setDesiredFollowDistance( ::int64_t value) {
|
||||
::capnp::bounded<3>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Reader::getExperimentalMode() const {
|
||||
inline bool FrogPilotPlan::Reader::getDisableThrottle() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<66>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Builder::getExperimentalMode() {
|
||||
inline bool FrogPilotPlan::Builder::getDisableThrottle() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<66>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void FrogPilotPlan::Builder::setExperimentalMode(bool value) {
|
||||
inline void FrogPilotPlan::Builder::setDisableThrottle(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<66>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Reader::getForcingStop() const {
|
||||
inline bool FrogPilotPlan::Reader::getExperimentalMode() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<67>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Builder::getForcingStop() {
|
||||
inline bool FrogPilotPlan::Builder::getExperimentalMode() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<67>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void FrogPilotPlan::Builder::setForcingStop(bool value) {
|
||||
inline void FrogPilotPlan::Builder::setExperimentalMode(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<67>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Reader::getForcingStop() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<68>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Builder::getForcingStop() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<68>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void FrogPilotPlan::Builder::setForcingStop(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<68>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float FrogPilotPlan::Reader::getForcingStopLength() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<5>() * ::capnp::ELEMENTS);
|
||||
@@ -3128,16 +2909,16 @@ inline void FrogPilotPlan::Builder::setIncreasedStoppedDistance(float value) {
|
||||
|
||||
inline bool FrogPilotPlan::Reader::getLateralCheck() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<68>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<69>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Builder::getLateralCheck() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<68>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<69>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void FrogPilotPlan::Builder::setLateralCheck(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<68>() * ::capnp::ELEMENTS, value);
|
||||
::capnp::bounded<69>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float FrogPilotPlan::Reader::getLaneWidthLeft() const {
|
||||
@@ -3198,16 +2979,16 @@ inline void FrogPilotPlan::Builder::setMinAcceleration(float value) {
|
||||
|
||||
inline bool FrogPilotPlan::Reader::getRedLight() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<69>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<70>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Builder::getRedLight() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<69>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<70>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void FrogPilotPlan::Builder::setRedLight(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<69>() * ::capnp::ELEMENTS, value);
|
||||
::capnp::bounded<70>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float FrogPilotPlan::Reader::getRoadCurvature() const {
|
||||
@@ -3372,16 +3153,16 @@ inline void FrogPilotPlan::Builder::setSpeedJerkStock(float value) {
|
||||
|
||||
inline bool FrogPilotPlan::Reader::getSpeedLimitChanged() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<70>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<71>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Builder::getSpeedLimitChanged() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<70>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<71>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void FrogPilotPlan::Builder::setSpeedLimitChanged(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<70>() * ::capnp::ELEMENTS, value);
|
||||
::capnp::bounded<71>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float FrogPilotPlan::Reader::getTFollow() const {
|
||||
@@ -3400,46 +3181,46 @@ inline void FrogPilotPlan::Builder::setTFollow(float value) {
|
||||
|
||||
inline bool FrogPilotPlan::Reader::getThemeUpdated() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<71>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<72>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Builder::getThemeUpdated() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<71>() * ::capnp::ELEMENTS);
|
||||
::capnp::bounded<72>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void FrogPilotPlan::Builder::setThemeUpdated(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<71>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Reader::getTogglesUpdated() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<72>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Builder::getTogglesUpdated() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<72>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void FrogPilotPlan::Builder::setTogglesUpdated(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<72>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Reader::getTrackingLead() const {
|
||||
inline bool FrogPilotPlan::Reader::getTogglesUpdated() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<73>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Builder::getTrackingLead() {
|
||||
inline bool FrogPilotPlan::Builder::getTogglesUpdated() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<73>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void FrogPilotPlan::Builder::setTrackingLead(bool value) {
|
||||
inline void FrogPilotPlan::Builder::setTogglesUpdated(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<73>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Reader::getTrackingLead() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<74>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline bool FrogPilotPlan::Builder::getTrackingLead() {
|
||||
return _builder.getDataField<bool>(
|
||||
::capnp::bounded<74>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void FrogPilotPlan::Builder::setTrackingLead(bool value) {
|
||||
_builder.setDataField<bool>(
|
||||
::capnp::bounded<74>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float FrogPilotPlan::Reader::getUnconfirmedSlcSpeedLimit() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<23>() * ::capnp::ELEMENTS);
|
||||
|
||||
+103
-71
@@ -9336,17 +9336,17 @@ const ::capnp::_::RawSchema s_f28c5dc9e09375e3 = {
|
||||
0, 10, i_f28c5dc9e09375e3, nullptr, nullptr, { &s_f28c5dc9e09375e3, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<191> b_e774a050cbf689a4 = {
|
||||
static const ::capnp::_::AlignedData<223> b_e774a050cbf689a4 = {
|
||||
{ 0, 0, 0, 0, 5, 0, 6, 0,
|
||||
164, 137, 246, 203, 80, 160, 116, 231,
|
||||
24, 0, 0, 0, 1, 0, 5, 0,
|
||||
24, 0, 0, 0, 1, 0, 6, 0,
|
||||
241, 171, 1, 54, 197, 105, 255, 151,
|
||||
0, 0, 7, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0, 0, 0, 90, 1, 0, 0,
|
||||
41, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
37, 0, 0, 0, 111, 2, 0, 0,
|
||||
37, 0, 0, 0, 223, 2, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
108, 111, 103, 46, 99, 97, 112, 110,
|
||||
@@ -9356,84 +9356,98 @@ static const ::capnp::_::AlignedData<191> b_e774a050cbf689a4 = {
|
||||
111, 114, 113, 117, 101, 83, 116, 97,
|
||||
116, 101, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0,
|
||||
44, 0, 0, 0, 3, 0, 4, 0,
|
||||
52, 0, 0, 0, 3, 0, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
37, 1, 0, 0, 58, 0, 0, 0,
|
||||
93, 1, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 1, 0, 0, 3, 0, 1, 0,
|
||||
44, 1, 0, 0, 2, 0, 1, 0,
|
||||
88, 1, 0, 0, 3, 0, 1, 0,
|
||||
100, 1, 0, 0, 2, 0, 1, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
41, 1, 0, 0, 50, 0, 0, 0,
|
||||
97, 1, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
36, 1, 0, 0, 3, 0, 1, 0,
|
||||
48, 1, 0, 0, 2, 0, 1, 0,
|
||||
92, 1, 0, 0, 3, 0, 1, 0,
|
||||
104, 1, 0, 0, 2, 0, 1, 0,
|
||||
3, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 1, 0, 2, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
45, 1, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
40, 1, 0, 0, 3, 0, 1, 0,
|
||||
52, 1, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
49, 1, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
44, 1, 0, 0, 3, 0, 1, 0,
|
||||
56, 1, 0, 0, 2, 0, 1, 0,
|
||||
5, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
53, 1, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
48, 1, 0, 0, 3, 0, 1, 0,
|
||||
60, 1, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
57, 1, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
52, 1, 0, 0, 3, 0, 1, 0,
|
||||
64, 1, 0, 0, 2, 0, 1, 0,
|
||||
7, 0, 0, 0, 6, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
61, 1, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
56, 1, 0, 0, 3, 0, 1, 0,
|
||||
68, 1, 0, 0, 2, 0, 1, 0,
|
||||
8, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
65, 1, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
64, 1, 0, 0, 3, 0, 1, 0,
|
||||
76, 1, 0, 0, 2, 0, 1, 0,
|
||||
2, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 8, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
73, 1, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
72, 1, 0, 0, 3, 0, 1, 0,
|
||||
84, 1, 0, 0, 2, 0, 1, 0,
|
||||
9, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 9, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
81, 1, 0, 0, 154, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
84, 1, 0, 0, 3, 0, 1, 0,
|
||||
96, 1, 0, 0, 2, 0, 1, 0,
|
||||
10, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 10, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
93, 1, 0, 0, 162, 0, 0, 0,
|
||||
101, 1, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
96, 1, 0, 0, 3, 0, 1, 0,
|
||||
108, 1, 0, 0, 2, 0, 1, 0,
|
||||
4, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
105, 1, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
100, 1, 0, 0, 3, 0, 1, 0,
|
||||
112, 1, 0, 0, 2, 0, 1, 0,
|
||||
5, 0, 0, 0, 4, 0, 0, 0,
|
||||
0, 0, 1, 0, 4, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
109, 1, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
104, 1, 0, 0, 3, 0, 1, 0,
|
||||
116, 1, 0, 0, 2, 0, 1, 0,
|
||||
6, 0, 0, 0, 5, 0, 0, 0,
|
||||
0, 0, 1, 0, 5, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
113, 1, 0, 0, 18, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
108, 1, 0, 0, 3, 0, 1, 0,
|
||||
120, 1, 0, 0, 2, 0, 1, 0,
|
||||
7, 0, 0, 0, 6, 0, 0, 0,
|
||||
0, 0, 1, 0, 6, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
117, 1, 0, 0, 58, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
112, 1, 0, 0, 3, 0, 1, 0,
|
||||
124, 1, 0, 0, 2, 0, 1, 0,
|
||||
8, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 7, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
121, 1, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
120, 1, 0, 0, 3, 0, 1, 0,
|
||||
132, 1, 0, 0, 2, 0, 1, 0,
|
||||
2, 0, 0, 0, 7, 0, 0, 0,
|
||||
0, 0, 1, 0, 8, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
129, 1, 0, 0, 82, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
128, 1, 0, 0, 3, 0, 1, 0,
|
||||
140, 1, 0, 0, 2, 0, 1, 0,
|
||||
9, 0, 0, 0, 8, 0, 0, 0,
|
||||
0, 0, 1, 0, 9, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
137, 1, 0, 0, 154, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
140, 1, 0, 0, 3, 0, 1, 0,
|
||||
152, 1, 0, 0, 2, 0, 1, 0,
|
||||
10, 0, 0, 0, 9, 0, 0, 0,
|
||||
0, 0, 1, 0, 10, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
149, 1, 0, 0, 162, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
152, 1, 0, 0, 3, 0, 1, 0,
|
||||
164, 1, 0, 0, 2, 0, 1, 0,
|
||||
11, 0, 0, 0, 10, 0, 0, 0,
|
||||
0, 0, 1, 0, 11, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
161, 1, 0, 0, 154, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
164, 1, 0, 0, 3, 0, 1, 0,
|
||||
176, 1, 0, 0, 2, 0, 1, 0,
|
||||
12, 0, 0, 0, 11, 0, 0, 0,
|
||||
0, 0, 1, 0, 12, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
173, 1, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
168, 1, 0, 0, 3, 0, 1, 0,
|
||||
180, 1, 0, 0, 2, 0, 1, 0,
|
||||
97, 99, 116, 105, 118, 101, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -9526,16 +9540,34 @@ static const ::capnp::_::AlignedData<191> b_e774a050cbf689a4 = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
100, 101, 115, 105, 114, 101, 100, 76,
|
||||
97, 116, 101, 114, 97, 108, 74, 101,
|
||||
114, 107, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
10, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
118, 101, 114, 115, 105, 111, 110, 0,
|
||||
4, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
4, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, }
|
||||
};
|
||||
::capnp::word const* const bp_e774a050cbf689a4 = b_e774a050cbf689a4.words;
|
||||
#if !CAPNP_LITE
|
||||
static const uint16_t m_e774a050cbf689a4[] = {0, 9, 4, 10, 1, 8, 5, 3, 6, 2, 7};
|
||||
static const uint16_t i_e774a050cbf689a4[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
static const uint16_t m_e774a050cbf689a4[] = {0, 9, 4, 10, 11, 1, 8, 5, 3, 6, 2, 7, 12};
|
||||
static const uint16_t i_e774a050cbf689a4[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
|
||||
const ::capnp::_::RawSchema s_e774a050cbf689a4 = {
|
||||
0xe774a050cbf689a4, b_e774a050cbf689a4.words, 191, nullptr, m_e774a050cbf689a4,
|
||||
0, 11, i_e774a050cbf689a4, nullptr, nullptr, { &s_e774a050cbf689a4, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
0xe774a050cbf689a4, b_e774a050cbf689a4.words, 223, nullptr, m_e774a050cbf689a4,
|
||||
0, 13, i_e774a050cbf689a4, nullptr, nullptr, { &s_e774a050cbf689a4, nullptr, nullptr, 0, 0, nullptr }, false
|
||||
};
|
||||
#endif // !CAPNP_LITE
|
||||
static const ::capnp::_::AlignedData<130> b_9024e2d790c82ade = {
|
||||
|
||||
@@ -1076,7 +1076,7 @@ struct ControlsState::LateralTorqueState {
|
||||
class Pipeline;
|
||||
|
||||
struct _capnpPrivate {
|
||||
CAPNP_DECLARE_STRUCT_HEADER(e774a050cbf689a4, 5, 0)
|
||||
CAPNP_DECLARE_STRUCT_HEADER(e774a050cbf689a4, 6, 0)
|
||||
#if !CAPNP_LITE
|
||||
static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
|
||||
#endif // !CAPNP_LITE
|
||||
@@ -7531,6 +7531,10 @@ public:
|
||||
|
||||
inline float getDesiredLateralAccel() const;
|
||||
|
||||
inline float getDesiredLateralJerk() const;
|
||||
|
||||
inline ::int32_t getVersion() const;
|
||||
|
||||
private:
|
||||
::capnp::_::StructReader _reader;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -7592,6 +7596,12 @@ public:
|
||||
inline float getDesiredLateralAccel();
|
||||
inline void setDesiredLateralAccel(float value);
|
||||
|
||||
inline float getDesiredLateralJerk();
|
||||
inline void setDesiredLateralJerk(float value);
|
||||
|
||||
inline ::int32_t getVersion();
|
||||
inline void setVersion( ::int32_t value);
|
||||
|
||||
private:
|
||||
::capnp::_::StructBuilder _builder;
|
||||
template <typename, ::capnp::Kind>
|
||||
@@ -30064,6 +30074,34 @@ inline void ControlsState::LateralTorqueState::Builder::setDesiredLateralAccel(f
|
||||
::capnp::bounded<9>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline float ControlsState::LateralTorqueState::Reader::getDesiredLateralJerk() const {
|
||||
return _reader.getDataField<float>(
|
||||
::capnp::bounded<10>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline float ControlsState::LateralTorqueState::Builder::getDesiredLateralJerk() {
|
||||
return _builder.getDataField<float>(
|
||||
::capnp::bounded<10>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void ControlsState::LateralTorqueState::Builder::setDesiredLateralJerk(float value) {
|
||||
_builder.setDataField<float>(
|
||||
::capnp::bounded<10>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline ::int32_t ControlsState::LateralTorqueState::Reader::getVersion() const {
|
||||
return _reader.getDataField< ::int32_t>(
|
||||
::capnp::bounded<11>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
|
||||
inline ::int32_t ControlsState::LateralTorqueState::Builder::getVersion() {
|
||||
return _builder.getDataField< ::int32_t>(
|
||||
::capnp::bounded<11>() * ::capnp::ELEMENTS);
|
||||
}
|
||||
inline void ControlsState::LateralTorqueState::Builder::setVersion( ::int32_t value) {
|
||||
_builder.setDataField< ::int32_t>(
|
||||
::capnp::bounded<11>() * ::capnp::ELEMENTS, value);
|
||||
}
|
||||
|
||||
inline bool ControlsState::LateralLQRState::Reader::getActive() const {
|
||||
return _reader.getDataField<bool>(
|
||||
::capnp::bounded<0>() * ::capnp::ELEMENTS);
|
||||
|
||||
Binary file not shown.
@@ -795,6 +795,8 @@ struct ControlsState @0x97ff69c53601abf1 {
|
||||
saturated @7 :Bool;
|
||||
actualLateralAccel @9 :Float32;
|
||||
desiredLateralAccel @10 :Float32;
|
||||
desiredLateralJerk @11 :Float32;
|
||||
version @12 :Int32;
|
||||
}
|
||||
|
||||
struct LateralLQRState {
|
||||
|
||||
@@ -224,6 +224,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"AdvancedLateralTune", PERSISTENT},
|
||||
{"AdvancedLongitudinalTune", PERSISTENT},
|
||||
{"AggressiveFollow", PERSISTENT},
|
||||
{"AggressiveFollowHigh", PERSISTENT},
|
||||
{"AggressiveJerkAcceleration", PERSISTENT},
|
||||
{"AggressiveJerkDanger", PERSISTENT},
|
||||
{"AggressiveJerkDeceleration", PERSISTENT},
|
||||
@@ -240,6 +241,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"AutomaticallyDownloadModels", PERSISTENT},
|
||||
{"AutomaticUpdates", PERSISTENT},
|
||||
{"AvailableModelNames", PERSISTENT},
|
||||
{"AvailableModelSeries", PERSISTENT},
|
||||
{"AvailableModels", PERSISTENT},
|
||||
{"BigMap", PERSISTENT},
|
||||
{"BlacklistedModels", PERSISTENT},
|
||||
@@ -320,6 +322,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"DynamicPedalsOnUI", PERSISTENT},
|
||||
{"EngageVolume", PERSISTENT},
|
||||
{"ExperimentalGMTune", PERSISTENT},
|
||||
{"EVTuning", PERSISTENT},
|
||||
{"Fahrenheit", PERSISTENT},
|
||||
{"FavoriteDestinations", PERSISTENT | DONT_LOG},
|
||||
{"FlashPanda", CLEAR_ON_MANAGER_START},
|
||||
@@ -345,6 +348,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"FrogsGoMoosTweak", PERSISTENT},
|
||||
{"FullMap", PERSISTENT},
|
||||
{"GasRegenCmd", PERSISTENT},
|
||||
{"GMPedalLongitudinal", PERSISTENT},
|
||||
{"GoatScream", PERSISTENT},
|
||||
{"GreenLightAlert", PERSISTENT},
|
||||
{"HideAlerts", PERSISTENT},
|
||||
@@ -398,12 +402,17 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"MinimumBackupSize", PERSISTENT},
|
||||
{"MinimumLaneChangeSpeed", PERSISTENT},
|
||||
{"Model", PERSISTENT},
|
||||
{"ModelVersion", PERSISTENT},
|
||||
{"ModelDownloadProgress", CLEAR_ON_MANAGER_START},
|
||||
{"ModelDrivesAndScores", PERSISTENT},
|
||||
{"ModelRandomizer", PERSISTENT},
|
||||
{"ModelToDownload", CLEAR_ON_MANAGER_START},
|
||||
{"ModelUI", PERSISTENT},
|
||||
{"ModelVersions", PERSISTENT},
|
||||
{"ModelReleasedDates", PERSISTENT},
|
||||
{"CommunityFavorites", PERSISTENT},
|
||||
{"UserFavorites", PERSISTENT},
|
||||
{"SortModelsByDate", PERSISTENT},
|
||||
{"NavigationUI", PERSISTENT},
|
||||
{"NextMapSpeedLimit", CLEAR_ON_MANAGER_START},
|
||||
{"NewLongAPI", PERSISTENT},
|
||||
@@ -449,6 +458,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"RandomThemes", PERSISTENT},
|
||||
{"RefuseVolume", PERSISTENT},
|
||||
{"RelaxedFollow", PERSISTENT},
|
||||
{"RelaxedFollowHigh", PERSISTENT},
|
||||
{"RelaxedJerkAcceleration", PERSISTENT},
|
||||
{"RelaxedJerkDanger", PERSISTENT},
|
||||
{"RelaxedJerkDeceleration", PERSISTENT},
|
||||
@@ -508,6 +518,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"SpeedLimitsFiltered", PERSISTENT | DONT_LOG},
|
||||
{"SpeedLimitSources", PERSISTENT},
|
||||
{"StandardFollow", PERSISTENT},
|
||||
{"StandardFollowHigh", PERSISTENT},
|
||||
{"StandardJerkAcceleration", PERSISTENT},
|
||||
{"StandardJerkDanger", PERSISTENT},
|
||||
{"StandardJerkDeceleration", PERSISTENT},
|
||||
@@ -524,6 +535,8 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"SteerDelayStock", PERSISTENT},
|
||||
{"SteerFriction", PERSISTENT},
|
||||
{"SteerFrictionStock", PERSISTENT},
|
||||
{"SteerOffset", PERSISTENT},
|
||||
{"SteerOffsetStock", PERSISTENT},
|
||||
{"SteerLatAccel", PERSISTENT},
|
||||
{"SteerLatAccelStock", PERSISTENT},
|
||||
{"SteerKP", PERSISTENT},
|
||||
@@ -538,6 +551,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"StoppedTimer", PERSISTENT},
|
||||
{"TacoTune", PERSISTENT},
|
||||
{"TacoTuneHacks", PERSISTENT},
|
||||
{"TrailerLoad", PERSISTENT},
|
||||
{"TestAlert", CLEAR_ON_MANAGER_START},
|
||||
{"TetheringEnabled", PERSISTENT},
|
||||
{"ThemeDownloadProgress", CLEAR_ON_MANAGER_START},
|
||||
@@ -576,6 +590,8 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"WarningSoftVolume", PERSISTENT},
|
||||
{"WheelIcon", PERSISTENT},
|
||||
{"WheelSpeed", PERSISTENT},
|
||||
{"StopDistance", PERSISTENT},
|
||||
{"RecoveryPower", PERSISTENT},
|
||||
{"WheelToDownload", CLEAR_ON_MANAGER_START},
|
||||
};
|
||||
|
||||
|
||||
Binary file not shown.
+12
-1
@@ -6,6 +6,7 @@ from collections import deque
|
||||
|
||||
from setproctitle import getproctitle
|
||||
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.system.hardware import PC
|
||||
|
||||
|
||||
@@ -34,7 +35,17 @@ def set_realtime_priority(level: int) -> None:
|
||||
|
||||
def set_core_affinity(cores: list[int]) -> None:
|
||||
if not PC:
|
||||
os.sched_setaffinity(0, cores)
|
||||
for attempt in range(3): # Retry up to 3 times
|
||||
try:
|
||||
os.sched_setaffinity(0, cores)
|
||||
return
|
||||
except OSError as e:
|
||||
if e.errno == 22: # EINVAL
|
||||
time.sleep(0.1) # Brief delay before retry
|
||||
else:
|
||||
raise # Re-raise other errors
|
||||
# If all retries fail, log and continue without affinity
|
||||
cloudlog.error(f"Failed to set core affinity after retries: {cores}")
|
||||
|
||||
|
||||
def config_realtime_process(cores: int | list[int], priority: int) -> None:
|
||||
|
||||
@@ -6,14 +6,13 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import delete_file, is_url_pingable
|
||||
from openpilot.frogpilot.common.frogpilot_variables import RESOURCES_REPO, params_memory
|
||||
|
||||
GITHUB_URL = f"https://raw.githubusercontent.com/{RESOURCES_REPO}"
|
||||
GITLAB_URL = f"https://gitlab.com/{RESOURCES_REPO}/-/raw"
|
||||
GITHUB_URL = "https://raw.githubusercontent.com/firestar5683/StarPilot-Resources"
|
||||
GITLAB_URL = "https://gitlab.com/firestar5683/FrogPilot-Resources/-/raw"
|
||||
|
||||
def check_github_rate_limit(session):
|
||||
def check_github_rate_limit():
|
||||
try:
|
||||
response = session.get("https://api.github.com/rate_limit", timeout=10)
|
||||
response = requests.get("https://api.github.com/rate_limit")
|
||||
response.raise_for_status()
|
||||
rate_limit_info = response.json()
|
||||
|
||||
@@ -26,91 +25,87 @@ def check_github_rate_limit(session):
|
||||
print("GitHub rate limit reached")
|
||||
print(f"GitHub Rate Limit Resets At (UTC): {reset_time}")
|
||||
return False
|
||||
except requests.exceptions.RequestException as exception:
|
||||
print(f"Error checking GitHub rate limit: {exception}")
|
||||
except requests.exceptions.RequestException as error:
|
||||
print(f"Error checking GitHub rate limit: {error}")
|
||||
return False
|
||||
|
||||
def download_file(cancel_param, destination, progress_param, url, download_param, session, offset_bytes=0, total_bytes=0):
|
||||
def download_file(cancel_param, destination, progress_param, url, download_param, params_memory):
|
||||
try:
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
total_size = get_remote_file_size(url, session)
|
||||
total_size = get_remote_file_size(url)
|
||||
if total_size == 0:
|
||||
if not url.endswith(".gif"):
|
||||
handle_error(None, "Download invalid...", "Download invalid...", download_param, progress_param)
|
||||
handle_error(None, "Download invalid...", "Download invalid...", download_param, progress_param, params_memory)
|
||||
return
|
||||
|
||||
with session.get(url, stream=True, timeout=10) as response:
|
||||
with requests.get(url, stream=True, timeout=10) as response:
|
||||
response.raise_for_status()
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=False, dir=destination.parent) as temp_file:
|
||||
with tempfile.NamedTemporaryFile(dir=destination.parent, delete=False) as temp_file:
|
||||
temp_file_path = Path(temp_file.name)
|
||||
|
||||
downloaded_size = 0
|
||||
for chunk in response.iter_content(chunk_size=16384):
|
||||
if params_memory.get_bool(cancel_param):
|
||||
temp_file_path.unlink(missing_ok=True)
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", download_param, progress_param)
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", download_param, progress_param, params_memory)
|
||||
return
|
||||
|
||||
if chunk:
|
||||
temp_file.write(chunk)
|
||||
downloaded_size += len(chunk)
|
||||
|
||||
if total_bytes:
|
||||
overall_progress = (offset_bytes + downloaded_size) / total_bytes * 100
|
||||
else:
|
||||
overall_progress = downloaded_size / total_size * 100
|
||||
|
||||
if overall_progress != 100:
|
||||
params_memory.put(progress_param, f"{overall_progress:.0f}%")
|
||||
progress = (downloaded_size / total_size) * 100
|
||||
if progress != 100:
|
||||
params_memory.put(progress_param, f"{progress:.0f}%")
|
||||
else:
|
||||
params_memory.put(progress_param, "Verifying authenticity...")
|
||||
|
||||
temp_file_path.rename(destination)
|
||||
|
||||
except Exception as exception:
|
||||
handle_request_error(exception, destination, download_param, progress_param)
|
||||
except Exception as error:
|
||||
handle_request_error(error, destination, download_param, progress_param, params_memory)
|
||||
|
||||
def get_remote_file_size(url, session):
|
||||
def get_remote_file_size(url):
|
||||
try:
|
||||
response = session.head(url, headers={"Accept-Encoding": "identity"}, timeout=10)
|
||||
response = requests.head(url, headers={"Accept-Encoding": "identity"}, timeout=10)
|
||||
response.raise_for_status()
|
||||
return int(response.headers.get("Content-Length", 0))
|
||||
except Exception as exception:
|
||||
handle_request_error(exception, None, None, None)
|
||||
except Exception as error:
|
||||
handle_request_error(error, None, None, None, None)
|
||||
return 0
|
||||
|
||||
def get_repository_url(session):
|
||||
def get_repository_url():
|
||||
if is_url_pingable("https://github.com"):
|
||||
if check_github_rate_limit(session):
|
||||
if check_github_rate_limit():
|
||||
return GITHUB_URL
|
||||
if is_url_pingable("https://gitlab.com"):
|
||||
return GITLAB_URL
|
||||
return None
|
||||
|
||||
def handle_error(destination, error_message, error, download_param, progress_param):
|
||||
def handle_error(destination, error_message, error, download_param, progress_param, params_memory):
|
||||
if destination:
|
||||
delete_file(destination)
|
||||
|
||||
if progress_param and "404" not in error_message:
|
||||
if params_memory and progress_param and "404" not in error_message:
|
||||
print(f"Error occurred: {error}")
|
||||
params_memory.put(progress_param, error_message)
|
||||
params_memory.remove(download_param)
|
||||
|
||||
def handle_request_error(error, destination, download_param, progress_param):
|
||||
def handle_request_error(error, destination, download_param, progress_param, params_memory):
|
||||
error_map = {
|
||||
requests.exceptions.ConnectionError: "Connection dropped",
|
||||
requests.exceptions.HTTPError: lambda error: f"Server error ({error.response.status_code})" if error and getattr(error, "response", None) else "Server error",
|
||||
requests.exceptions.RequestException: "Network request error. Check connection",
|
||||
requests.exceptions.Timeout: "Download timed out",
|
||||
requests.ConnectionError: "Connection dropped",
|
||||
requests.HTTPError: lambda error: f"Server error ({error.response.status_code})" if error.response else "Server error",
|
||||
requests.RequestException: "Network request error. Check connection",
|
||||
requests.Timeout: "Download timed out"
|
||||
}
|
||||
|
||||
error_message = error_map.get(type(error), "Unexpected error")
|
||||
handle_error(destination, f"Failed: {error_message}", error, download_param, progress_param)
|
||||
handle_error(destination, f"Failed: {error_message}", error, download_param, progress_param, params_memory)
|
||||
|
||||
def verify_download(file_path, url, session):
|
||||
remote_file_size = get_remote_file_size(url, session)
|
||||
def verify_download(file_path, url):
|
||||
remote_file_size = get_remote_file_size(url)
|
||||
|
||||
if remote_file_size == 0:
|
||||
print(f"Error fetching remote size for {file_path}")
|
||||
|
||||
+362
-462
@@ -5,59 +5,240 @@ import requests
|
||||
import shutil
|
||||
import time
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.frogpilot.assets.download_functions import GITLAB_URL, download_file, get_remote_file_size, get_repository_url, handle_error, handle_request_error, verify_download
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import delete_file, extract_tar, load_json_file, update_json_file
|
||||
from openpilot.frogpilot.common.frogpilot_variables import DEFAULT_MODEL, MODELS_PATH, RESOURCES_REPO, TINYGRAD_FILES, params, params_default, params_memory, update_frogpilot_toggles
|
||||
from openpilot.frogpilot.assets.download_functions import GITLAB_URL, download_file, get_repository_url, handle_error, handle_request_error, verify_download
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import delete_file
|
||||
from openpilot.frogpilot.common.frogpilot_variables import DEFAULT_MODEL, MODELS_PATH, params, params_default, params_memory
|
||||
|
||||
VERSION = "v16"
|
||||
VERSION_PATH = MODELS_PATH / "model_version"
|
||||
VERSION = "v20"
|
||||
|
||||
CANCEL_DOWNLOAD_PARAM = "CancelModelDownload"
|
||||
DOWNLOAD_PROGRESS_PARAM = "ModelDownloadProgress"
|
||||
MODEL_DOWNLOAD_PARAM = "ModelToDownload"
|
||||
MODEL_DOWNLOAD_ALL_PARAM = "DownloadAllModels"
|
||||
UPDATE_TINYGRAD_PARAM = "UpdateTinygrad"
|
||||
|
||||
DEFAULT_TINYGRAD_SIZE = 87746736
|
||||
TAR_FILE_NAME = f"Tinygrad_{VERSION}.tar.gz"
|
||||
|
||||
TINYGRAD_MODELD_PATH = Path(BASEDIR) / "frogpilot/tinygrad_modeld"
|
||||
TINYGRAD_REPO_PATH = Path(BASEDIR) / "tinygrad_repo"
|
||||
|
||||
class ModelManager:
|
||||
def __init__(self, boot_run=False):
|
||||
def __init__(self):
|
||||
self.available_models = (params.get("AvailableModels", encoding="utf-8") or "").split(",")
|
||||
self.model_versions = (params.get("ModelVersions", encoding="utf-8") or "").split(",")
|
||||
self.model_series = (params.get("AvailableModelSeries", encoding="utf-8") or "").split(",")
|
||||
|
||||
|
||||
self.downloading_model = False
|
||||
|
||||
self.available_models = (params.get("AvailableModels", encoding="utf-8") or "").split(",")
|
||||
self.available_model_names = (params.get("AvailableModelNames", encoding="utf-8") or "").split(",")
|
||||
self.model_versions = (params.get("ModelVersions", encoding="utf-8") or "").split(",")
|
||||
@staticmethod
|
||||
def fetch_models(url):
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=10) as response:
|
||||
return json.loads(response.read().decode("utf-8"))["models"]
|
||||
except Exception as error:
|
||||
handle_request_error(error, None, None, None, None)
|
||||
return []
|
||||
|
||||
self.model_sizes_path = MODELS_PATH / "model_sizes.json"
|
||||
self.tinygrad_sizes_path = MODELS_PATH / "tinygrad_sizes.json"
|
||||
@staticmethod
|
||||
def fetch_all_model_sizes(repo_url):
|
||||
project_path = "firestar5683/StarPilot-Resources"
|
||||
branch = "Models"
|
||||
|
||||
self.model_sizes = load_json_file(self.model_sizes_path)
|
||||
self.tinygrad_sizes = load_json_file(self.tinygrad_sizes_path)
|
||||
if "github" in repo_url:
|
||||
api_url = f"https://api.github.com/repos/{project_path}/contents?ref={branch}"
|
||||
elif "gitlab" in repo_url:
|
||||
api_url = f"https://gitlab.com/api/v4/projects/{urllib.parse.quote_plus(project_path)}/repository/tree?ref={branch}"
|
||||
else:
|
||||
return {}
|
||||
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({"Accept-Language": "en"})
|
||||
self.session.headers.update({"User-Agent": "frogpilot-model-downloader/1.0 (https://github.com/FrogAi/FrogPilot)"})
|
||||
try:
|
||||
response = requests.get(api_url)
|
||||
response.raise_for_status()
|
||||
model_files = [file for file in response.json() if "." in file["name"]]
|
||||
|
||||
if boot_run:
|
||||
self.copy_default_model()
|
||||
self.validate_models()
|
||||
if "gitlab" in repo_url:
|
||||
model_sizes = {}
|
||||
for file in model_files:
|
||||
file_path = file["path"]
|
||||
metadata_url = f"https://gitlab.com/api/v4/projects/{urllib.parse.quote_plus(project_path)}/repository/files/{urllib.parse.quote_plus(file_path)}/raw?ref={branch}"
|
||||
metadata_response = requests.head(metadata_url)
|
||||
metadata_response.raise_for_status()
|
||||
model_sizes[file["name"].rsplit(".", 1)[0]] = int(metadata_response.headers.get("content-length", 0))
|
||||
return model_sizes
|
||||
else:
|
||||
return {file["name"].rsplit(".", 1)[0]: file["size"] for file in model_files if "size" in file}
|
||||
|
||||
except Exception as error:
|
||||
handle_request_error(f"Failed to fetch model sizes from {'GitHub' if 'github' in repo_url else 'GitLab'}: {error}", None, None, None, None)
|
||||
return {}
|
||||
|
||||
def handle_verification_failure(self, model, model_path, file_extension):
|
||||
print(f"Verification failed for model {model}. Retrying from GitLab...")
|
||||
model_url = f"{GITLAB_URL}/Models/{model}.{file_extension}"
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, model_url, MODEL_DOWNLOAD_PARAM, params_memory)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
if verify_download(model_path, model_url):
|
||||
print(f"Model {model} downloaded and verified successfully!")
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(MODEL_DOWNLOAD_PARAM)
|
||||
self.downloading_model = False
|
||||
else:
|
||||
handle_error(model_path, "Verification failed...", "GitLab verification failed", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_model = False
|
||||
|
||||
def download_model(self, model_to_download):
|
||||
self.downloading_model = True
|
||||
|
||||
repo_url = get_repository_url()
|
||||
if not repo_url:
|
||||
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
try:
|
||||
model_index = self.available_models.index(model_to_download)
|
||||
model_version = self.model_versions[model_index]
|
||||
except Exception:
|
||||
handle_error(None, f"Unknown model version for {model_to_download}! Download aborted.", "Model download failed", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
if model_version in ("v8", "v9", "v10", "v11", "v12"):
|
||||
# Download all PKL and metadata files for multi-file tinygrad models (v8 and v9)
|
||||
filenames = [
|
||||
f"{model_to_download}_driving_policy_tinygrad.pkl",
|
||||
f"{model_to_download}_driving_vision_tinygrad.pkl",
|
||||
f"{model_to_download}_driving_policy_metadata.pkl",
|
||||
f"{model_to_download}_driving_vision_metadata.pkl",
|
||||
]
|
||||
if model_version == "v12":
|
||||
filenames += [
|
||||
f"{model_to_download}_driving_off_policy_tinygrad.pkl",
|
||||
f"{model_to_download}_driving_off_policy_metadata.pkl",
|
||||
]
|
||||
for filename in filenames:
|
||||
model_path = MODELS_PATH / filename
|
||||
model_url = f"{repo_url}/Models/{filename}"
|
||||
print(f"Downloading model file: {filename}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, model_url, MODEL_DOWNLOAD_PARAM, params_memory)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
if verify_download(model_path, model_url):
|
||||
print(f"File {filename} downloaded and verified successfully!")
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloaded {filename}!")
|
||||
else:
|
||||
self.handle_verification_failure(filename[:-4], model_path, "pkl")
|
||||
self.downloading_model = False
|
||||
return
|
||||
# After all files are downloaded and verified
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(MODEL_DOWNLOAD_PARAM)
|
||||
|
||||
elif model_version == "v7":
|
||||
# Download both PKL and metadata for OG tinygrad models
|
||||
v7_filenames = [
|
||||
f"{model_to_download}.pkl",
|
||||
f"{model_to_download}_metadata.pkl"
|
||||
]
|
||||
for filename in v7_filenames:
|
||||
model_path = MODELS_PATH / filename
|
||||
model_url = f"{repo_url}/Models/{filename}"
|
||||
print(f"Downloading v7 model file: {filename}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, model_url, MODEL_DOWNLOAD_PARAM, params_memory)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
if verify_download(model_path, model_url):
|
||||
print(f"File {filename} downloaded and verified successfully!")
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloaded {filename}!")
|
||||
else:
|
||||
self.handle_verification_failure(filename.rsplit('.',1)[0], model_path, "pkl")
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
# Once both files are fetched
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(MODEL_DOWNLOAD_PARAM)
|
||||
|
||||
else:
|
||||
# Classic model: download only the .thneed file
|
||||
file_extension = "thneed"
|
||||
model_path = MODELS_PATH / f"{model_to_download}.{file_extension}"
|
||||
model_url = f"{repo_url}/Models/{model_to_download}.{file_extension}"
|
||||
print(f"Downloading classic model: {model_to_download}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, model_url, MODEL_DOWNLOAD_PARAM, params_memory)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
if verify_download(model_path, model_url):
|
||||
print(f"Model {model_to_download} downloaded and verified successfully!")
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(MODEL_DOWNLOAD_PARAM)
|
||||
else:
|
||||
self.handle_verification_failure(model_to_download, model_path, file_extension)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
self.downloading_model = False
|
||||
|
||||
@staticmethod
|
||||
def copy_default_model():
|
||||
default_model_path = MODELS_PATH / f"{DEFAULT_MODEL}.thneed"
|
||||
source_path = Path(__file__).parents[2] / "selfdrive/modeld/models/supercombo.thneed"
|
||||
if source_path.is_file() and not default_model_path.is_file():
|
||||
shutil.copyfile(source_path, default_model_path)
|
||||
print(f"Copied the default model from {source_path} to {default_model_path}")
|
||||
|
||||
def check_models(self, boot_run, repo_url):
|
||||
downloaded_models = [
|
||||
model for model in MODELS_PATH.iterdir()
|
||||
if (MODELS_PATH / f"{model}.thneed").is_file() or all((MODELS_PATH / f"{model}_{filename}").is_file() for filename, _ in TINYGRAD_FILES)
|
||||
]
|
||||
for model_file in downloaded_models:
|
||||
if not any(model in model_file.name for model in set(self.available_models)):
|
||||
available_models = set(self.available_models) - {DEFAULT_MODEL}
|
||||
downloaded_models = set()
|
||||
for model in available_models:
|
||||
try:
|
||||
model_index = self.available_models.index(model)
|
||||
model_version = self.model_versions[model_index]
|
||||
except Exception:
|
||||
model_version = None
|
||||
|
||||
if model_version in ("v8", "v9", "v10", "v11", "v12"):
|
||||
v8_v9_files = [
|
||||
f"{model}_driving_policy_tinygrad.pkl",
|
||||
f"{model}_driving_vision_tinygrad.pkl",
|
||||
f"{model}_driving_policy_metadata.pkl",
|
||||
f"{model}_driving_vision_metadata.pkl",
|
||||
]
|
||||
if model_version == "v12":
|
||||
v8_v9_files += [
|
||||
f"{model}_driving_off_policy_tinygrad.pkl",
|
||||
f"{model}_driving_off_policy_metadata.pkl",
|
||||
]
|
||||
if all((MODELS_PATH / f).is_file() for f in v8_v9_files):
|
||||
downloaded_models.add(model)
|
||||
elif model_version == "v7":
|
||||
filename = f"{model}.pkl"
|
||||
if (MODELS_PATH / filename).is_file():
|
||||
downloaded_models.add(model)
|
||||
else:
|
||||
filename = f"{model}.thneed"
|
||||
if (MODELS_PATH / filename).is_file():
|
||||
downloaded_models.add(model)
|
||||
|
||||
outdated_models = downloaded_models - available_models
|
||||
for model in outdated_models:
|
||||
for model_file in MODELS_PATH.glob(f"{model}*"):
|
||||
print(f"Removing outdated model: {model_file}")
|
||||
delete_file(model_file)
|
||||
|
||||
@@ -65,466 +246,185 @@ class ModelManager:
|
||||
if tmp_file.is_file():
|
||||
delete_file(tmp_file)
|
||||
|
||||
if params.get("Model", encoding="utf-8").removesuffix("_default") not in self.available_models:
|
||||
if params.get("Model", encoding="utf-8") not in self.available_models:
|
||||
params.put("Model", params_default.get("Model", encoding="utf-8"))
|
||||
|
||||
if not (not boot_run and params.get_bool("AutomaticallyDownloadModels")):
|
||||
automatically_download_models = params.get_bool("AutomaticallyDownloadModels")
|
||||
if not automatically_download_models:
|
||||
return
|
||||
|
||||
model_sizes = self.fetch_all_model_sizes(repo_url)
|
||||
if not model_sizes:
|
||||
print("No model size data available. Skipping model checks...")
|
||||
return
|
||||
print("No model size data available. Continuing downloads based on file existence")
|
||||
# do not return; proceed to download missing files
|
||||
|
||||
need_to_update_models = False
|
||||
for model in self.available_models:
|
||||
if self.is_tinygrad_model(model):
|
||||
model_file = MODELS_PATH / f"{model}.thneed"
|
||||
if not model_file.is_file():
|
||||
need_to_update_models = True
|
||||
continue
|
||||
needs_download = False
|
||||
|
||||
expected_size = model_sizes.get(model_file.name)
|
||||
local_size = self.model_sizes.get(model_file.name)
|
||||
# Enhanced model file validation per model version
|
||||
for model in available_models:
|
||||
model_version = None
|
||||
try:
|
||||
model_index = self.available_models.index(model)
|
||||
model_version = self.model_versions[model_index]
|
||||
except Exception:
|
||||
model_version = None
|
||||
|
||||
if expected_size > 0 and local_size != expected_size:
|
||||
print(f"Model {model} is outdated. Deleting {model_file}...")
|
||||
delete_file(model_file)
|
||||
need_to_update_models = True
|
||||
else:
|
||||
model_missing = False
|
||||
model_outdated = False
|
||||
|
||||
for filename, _ in TINYGRAD_FILES:
|
||||
expected_file = MODELS_PATH / f"{model}_{filename}"
|
||||
if not expected_file.is_file():
|
||||
model_missing = True
|
||||
need_to_update_models = True
|
||||
if model_version in ("v8", "v9", "v10", "v11", "v12"):
|
||||
v8_v9_files = [
|
||||
f"{model}_driving_policy_tinygrad.pkl",
|
||||
f"{model}_driving_vision_tinygrad.pkl",
|
||||
f"{model}_driving_policy_metadata.pkl",
|
||||
f"{model}_driving_vision_metadata.pkl",
|
||||
]
|
||||
if model_version == "v12":
|
||||
v8_v9_files += [
|
||||
f"{model}_driving_off_policy_tinygrad.pkl",
|
||||
f"{model}_driving_off_policy_metadata.pkl",
|
||||
]
|
||||
for filename in v8_v9_files:
|
||||
path = MODELS_PATH / filename
|
||||
expected_size = model_sizes.get(filename.rsplit(".", 1)[0])
|
||||
if not path.is_file() or expected_size is None or path.stat().st_size != expected_size:
|
||||
needs_download = True
|
||||
break
|
||||
|
||||
for filename, _ in TINYGRAD_FILES:
|
||||
model_file = f"{model}_{filename}"
|
||||
|
||||
expected_size = model_sizes.get(model_file)
|
||||
local_size = self.model_sizes.get(model_file)
|
||||
|
||||
if expected_size > 0 and local_size != expected_size:
|
||||
model_outdated = True
|
||||
need_to_update_models = True
|
||||
break
|
||||
|
||||
if model_missing or model_outdated:
|
||||
print(f"Model {model} is either missing required files or outdated. Deleting...")
|
||||
for filename, _ in TINYGRAD_FILES:
|
||||
delete_file(MODELS_PATH / f"{model}_{filename}")
|
||||
|
||||
if need_to_update_models:
|
||||
params_memory.put_bool(MODEL_DOWNLOAD_ALL_PARAM, True)
|
||||
|
||||
def check_tinygrad(self, repo_url):
|
||||
tinygrad_url = f"{repo_url}/Tinygrad/{TAR_FILE_NAME}"
|
||||
|
||||
expected_size = get_remote_file_size(tinygrad_url, self.session)
|
||||
local_size = int(self.tinygrad_sizes.get(TAR_FILE_NAME, 0))
|
||||
|
||||
if expected_size > 0 and local_size != expected_size:
|
||||
print(f"Tinygrad version {VERSION} is outdated, expected_size: {expected_size}, local_size: {local_size}, flagging for update...")
|
||||
params.put_bool("TinygradUpdateAvailable", True)
|
||||
|
||||
def copy_default_model(self):
|
||||
classic_default_model_path = MODELS_PATH / "wd-40.thneed"
|
||||
source_path = Path(__file__).parents[1] / "classic_modeld/models/supercombo.thneed"
|
||||
if source_path.is_file() and (not classic_default_model_path.is_file() or source_path.stat().st_size != classic_default_model_path.stat().st_size):
|
||||
shutil.copyfile(source_path, classic_default_model_path)
|
||||
print(f"Copied the classic default model from {source_path} to {classic_default_model_path}")
|
||||
self.update_model_size(classic_default_model_path)
|
||||
|
||||
default_model_path = MODELS_PATH / "national-public-radio.thneed"
|
||||
source_path = Path(__file__).parents[2] / "selfdrive/modeld/models/supercombo.thneed"
|
||||
if source_path.is_file() and (not default_model_path.is_file() or source_path.stat().st_size != default_model_path.stat().st_size):
|
||||
shutil.copyfile(source_path, default_model_path)
|
||||
print(f"Copied the default model from {source_path} to {default_model_path}")
|
||||
self.update_model_size(default_model_path)
|
||||
|
||||
for filename, description in TINYGRAD_FILES:
|
||||
source = TINYGRAD_MODELD_PATH / "models" / filename
|
||||
target = MODELS_PATH / f"{DEFAULT_MODEL}_{filename}"
|
||||
if source.is_file() and (not target.is_file() or source.stat().st_size != target.stat().st_size):
|
||||
shutil.copyfile(source, target)
|
||||
print(f"Copied the tinygrad {description} from {source} to {target}")
|
||||
|
||||
def download_all_models(self):
|
||||
repo_url = get_repository_url(self.session)
|
||||
if not repo_url:
|
||||
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
return
|
||||
|
||||
self.fetch_models(f"{repo_url}/Versions/model_names_{VERSION}.json", repo_url)
|
||||
|
||||
for model in self.available_models:
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
return
|
||||
|
||||
if self.is_tinygrad_model(model):
|
||||
already_downloaded = (MODELS_PATH / f"{model}.thneed").is_file()
|
||||
elif model_version == "v7":
|
||||
filename = f"{model}.pkl"
|
||||
path = MODELS_PATH / filename
|
||||
expected_size = model_sizes.get(model)
|
||||
if not path.is_file() or expected_size is None or path.stat().st_size != expected_size:
|
||||
needs_download = True
|
||||
else:
|
||||
already_downloaded = all((MODELS_PATH / f"{model}_{filename}").is_file() for filename, _ in TINYGRAD_FILES)
|
||||
filename = f"{model}.thneed"
|
||||
path = MODELS_PATH / filename
|
||||
expected_size = model_sizes.get(model)
|
||||
if not path.is_file() or expected_size is None or path.stat().st_size != expected_size:
|
||||
needs_download = True
|
||||
|
||||
if already_downloaded:
|
||||
continue
|
||||
if needs_download:
|
||||
self.download_all_models()
|
||||
|
||||
print(f"Model {model} is not downloaded. Preparing to download...")
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloading \"{self.available_model_names[self.available_models.index(model)]}\"...")
|
||||
self.download_model(model)
|
||||
def update_model_params(self, model_info, repo_url):
|
||||
self.available_models = [model["id"] for model in model_info]
|
||||
self.model_versions = [model["version"] for model in model_info]
|
||||
self.model_series = [model.get("series", "Dom Forgot To Label Me") for model in model_info]
|
||||
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "All models downloaded!")
|
||||
params_memory.remove(MODEL_DOWNLOAD_ALL_PARAM)
|
||||
params.put("AvailableModels", ",".join(self.available_models))
|
||||
params.put("AvailableModelNames", ",".join([model["name"] for model in model_info]))
|
||||
params.put("AvailableModelSeries", ",".join(self.model_series))
|
||||
params.put("CommunityFavorites", ",".join([model["id"] for model in model_info if model.get("community_favorite", False)]))
|
||||
params.put("ModelReleasedDates", ",".join([model.get("released", "2023-01-01") for model in model_info]))
|
||||
params.put("ModelVersions", ",".join(self.model_versions))
|
||||
params.put("CommunityFavorites", ",".join([model["id"] for model in model_info if model.get("community_favorite", False)]))
|
||||
params.put("AvailableModelSeries", ",".join(self.model_series))
|
||||
print("Models list updated successfully")
|
||||
|
||||
def download_model(self, model_to_download):
|
||||
self.downloading_model = True
|
||||
# --- Generate per-model version JSON for offline UI ---
|
||||
try:
|
||||
versions_file = MODELS_PATH / ".model_versions.json"
|
||||
version_map = {model_id: version for model_id, version in zip(self.available_models, self.model_versions)}
|
||||
with open(versions_file, "w") as vf:
|
||||
json.dump(version_map, vf)
|
||||
except Exception as e:
|
||||
print(f"Failed to write .model_versions.json: {e}")
|
||||
# --- end JSON generation ---
|
||||
|
||||
repo_url = get_repository_url(self.session)
|
||||
if not repo_url:
|
||||
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
self.downloading_model = False
|
||||
return
|
||||
# Immediately sync the active ModelVersion param
|
||||
try:
|
||||
current = params.get("Model", encoding="utf-8")
|
||||
if current in version_map:
|
||||
params.put("ModelVersion", version_map[current])
|
||||
print(f"Successfully synced ModelVersion to {version_map[current]} for model {current}")
|
||||
else:
|
||||
print(f"Warning: Model {current} not found in version map")
|
||||
except Exception as e:
|
||||
print(f"Failed to sync ModelVersion for {current}: {e}")
|
||||
|
||||
if self.is_tinygrad_model(model_to_download):
|
||||
model_path = MODELS_PATH / f"{model_to_download}.thneed"
|
||||
model_url = f"{repo_url}/Models/{model_to_download}.thneed"
|
||||
# Also ensure ModelVersion is set for the default model if not already set
|
||||
try:
|
||||
if not params.get("ModelVersion", encoding="utf-8"):
|
||||
default_model = params.get("Model", encoding="utf-8") or DEFAULT_MODEL
|
||||
if default_model in version_map:
|
||||
params.put("ModelVersion", version_map[default_model])
|
||||
print(f"Set default ModelVersion to {version_map[default_model]} for model {default_model}")
|
||||
except Exception as e:
|
||||
print(f"Failed to set default ModelVersion: {e}")
|
||||
|
||||
print(f"Downloading model: {model_to_download}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, model_url, MODEL_DOWNLOAD_PARAM, self.session)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
delete_file(model_path)
|
||||
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
if verify_download(model_path, model_url, self.session):
|
||||
print(f"Model {model_to_download} downloaded and verified successfully!")
|
||||
self.update_model_size(model_path)
|
||||
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(MODEL_DOWNLOAD_PARAM)
|
||||
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
print(f"Verification failed for model {model_to_download}. Retrying from GitLab...")
|
||||
fallback_url = f"{GITLAB_URL}/Models/{model_to_download}.thneed"
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, fallback_url, MODEL_DOWNLOAD_PARAM, self.session)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
delete_file(model_path)
|
||||
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
if verify_download(model_path, fallback_url, self.session):
|
||||
print(f"Model {model_to_download} downloaded and verified successfully from GitLab!")
|
||||
self.update_model_size(model_path)
|
||||
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(MODEL_DOWNLOAD_PARAM)
|
||||
|
||||
self.downloading_model = False
|
||||
else:
|
||||
handle_error(model_path, "Verification failed...", "GitLab verification failed", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
self.downloading_model = False
|
||||
else:
|
||||
all_model_sizes = self.fetch_all_model_sizes(repo_url) or {}
|
||||
tinygrad_filenames = [f"{model_to_download}_{file_key}" for file_key, _ in TINYGRAD_FILES]
|
||||
|
||||
file_sizes = []
|
||||
file_sources = []
|
||||
|
||||
missing = [name for name in tinygrad_filenames if int(all_model_sizes.get(name, 0)) <= 0]
|
||||
if missing:
|
||||
handle_error(None, "Missing size metadata...", f"Sizes not found for: {', '.join(missing)}...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
for filename in tinygrad_filenames:
|
||||
primary_url = f"{repo_url}/Models/compiled/{filename}"
|
||||
file_size = int(all_model_sizes.get(filename, 0))
|
||||
file_sizes.append(file_size)
|
||||
file_sources.append((primary_url, None))
|
||||
|
||||
downloaded_offset_bytes = 0
|
||||
known_file_sizes = [size for size in file_sizes if size > 0]
|
||||
total_model_bytes = sum(known_file_sizes) if len(known_file_sizes) == len(file_sizes) else 0
|
||||
|
||||
for (file_key, description), part_bytes, (primary_url, fallback_url) in zip(TINYGRAD_FILES, file_sizes, file_sources):
|
||||
filename = f"{model_to_download}_{file_key}"
|
||||
model_path = MODELS_PATH / filename
|
||||
|
||||
print(f"Downloading {description} for model: {model_to_download}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, primary_url, MODEL_DOWNLOAD_PARAM, self.session, offset_bytes=downloaded_offset_bytes, total_bytes=total_model_bytes)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
delete_file(model_path)
|
||||
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
if verify_download(model_path, primary_url, self.session):
|
||||
print(f"{description.capitalize()} for {model_to_download} downloaded and verified successfully!")
|
||||
if total_model_bytes:
|
||||
downloaded_offset_bytes += part_bytes
|
||||
continue
|
||||
|
||||
print(f"Verification failed for {filename}. Retrying from GitLab...")
|
||||
fallback_url = f"{GITLAB_URL}/Models/compiled/{filename}"
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, model_path, DOWNLOAD_PROGRESS_PARAM, fallback_url, MODEL_DOWNLOAD_PARAM, self.session, offset_bytes=downloaded_offset_bytes, total_bytes=total_model_bytes)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
delete_file(model_path)
|
||||
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
if verify_download(model_path, fallback_url, self.session):
|
||||
print(f"{description.capitalize()} for {model_to_download} downloaded and verified successfully from GitLab!")
|
||||
if total_model_bytes:
|
||||
downloaded_offset_bytes += part_bytes
|
||||
else:
|
||||
handle_error(model_path, "Verification failed...", f"GitLab verification failed for {filename}", MODEL_DOWNLOAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
print(f"Updating model sizes for {model_to_download}...")
|
||||
for filename, _ in TINYGRAD_FILES:
|
||||
file_path = MODELS_PATH / f"{model_to_download}_{filename}"
|
||||
self.update_model_size(file_path)
|
||||
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(MODEL_DOWNLOAD_PARAM)
|
||||
|
||||
self.downloading_model = False
|
||||
|
||||
def fetch_all_model_sizes(self, repo_url):
|
||||
is_github = "github" in repo_url
|
||||
is_gitlab = "gitlab" in repo_url
|
||||
repo_encoded = quote_plus(RESOURCES_REPO)
|
||||
|
||||
model_sizes = {}
|
||||
try:
|
||||
def fetch_dir_sizes(api_url):
|
||||
sizes = {}
|
||||
print(f"Fetching model metadata: {api_url}")
|
||||
response = self.session.get(api_url, timeout=10)
|
||||
response.raise_for_status()
|
||||
content = response.json()
|
||||
|
||||
model_files = [file for file in content if "." in file["name"]]
|
||||
|
||||
if is_github:
|
||||
for file in model_files:
|
||||
sizes[file["name"]] = file.get("size", 0)
|
||||
else:
|
||||
for file in model_files:
|
||||
file_path = quote_plus(file["path"])
|
||||
metadata_url = f"https://gitlab.com/api/v4/projects/{repo_encoded}/repository/files/{file_path}/raw?ref=Models"
|
||||
head_response = self.session.head(metadata_url, timeout=10)
|
||||
if head_response.ok:
|
||||
sizes[file["name"]] = int(head_response.headers.get("content-length", 0))
|
||||
return sizes
|
||||
|
||||
if is_github:
|
||||
top_api_url = f"https://api.github.com/repos/{RESOURCES_REPO}/contents?ref=Models"
|
||||
version_api_url = f"https://api.github.com/repos/{RESOURCES_REPO}/contents/compiled?ref=Models"
|
||||
elif is_gitlab:
|
||||
top_api_url = f"https://gitlab.com/api/v4/projects/{repo_encoded}/repository/tree?ref=Models"
|
||||
version_api_url = f"https://gitlab.com/api/v4/projects/{repo_encoded}/repository/tree?path=compiled&ref=Models"
|
||||
else:
|
||||
print(f"Unsupported repository URL: {repo_url}")
|
||||
return model_sizes
|
||||
|
||||
model_sizes.update(fetch_dir_sizes(top_api_url))
|
||||
model_sizes.update(fetch_dir_sizes(version_api_url))
|
||||
|
||||
return model_sizes
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
handle_request_error(f"Failed to fetch model sizes from {'GitHub' if is_github else 'GitLab'}: {e}", None, None, None)
|
||||
return {}
|
||||
|
||||
def fetch_models(self, url, repo_url, boot_run=False):
|
||||
try:
|
||||
response = self.session.get(url, timeout=10)
|
||||
response.raise_for_status()
|
||||
model_info = response.json().get("models", [])
|
||||
|
||||
if model_info:
|
||||
self.update_model_params(model_info)
|
||||
self.check_models(boot_run, repo_url)
|
||||
self.check_tinygrad(repo_url)
|
||||
except Exception as exception:
|
||||
handle_request_error(exception, None, None, None)
|
||||
return []
|
||||
|
||||
def is_tinygrad_model(self, model):
|
||||
return self.model_versions[self.available_models.index(model)] in {"v1", "v2", "v3", "v4", "v5", "v6"}
|
||||
|
||||
def update_model_params(self, model_info):
|
||||
self.available_models = [model["id"] for model in model_info]
|
||||
self.available_model_names = [model["name"] for model in model_info]
|
||||
self.model_versions = [model["version"] for model in model_info]
|
||||
|
||||
params.put("AvailableModels", ",".join(self.available_models))
|
||||
params.put("AvailableModelNames", ",".join(self.available_model_names))
|
||||
params.put("ModelVersions", ",".join(self.model_versions))
|
||||
print("Models list updated successfully!")
|
||||
|
||||
def update_models(self, boot_run):
|
||||
def update_models(self, boot_run=False):
|
||||
if self.downloading_model:
|
||||
return
|
||||
|
||||
repo_url = get_repository_url(self.session)
|
||||
repo_url = get_repository_url()
|
||||
if repo_url is None:
|
||||
print("GitHub and GitLab are offline...")
|
||||
return
|
||||
|
||||
self.fetch_models(f"{repo_url}/Versions/model_names_{VERSION}.json", repo_url, boot_run)
|
||||
model_info = self.fetch_models(f"{repo_url}/Versions/model_names_{VERSION}.json")
|
||||
if model_info:
|
||||
self.update_model_params(model_info, repo_url)
|
||||
self.check_models(boot_run, repo_url)
|
||||
|
||||
def update_model_size(self, file_path):
|
||||
self.model_sizes[file_path.name] = file_path.stat().st_size
|
||||
update_json_file(self.model_sizes_path, self.model_sizes)
|
||||
print(f"Updated size for {file_path.name} in {self.model_sizes_path.name}")
|
||||
# Ensure ModelVersion is set immediately after updating model params
|
||||
if boot_run:
|
||||
try:
|
||||
current = params.get("Model", encoding="utf-8")
|
||||
if current and current in [model["id"] for model in model_info]:
|
||||
model_index = [model["id"] for model in model_info].index(current)
|
||||
version = model_info[model_index]["version"]
|
||||
params.put("ModelVersion", version)
|
||||
print(f"Boot sync: Set ModelVersion to {version} for model {current}")
|
||||
except Exception as e:
|
||||
print(f"Boot sync failed: {e}")
|
||||
|
||||
def update_tinygrad_size(self, file_path):
|
||||
self.tinygrad_sizes[TAR_FILE_NAME] = file_path.stat().st_size
|
||||
update_json_file(self.tinygrad_sizes_path, self.tinygrad_sizes)
|
||||
print(f"Updated size for {TAR_FILE_NAME} in {self.tinygrad_sizes_path.name}")
|
||||
|
||||
def update_tinygrad(self):
|
||||
repo_url = get_repository_url(self.session)
|
||||
def download_all_models(self):
|
||||
repo_url = get_repository_url()
|
||||
if not repo_url:
|
||||
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", None, None)
|
||||
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
return
|
||||
|
||||
primary_url = f"{repo_url}/Tinygrad/{TAR_FILE_NAME}"
|
||||
fallback_url = f"https://gitlab.com/{RESOURCES_REPO}/-/raw/Tinygrad/{TAR_FILE_NAME}"
|
||||
model_info = self.fetch_models(f"{repo_url}/Versions/model_names_{VERSION}.json")
|
||||
if model_info:
|
||||
available_models = [model["id"] for model in model_info]
|
||||
available_model_names = [re.sub(r"[🗺️👀📡]", "", model["name"]).strip() for model in model_info]
|
||||
model_versions = [model["version"] for model in model_info]
|
||||
model_series = [model.get("series", "Dom Forgot To Label Me") for model in model_info]
|
||||
|
||||
tinygrad_tar_path = Path("/data/tmp/tinygrad.tar.gz")
|
||||
try:
|
||||
print(f"Attempting to download tinygrad from {primary_url}...")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, tinygrad_tar_path, DOWNLOAD_PROGRESS_PARAM, primary_url, UPDATE_TINYGRAD_PARAM, self.session)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
delete_file(tinygrad_tar_path)
|
||||
|
||||
handle_error(None, "Tinygrad update cancelled...", "Tinygrad update cancelled...", UPDATE_TINYGRAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
params_memory.remove("CancelModelDownload")
|
||||
return
|
||||
|
||||
if not verify_download(tinygrad_tar_path, primary_url, self.session):
|
||||
print(f"Verification failed for {primary_url}. Retrying from GitLab...")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, tinygrad_tar_path, DOWNLOAD_PROGRESS_PARAM, fallback_url, UPDATE_TINYGRAD_PARAM, self.session)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
delete_file(tinygrad_tar_path)
|
||||
|
||||
handle_error(None, "Tinygrad update cancelled...", "Tinygrad update cancelled...", UPDATE_TINYGRAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
params_memory.remove("CancelModelDownload")
|
||||
return
|
||||
|
||||
if not verify_download(tinygrad_tar_path, fallback_url, self.session):
|
||||
handle_error(tinygrad_tar_path, "Verification Failed", "Tinygrad verification failed", UPDATE_TINYGRAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
return
|
||||
|
||||
print("Tinygrad downloaded successfully! Proceeding with installation...")
|
||||
self.update_tinygrad_size(tinygrad_tar_path)
|
||||
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Installing...")
|
||||
|
||||
print("Deleting old tinygrad directories...")
|
||||
delete_file(TINYGRAD_MODELD_PATH)
|
||||
print(f"Removed {TINYGRAD_MODELD_PATH}")
|
||||
delete_file(TINYGRAD_REPO_PATH)
|
||||
print(f"Removed {TINYGRAD_REPO_PATH}")
|
||||
|
||||
extract_tar(tinygrad_tar_path, Path(BASEDIR))
|
||||
|
||||
print("Tinygrad update completed successfully!")
|
||||
|
||||
params.put_bool("TinygradUpdateAvailable", False)
|
||||
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Updated!")
|
||||
params_memory.remove(UPDATE_TINYGRAD_PARAM)
|
||||
|
||||
self.update_tinygrad_models(repo_url)
|
||||
except Exception as exception:
|
||||
handle_error(tinygrad_tar_path, "Update Failed", f"An unexpected error occurred: {exception}", UPDATE_TINYGRAD_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
|
||||
def update_tinygrad_models(self, repo_url=None):
|
||||
print("Updating old Tinygrad models...")
|
||||
|
||||
installed_tinygrad_models = set()
|
||||
for filename, _ in TINYGRAD_FILES:
|
||||
suffix = f"_{filename}"
|
||||
for file_path in MODELS_PATH.glob(f"*{suffix}"):
|
||||
model_name = file_path.name.rsplit(suffix, 1)[0]
|
||||
if model_name in set(self.available_models):
|
||||
installed_tinygrad_models.add(model_name)
|
||||
delete_file(file_path)
|
||||
|
||||
self.copy_default_model()
|
||||
|
||||
update_frogpilot_toggles()
|
||||
|
||||
if repo_url is None:
|
||||
return
|
||||
|
||||
current_model = params.get("Model", encoding="utf-8").removesuffix("_default")
|
||||
|
||||
models_to_redownload = [current_model]
|
||||
models_to_redownload += [model for model in sorted(installed_tinygrad_models) if model != current_model]
|
||||
|
||||
if DEFAULT_MODEL in models_to_redownload:
|
||||
models_to_redownload.remove(DEFAULT_MODEL)
|
||||
|
||||
if models_to_redownload:
|
||||
print(f"Redownloading the following models: {', '.join(models_to_redownload)}")
|
||||
self.fetch_models(f"{repo_url}/Versions/model_names_{VERSION}.json", repo_url, boot_run=True)
|
||||
|
||||
for model in models_to_redownload:
|
||||
for model, model_name, model_version in zip(available_models, available_model_names, model_versions):
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM)
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
return
|
||||
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloading \"{self.available_model_names[self.available_models.index(model)]}\"...")
|
||||
self.download_model(model)
|
||||
if model_version in ("v8", "v9", "v10", "v11", "v12"):
|
||||
required_files = [
|
||||
f"{model}_driving_policy_tinygrad.pkl",
|
||||
f"{model}_driving_vision_tinygrad.pkl",
|
||||
f"{model}_driving_policy_metadata.pkl",
|
||||
f"{model}_driving_vision_metadata.pkl",
|
||||
]
|
||||
if model_version == "v12":
|
||||
required_files += [
|
||||
f"{model}_driving_off_policy_tinygrad.pkl",
|
||||
f"{model}_driving_off_policy_metadata.pkl",
|
||||
]
|
||||
missing = [f for f in required_files if not (MODELS_PATH / f).is_file()]
|
||||
if missing:
|
||||
print(f"Tinygrad model {model} is missing files. Preparing to download...")
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloading \"{model_name}\"...")
|
||||
self.download_model(model)
|
||||
elif model_version == "v7":
|
||||
# OG tinygrad: only need PKL file
|
||||
model_file = MODELS_PATH / f"{model}.pkl"
|
||||
if not model_file.is_file():
|
||||
print(f"PKL model {model} is missing. Preparing to download...")
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloading \"{model_name}\"...")
|
||||
self.download_model(model)
|
||||
else:
|
||||
# Classic: only need .thneed
|
||||
model_file = MODELS_PATH / f"{model}.thneed"
|
||||
if not model_file.is_file():
|
||||
print(f"Classic model {model} is missing. Preparing to download...")
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, f"Downloading \"{model_name}\"...")
|
||||
self.download_model(model)
|
||||
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "All models downloaded!")
|
||||
else:
|
||||
print("No previously installed tinygrad models to redownload")
|
||||
|
||||
update_frogpilot_toggles()
|
||||
|
||||
def validate_models(self):
|
||||
current = params.get("Model", encoding="utf-8")
|
||||
default = params_default.get("Model", encoding="utf-8")
|
||||
|
||||
if current.endswith("_default") and current != default:
|
||||
print(f"Model '{current}' does not match default '{default}', resetting...")
|
||||
params.put("Model", default)
|
||||
|
||||
if VERSION_PATH.is_file():
|
||||
version_name = VERSION_PATH.read_text().strip()
|
||||
if version_name != VERSION or int(self.tinygrad_sizes.get(TAR_FILE_NAME, 0)) == 0:
|
||||
self.update_tinygrad_models()
|
||||
|
||||
self.tinygrad_sizes[TAR_FILE_NAME] = DEFAULT_TINYGRAD_SIZE
|
||||
update_json_file(self.tinygrad_sizes_path, self.tinygrad_sizes)
|
||||
print(f"Updated size for {TAR_FILE_NAME} in {self.tinygrad_sizes_path.name}")
|
||||
|
||||
params.remove("TinygradUpdateAvailable")
|
||||
|
||||
VERSION_PATH.write_text(VERSION)
|
||||
print(f"Updated {VERSION_PATH} to {VERSION}")
|
||||
handle_error(None, "Unable to fetch models...", "Model list unavailable", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"input_std":[[9.281861],[1.8477924],[0.7977224],[0.047467366],[1.7969038],[1.8168812],[1.8351218],[1.785757],[1.7335743],[1.6658221],[1.5893887],[0.047346078],[0.0473731],[0.047383286],[0.047291175],[0.047300573],[0.04712479],[0.046799928]],"model_test_loss":0.027355343103408813,"input_size":18,"current_date_and_time":"2023-08-05_05-11-44","input_mean":[[21.655252],[-0.07694559],[-0.006081294],[-0.007598456],[-0.07309746],[-0.075890236],[-0.07804942],[-0.076106496],[-0.07184982],[-0.06528668],[-0.060404416],[-0.0077262702],[-0.0077046235],[-0.0076850485],[-0.007687444],[-0.007708868],[-0.0077959923],[-0.008017422]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.35776415],[-0.126288],[0.007988413],[0.03850029],[-0.056796823],[0.0072075897],[0.17376427]],"dense_1_W":[[0.010538558,6.7293744,-0.007391298,-1.035045,-0.47446147,1.0940393,0.08225446,-0.5795131,0.573115,1.82923,-0.16999252,0.8818023,0.03560901,-0.9470653,-1.042151,1.1532398,1.9009485,-1.2265956],[0.009010456,-1.3618345,0.039026346,0.22943486,-0.6669799,-0.768271,-0.16828564,-0.13406865,0.19760236,-0.18953702,-0.019137766,0.1468623,0.26367718,0.4732375,0.73679143,0.62450147,0.30198866,-0.93340015],[-0.044318866,2.2138615,9.620889,-0.66480356,-0.06849887,-0.038568456,-0.30580127,-0.088089585,0.31187677,0.5968372,-1.1462088,0.13234706,0.29166338,-0.69978577,-0.01790655,-0.097928315,-0.56950736,0.7560642],[1.3758256,0.8700429,-0.24295154,0.20832618,-0.7735097,1.0379355,-0.60753095,-0.5457418,-1.0892137,-0.6708325,0.24982774,0.09251328,0.08596103,-0.58034134,-0.3046114,-0.19754426,0.006461508,0.5989185],[0.06314328,-2.492993,-0.28200585,0.49902886,0.8513198,-0.5704965,1.0960953,-0.08426956,-0.76074624,0.10669376,0.02775254,-0.42194095,-0.34657434,-0.10863868,0.17019278,0.0963825,-0.10169116,0.21243806],[0.007984091,-1.9276509,-0.0013926353,0.04057924,1.4438432,1.3440262,-2.506722,1.700801,0.72410774,0.13471895,-0.18753268,0.7303607,0.018133093,-0.71643597,0.07050904,-0.30161944,0.085108586,0.061202843],[0.9413167,-0.9954305,-0.19510143,-0.4711845,-0.12398665,-0.45175523,1.0616771,0.28550953,-0.55543137,-0.21576759,-0.2364377,-0.012782618,-0.050565187,0.257694,-0.20975965,0.00022543047,-0.08760746,0.4983852]],"activation":"σ"},{"dense_2_W":[[-0.71804935,0.017023304,-0.099854074,-0.3262651,-0.402829,-0.12073083,0.13537839],[-0.44002575,-0.1071093,-0.58886194,-0.17319413,-0.21134079,0.23135148,0.0006880979],[-0.55711204,-0.8693639,-0.6443761,-0.7382846,0.18980889,-0.6082511,-0.63103676],[0.11338112,-0.5327033,0.2856197,0.32239884,-0.72682667,0.5302305,-0.45094746],[-0.35197002,-0.14440043,-0.025249843,-0.48697743,0.3340018,-0.25992322,0.0050561456],[0.34757507,0.15670523,-0.14263277,0.50704384,-0.020171141,0.6408679,-0.3074123],[0.40258837,-0.59363264,0.35948923,-0.060853776,-0.0072541097,0.89743376,-0.49789146],[-0.63476,0.29580453,0.1689314,-0.5061521,0.24901053,-0.23218995,0.57968193],[-0.66173035,0.50861925,-0.5845035,-0.6602214,0.8341883,-0.31437424,0.8046359],[0.038493533,0.15464582,-0.04848341,-0.57820857,-0.25891733,-0.47527292,0.21441916],[-0.15464531,-0.07454202,-0.8215851,-0.12614948,-0.5924209,0.00017916794,0.24154592],[0.6091424,-0.112086505,0.1144111,-0.31035227,-0.9237534,0.041003596,-0.3542808],[0.2989792,-0.23780549,0.116059326,-0.6056522,0.5499526,-0.9001413,0.5200723]],"activation":"σ","dense_2_b":[[-0.2638964],[-0.24607328],[-0.15493082],[-0.0071187904],[-0.23515384],[-0.09098133],[-0.034066215],[0.03609036],[-0.03842933],[-0.042302527],[-0.2439947],[-0.16342077],[-0.01030317]]},{"dense_3_W":[[0.45771673,-0.2084603,0.3570187,0.35835707,0.13684571,-0.582958,0.29889587,0.43543494,0.11841301,-0.26185623,-0.4988437,0.5752996,-0.28057483],[-0.19594137,0.050280698,-0.29057446,-0.2829161,-0.16987291,-0.21278952,-0.59946907,0.21295123,0.7040468,0.53549695,-0.52553934,-0.19560973,0.6233473],[-0.19091003,0.08669418,-0.5792192,0.57799137,-0.36263424,0.6037143,0.27898273,-0.30951327,-0.3572644,0.46720102,-0.5403428,0.32415462,-0.60570025]],"activation":"identity","dense_3_b":[[-0.0047377176],[0.023170695],[-0.021546118]]},{"dense_4_W":[[-0.12453941,-1.006034,0.96776205]],"dense_4_b":[[-0.02351544]],"activation":"identity"}]}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 912 KiB After Width: | Height: | Size: 503 KiB |
@@ -96,9 +96,9 @@ class ThemeManager:
|
||||
def download_theme(self, theme_component, theme_name, asset_param, frogpilot_toggles):
|
||||
self.downloading_theme = True
|
||||
|
||||
repo_url = get_repository_url(self.session)
|
||||
repo_url = get_repository_url()
|
||||
if not repo_url:
|
||||
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", asset_param, DOWNLOAD_PROGRESS_PARAM)
|
||||
handle_error(None, "GitHub and GitLab are offline...", "Repository unavailable", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_theme = False
|
||||
return
|
||||
|
||||
@@ -122,16 +122,16 @@ class ThemeManager:
|
||||
delete_file(theme_path)
|
||||
|
||||
print(f"Downloading theme from GitHub: {theme_name}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, self.session)
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, params_memory)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
delete_file(theme_path)
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", asset_param, DOWNLOAD_PROGRESS_PARAM)
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
|
||||
self.downloading_theme = False
|
||||
return
|
||||
|
||||
if verify_download(theme_path, theme_url, self.session):
|
||||
if verify_download(theme_path, theme_url):
|
||||
print(f"Theme {theme_name} downloaded and verified successfully from GitHub!")
|
||||
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
|
||||
|
||||
@@ -149,7 +149,7 @@ class ThemeManager:
|
||||
elif self.handle_verification_failure(extension, theme_component, theme_name, asset_param, theme_path, download_path, frogpilot_toggles):
|
||||
return
|
||||
|
||||
handle_error(download_path, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM)
|
||||
handle_error(download_path, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_theme = False
|
||||
|
||||
def fetch_assets(self, repo_url, frogpilot_toggles):
|
||||
@@ -252,7 +252,7 @@ class ThemeManager:
|
||||
|
||||
except requests.exceptions.RequestException as error:
|
||||
print(f"Request failed: {error}")
|
||||
handle_request_error(f"Failed to fetch theme sizes from {'GitHub' if is_github else 'GitLab'}: {error}", None, None, None)
|
||||
handle_request_error(f"Failed to fetch theme sizes from {'GitHub' if is_github else 'GitLab'}: {error}", None, None, None, None)
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
@@ -332,9 +332,9 @@ class ThemeManager:
|
||||
|
||||
theme_url = download_link + extension
|
||||
print(f"Downloading theme from GitLab: {theme_name}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, self.session)
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, params_memory)
|
||||
|
||||
if verify_download(theme_path, theme_url, self.session):
|
||||
if verify_download(theme_path, theme_url):
|
||||
print(f"Theme {theme_name} downloaded and verified successfully from GitLab!")
|
||||
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
|
||||
|
||||
@@ -350,7 +350,7 @@ class ThemeManager:
|
||||
self.update_themes(frogpilot_toggles)
|
||||
return True
|
||||
|
||||
handle_error(None, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM)
|
||||
handle_error(None, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_theme = False
|
||||
return False
|
||||
|
||||
@@ -563,7 +563,7 @@ class ThemeManager:
|
||||
if self.downloading_theme:
|
||||
return
|
||||
|
||||
repo_url = get_repository_url(self.session)
|
||||
repo_url = get_repository_url()
|
||||
if repo_url is None:
|
||||
print("GitHub and GitLab are offline...")
|
||||
return
|
||||
|
||||
@@ -150,7 +150,7 @@ def frogpilot_boot_functions(build_metadata, params_cache):
|
||||
params_cache.clear_all()
|
||||
|
||||
FrogPilotVariables().update(holiday_theme="stock", started=False)
|
||||
ModelManager(boot_run=True)
|
||||
ModelManager()
|
||||
ThemeManager(boot_run=True).update_active_theme(time_validated=system_time_valid(), frogpilot_toggles=get_frogpilot_toggles(), boot_run=True)
|
||||
|
||||
if VIDEO_CACHE_PATH.exists():
|
||||
|
||||
@@ -296,12 +296,12 @@ def update_openpilot():
|
||||
if params.get("UpdaterState", encoding="utf-8") != "idle":
|
||||
return
|
||||
|
||||
while params.get_bool("IsOnroad") or params_memory.get_bool("UpdateSpeedLimits") or running_threads.get("lock_doors", threading.Thread()).is_alive():
|
||||
time.sleep(60)
|
||||
|
||||
if not update_available():
|
||||
return
|
||||
|
||||
while params.get_bool("IsOnroad") or params_memory.get_bool("UpdateSpeedLimits") or running_threads.get("lock_doors", threading.Thread()).is_alive():
|
||||
time.sleep(60)
|
||||
|
||||
while True:
|
||||
if not update_available():
|
||||
break
|
||||
|
||||
@@ -13,7 +13,8 @@ from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.car import gen_empty_fingerprint
|
||||
from openpilot.selfdrive.car.car_helpers import interfaces
|
||||
from openpilot.selfdrive.car.gm.values import GMFlags
|
||||
from openpilot.selfdrive.car.gm.values import EV_CAR as GM_EV_CAR, GMFlags
|
||||
from openpilot.selfdrive.car.hyundai.values import EV_CAR as HYUNDAI_EV_CAR
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
|
||||
from openpilot.selfdrive.car.mock.interface import CarInterface
|
||||
from openpilot.selfdrive.car.mock.values import CAR as MOCK
|
||||
@@ -32,6 +33,7 @@ params_memory = Params("/dev/shm/params")
|
||||
|
||||
GearShifter = car.CarState.GearShifter
|
||||
SafetyModel = car.CarParams.SafetyModel
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
|
||||
CITY_SPEED_LIMIT = 25 # 55mph is typically the minimum speed for highways
|
||||
CRUISING_SPEED = 5 # Roughly the speed cars go when not touching the gas while in drive
|
||||
@@ -40,11 +42,16 @@ EARTH_RADIUS = 6378137 # Radius of the Earth in meters
|
||||
MAX_T_FOLLOW = 3.0 # Maximum allowed following duration. Larger values risk losing track of the lead but may be increased as models improve
|
||||
MINIMUM_LATERAL_ACCELERATION = 1.3 # m/s^2, typical minimum lateral acceleration when taking curves
|
||||
PLANNER_TIME = ModelConstants.T_IDXS[-1] # Length of time the model projects out for
|
||||
|
||||
THRESHOLD = 0.63 # Requires the condition to be true for ~1 second
|
||||
|
||||
def scale_threshold(v_ego):#0 40 60 80 100 0 40 60 80 100
|
||||
# More aggressive with hysteresis and lead probability: faster activation at higher speeds
|
||||
return np.interp(v_ego, [0, 17.9, 26.8, 35.8, 44.7], [0.58, 0.60, 0.62, 0.75, 0.9])
|
||||
|
||||
NON_DRIVING_GEARS = [GearShifter.neutral, GearShifter.park, GearShifter.reverse, GearShifter.unknown]
|
||||
|
||||
RESOURCES_REPO = "FrogAi/FrogPilot-Resources"
|
||||
RESOURCES_REPO = "firestar5683/StarPilot-Resources"
|
||||
|
||||
ACTIVE_THEME_PATH = Path(__file__).parents[1] / "assets/active_theme"
|
||||
METADATAS_PATH = Path(__file__).parents[1] / "assets/model_metadata"
|
||||
@@ -66,12 +73,11 @@ KONIK_PATH = Path("/cache/use_konik")
|
||||
|
||||
MAPD_PATH = Path("/data/media/0/osm/mapd")
|
||||
MAPS_PATH = Path("/data/media/0/osm/offline")
|
||||
|
||||
NNFF_MODELS_PATH = Path(BASEDIR) / "frogpilot/assets/nnff_models"
|
||||
|
||||
DEFAULT_MODEL = "firehose"
|
||||
DEFAULT_MODEL = "bd2"
|
||||
DEFAULT_MODEL_NAME = "Firehose (Default) 👀📡"
|
||||
DEFAULT_MODEL_VERSION = "v9"
|
||||
DEFAULT_MODEL_VERSION = "v11"
|
||||
|
||||
BUTTON_FUNCTIONS = {
|
||||
"NOTHING": 0,
|
||||
@@ -90,12 +96,15 @@ EXCLUDED_KEYS = {
|
||||
}
|
||||
|
||||
TINYGRAD_FILES = [
|
||||
("driving_off_policy_metadata.pkl", "off-policy metadata"),
|
||||
("driving_off_policy_tinygrad.pkl", "off-policy model"),
|
||||
("driving_policy_metadata.pkl", "policy metadata"),
|
||||
("driving_policy_tinygrad.pkl", "policy model"),
|
||||
("driving_vision_metadata.pkl", "vision metadata"),
|
||||
("driving_vision_tinygrad.pkl", "vision model"),
|
||||
]
|
||||
|
||||
|
||||
@cache
|
||||
def get_nnff_model_files():
|
||||
model_dir = Path(NNFF_MODELS_PATH)
|
||||
@@ -117,13 +126,15 @@ def update_frogpilot_toggles():
|
||||
frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("AccelerationPath", "1", 2, "0"),
|
||||
("AccelerationProfile", "2", 0, "0"),
|
||||
("AdjacentLeadsUI", "1", 3, "0"),
|
||||
("AdjacentLeadsUI", "0", 3, "0"),
|
||||
("AdjacentPath", "0", 3, "0"),
|
||||
("AdjacentPathMetrics", "0", 3, "0"),
|
||||
("AdvancedCustomUI", "0", 2, "0"),
|
||||
("AdvancedLateralTune", "0", 3, "0"),
|
||||
("AdvancedLateralTune", "1", 2, "0"),
|
||||
("AdvancedLongitudinalTune", "0", 3, "0"),
|
||||
("EVTuning", "", 3, "0"),
|
||||
("AggressiveFollow", "1.25", 2, "1.25"),
|
||||
("AggressiveFollowHigh", "1.25", 2, "1.25"),
|
||||
("AggressiveJerkAcceleration", "50", 3, "50"),
|
||||
("AggressiveJerkDanger", "100", 3, "100"),
|
||||
("AggressiveJerkDeceleration", "50", 3, "50"),
|
||||
@@ -133,19 +144,22 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("AlertVolumeControl", "0", 2, "0"),
|
||||
("AlwaysOnDM", "0", 0, "0"),
|
||||
("AlwaysOnLateral", "1", 0, "0"),
|
||||
("AlwaysOnLateralLKAS", "1", 2, "0"),
|
||||
("AlwaysOnLateralMain", "1", 2, "0"),
|
||||
("AlwaysOnLateralLKAS", "1", 0, "0"),
|
||||
("AlwaysOnLateralMain", "1", 0, "0"),
|
||||
("AMapKey1", "", 0, ""),
|
||||
("AMapKey2", "", 0, ""),
|
||||
("AutomaticallyDownloadModels", "1", 1, "0"),
|
||||
("AutomaticUpdates", "1", 0, "1"),
|
||||
("AvailableModelNames", "", 1, ""),
|
||||
("AvailableModelSeries", "", 1, ""),
|
||||
("AvailableModels", "", 1, ""),
|
||||
("CommunityFavorites", "", 1, ""),
|
||||
("UserFavorites", "", 0, ""),
|
||||
("BigMap", "0", 2, "0"),
|
||||
("BlacklistedModels", "", 2, ""),
|
||||
("BlindSpotMetrics", "1", 3, "0"),
|
||||
("BlindSpotPath", "1", 1, "0"),
|
||||
("BorderMetrics", "0", 3, "0"),
|
||||
("BorderMetrics", "1", 3, "0"),
|
||||
("CalibratedLateralAcceleration", str(DEFAULT_LATERAL_ACCELERATION), 2, str(DEFAULT_LATERAL_ACCELERATION)),
|
||||
("CalibrationProgress", "0", 3, "0"),
|
||||
("CameraView", "3", 2, "0"),
|
||||
@@ -157,16 +171,16 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("CECurvesLead", "0", 1, "0"),
|
||||
("CELead", "0", 1, "0"),
|
||||
("CEModelStopTime", str(PLANNER_TIME - 2), 2, "0"),
|
||||
("CENavigation", "1", 2, "0"),
|
||||
("CENavigationIntersections", "0", 2, "0"),
|
||||
("CENavigation", "0", 2, "0"),
|
||||
("CENavigationIntersections", "1", 2, "0"),
|
||||
("CENavigationLead", "1", 2, "0"),
|
||||
("CENavigationTurns", "1", 2, "0"),
|
||||
("CESignalSpeed", "55", 2, "0"),
|
||||
("CESignalLaneDetection", "1", 2, "0"),
|
||||
("CESlowerLead", "0", 1, "0"),
|
||||
("CESlowerLead", "1", 1, "0"),
|
||||
("CESpeed", "0", 1, "0"),
|
||||
("CESpeedLead", "0", 1, "0"),
|
||||
("CEStoppedLead", "0", 1, "0"),
|
||||
("CEStoppedLead", "1", 1, "0"),
|
||||
("ClusterOffset", "1.015", 2, "1.015"),
|
||||
("Compass", "0", 1, "0"),
|
||||
("ConditionalExperimental", "1", 1, "0"),
|
||||
@@ -184,7 +198,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("CustomUI", "1", 1, "0"),
|
||||
("DecelerationProfile", "1", 2, "0"),
|
||||
("DeveloperMetrics", "1", 3, "0"),
|
||||
("DeveloperSidebar", "1", 3, "0"),
|
||||
("DeveloperSidebar", "0", 3, "0"),
|
||||
("DeveloperSidebarMetric1", "1", 3, "0"),
|
||||
("DeveloperSidebarMetric2", "2", 3, "0"),
|
||||
("DeveloperSidebarMetric3", "3", 3, "0"),
|
||||
@@ -193,7 +207,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("DeveloperSidebarMetric6", "6", 3, "0"),
|
||||
("DeveloperSidebarMetric7", "7", 3, "0"),
|
||||
("DeveloperWidgets", "1", 3, "0"),
|
||||
("DeveloperUI", "0", 3, "0"),
|
||||
("DeveloperUI", "1", 3, "0"),
|
||||
("DeviceManagement", "1", 1, "0"),
|
||||
("DeviceShutdown", "9", 1, "33"),
|
||||
("DisableOnroadUploads", "0", 2, "0"),
|
||||
@@ -203,17 +217,17 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("DistanceButtonControl", "1", 2, "0"),
|
||||
("DriverCamera", "0", 1, "0"),
|
||||
("DynamicPathWidth", "0", 2, "0"),
|
||||
("DynamicPedalsOnUI", "1", 1, "0"),
|
||||
("DynamicPedalsOnUI", "1", 2, "0"),
|
||||
("EngageVolume", "101", 2, "101"),
|
||||
("ExperimentalGMTune", "0", 2, "0"),
|
||||
("ExperimentalLongitudinalEnabled", "0", 0, "0"),
|
||||
("ExperimentalModeConfirmed", "0", 0, "0"),
|
||||
("Fahrenheit", "0", 3, "0"),
|
||||
("FavoriteDestinations", "", 0, ""),
|
||||
("ForceAutoTune", "0", 3, "0"),
|
||||
("ForceAutoTuneOff", "0", 3, "0"),
|
||||
("ForceAutoTune", "0", 2, "0"),
|
||||
("ForceAutoTuneOff", "1", 2, "0"),
|
||||
("ForceFingerprint", "0", 2, "0"),
|
||||
("ForceMPHDashboard", "0", 3, "0"),
|
||||
("ForceMPHDashboard", "0", 2, "0"),
|
||||
("ForceStops", "0", 2, "0"),
|
||||
("ForceTorqueController", "0", 3, "0"),
|
||||
("FPSCounter", "1", 3, "0"),
|
||||
@@ -222,6 +236,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("FrogsGoMoosTweak", "1", 2, "0"),
|
||||
("FullMap", "0", 2, "0"),
|
||||
("GasRegenCmd", "1", 2, "0"),
|
||||
("GMPedalLongitudinal", "1", 2, "1"),
|
||||
("GithubSshKeys", "", 0, ""),
|
||||
("GithubUsername", "", 0, ""),
|
||||
("GoatScream", "0", 1, "0"),
|
||||
@@ -235,10 +250,10 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("HideMaxSpeed", "0", 2, "0"),
|
||||
("HideSpeed", "0", 2, "0"),
|
||||
("HideSpeedLimit", "0", 2, "0"),
|
||||
("HigherBitrate", "0", 2, "0"),
|
||||
("HigherBitrate", "0", 3, "0"),
|
||||
("HolidayThemes", "1", 0, "0"),
|
||||
("HumanAcceleration", "1", 2, "0"),
|
||||
("HumanFollowing", "1", 2, "0"),
|
||||
("HumanAcceleration", "0", 2, "0"),
|
||||
("HumanFollowing", "0", 2, "0"),
|
||||
("IncreasedStoppedDistance", "0", 1, "0"),
|
||||
("IncreaseThermalLimits", "0", 2, "0"),
|
||||
("IsLdwEnabled", "0", 0, "0"),
|
||||
@@ -246,13 +261,13 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("KonikDongleId", "", 0, ""),
|
||||
("KonikMinutes", "0", 0, "0"),
|
||||
("LaneChanges", "1", 0, "1"),
|
||||
("LaneChangeTime", "1.0", 1, "0"),
|
||||
("LaneDetectionWidth", "0", 1, "0"),
|
||||
("LaneChangeTime", "2.0", 0, "0"),
|
||||
("LaneDetectionWidth", "0", 2, "0"),
|
||||
("LaneLinesWidth", "4", 2, "2"),
|
||||
("LateralTune", "1", 1, "0"),
|
||||
("LateralTune", "1", 2, "0"),
|
||||
("LeadDepartingAlert", "0", 0, "0"),
|
||||
("LeadDetectionThreshold", "35", 3, "50"),
|
||||
("LeadInfo", "1", 3, "0"),
|
||||
("LeadInfo", "1", 2, "0"),
|
||||
("LiveDelay", "", 0, ""),
|
||||
("LKASButtonControl", "5", 2, "0"),
|
||||
("LockDoors", "1", 0, "0"),
|
||||
@@ -261,33 +276,36 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("LongitudinalActuatorDelay", "", 3, ""),
|
||||
("LongitudinalActuatorDelayStock", "", 3, ""),
|
||||
("LongitudinalTune", "1", 0, "0"),
|
||||
("TrailerLoad", "0", 2, "0"),
|
||||
("LongPitch", "1", 2, "0"),
|
||||
("LoudBlindspotAlert", "0", 0, "0"),
|
||||
("LowVoltageShutdown", str(VBATT_PAUSE_CHARGING), 3, str(VBATT_PAUSE_CHARGING)),
|
||||
("LowVoltageShutdown", str(VBATT_PAUSE_CHARGING), 2, str(VBATT_PAUSE_CHARGING)),
|
||||
("MapAcceleration", "0", 1, "0"),
|
||||
("MapboxPublicKey", "", 0, ""),
|
||||
("MapboxSecretKey", "", 0, ""),
|
||||
("MapDeceleration", "0", 1, "0"),
|
||||
("MapGears", "0", 2, "0"),
|
||||
("MapGears", "0", 1, "0"),
|
||||
("MapsSelected", "", 0, ""),
|
||||
("MapStyle", "1", 2, "0"),
|
||||
("MaxDesiredAcceleration", "4.0", 2, "2.0"),
|
||||
("MinimumLaneChangeSpeed", str(LANE_CHANGE_SPEED_MIN / CV.MPH_TO_MS), 2, str(LANE_CHANGE_SPEED_MIN / CV.MPH_TO_MS)),
|
||||
("Model", DEFAULT_MODEL + "_default", 1, DEFAULT_MODEL + "_default"),
|
||||
("Model", DEFAULT_MODEL, 1, DEFAULT_MODEL),
|
||||
("ModelDrivesAndScores", "", 2, ""),
|
||||
("ModelRandomizer", "0", 2, "0"),
|
||||
("ModelReleasedDates", "", 1, ""),
|
||||
("ModelUI", "1", 2, "0"),
|
||||
("ModelVersions", "", 2, ""),
|
||||
("SortModelsByDate", "0", 2, "0"),
|
||||
("NavigationUI", "1", 1, "0"),
|
||||
("NavSettingLeftSide", "0", 0, "0"),
|
||||
("NavSettingTime24h", "0", 0, "0"),
|
||||
("NewLongAPI", "1", 3, "1"),
|
||||
("NNFF", "1", 2, "0"),
|
||||
("NNFFLite", "1", 2, "0"),
|
||||
("NewLongAPI", "0", 2, "1"),
|
||||
("NNFF", "0", 2, "0"),
|
||||
("NNFFLite", "0", 2, "0"),
|
||||
("NoLogging", "0", 2, "0"),
|
||||
("NoUploads", "0", 2, "0"),
|
||||
("NudgelessLaneChange", "1", 0, "0"),
|
||||
("NumericalTemp", "1", 3, "0"),
|
||||
("NudgelessLaneChange", "0", 0, "0"),
|
||||
("NumericalTemp", "1", 2, "0"),
|
||||
("Offset1", "5", 0, "0"),
|
||||
("Offset2", "5", 0, "0"),
|
||||
("Offset3", "5", 0, "0"),
|
||||
@@ -300,15 +318,15 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("openpilotMinutes", "0", 0, "0"),
|
||||
("PathEdgeWidth", "20", 2, "0"),
|
||||
("PathWidth", "6.1", 2, "5.9"),
|
||||
("PauseAOLOnBrake", "0", 1, "0"),
|
||||
("PauseLateralOnSignal", "0", 1, "0"),
|
||||
("PauseLateralSpeed", "0", 1, "0"),
|
||||
("PedalsOnUI", "0", 1, "0"),
|
||||
("PauseAOLOnBrake", "0", 2, "0"),
|
||||
("PauseLateralOnSignal", "0", 2, "0"),
|
||||
("PauseLateralSpeed", "0", 2, "0"),
|
||||
("PedalsOnUI", "0", 2, "0"),
|
||||
("PersonalizeOpenpilot", "1", 0, "0"),
|
||||
("PreferredSchedule", "2", 0, "0"),
|
||||
("PromptDistractedVolume", "101", 2, "101"),
|
||||
("PromptVolume", "101", 2, "101"),
|
||||
("QOLLateral", "1", 1, "0"),
|
||||
("QOLLateral", "1", 2, "0"),
|
||||
("QOLLongitudinal", "1", 1, "0"),
|
||||
("QOLVisuals", "1", 0, "0"),
|
||||
("RadarTracksUI", "0", 3, "0"),
|
||||
@@ -318,19 +336,20 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("RecordFront", "0", 0, "0"),
|
||||
("RefuseVolume", "101", 2, "101"),
|
||||
("RelaxedFollow", "1.75", 2, "1.75"),
|
||||
("RelaxedJerkAcceleration", "100", 3, "100"),
|
||||
("RelaxedFollowHigh", "1.75", 2, "1.75"),
|
||||
("RelaxedJerkAcceleration", "50", 3, "50"),
|
||||
("RelaxedJerkDanger", "100", 3, "100"),
|
||||
("RelaxedJerkDeceleration", "100", 3, "100"),
|
||||
("RelaxedJerkSpeed", "100", 3, "100"),
|
||||
("RelaxedJerkSpeedDecrease", "100", 3, "100"),
|
||||
("RelaxedJerkDeceleration", "50", 3, "50"),
|
||||
("RelaxedJerkSpeed", "50", 3, "50"),
|
||||
("RelaxedJerkSpeedDecrease", "50", 3, "50"),
|
||||
("RelaxedPersonalityProfile", "1", 2, "0"),
|
||||
("ReverseCruise", "0", 1, "0"),
|
||||
("ReverseCruise", "1", 1, "0"),
|
||||
("RoadEdgesWidth", "2", 2, "2"),
|
||||
("RoadNameUI", "1", 1, "0"),
|
||||
("RoadNameUI", "1", 2, "0"),
|
||||
("RotatingWheel", "1", 1, "0"),
|
||||
("ScreenBrightness", "101", 2, "101"),
|
||||
("ScreenBrightnessOnroad", "101", 2, "101"),
|
||||
("ScreenManagement", "1", 1, "0"),
|
||||
("ScreenManagement", "1", 2, "0"),
|
||||
("ScreenRecorder", "1", 2, "0"),
|
||||
("ScreenTimeout", "30", 2, "30"),
|
||||
("ScreenTimeoutOnroad", "30", 2, "10"),
|
||||
@@ -348,13 +367,13 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("ShownToggleDescriptions", "", 0, ""),
|
||||
("ShowSLCOffset", "1", 0, "0"),
|
||||
("ShowSpeedLimits", "1", 1, "0"),
|
||||
("ShowSteering", "0", 3, "0"),
|
||||
("ShowStoppingPoint", "1", 3, "0"),
|
||||
("ShowStoppingPointMetrics", "1", 3, "0"),
|
||||
("ShowSteering", "1", 3, "0"),
|
||||
("ShowStoppingPoint", "0", 2, "0"),
|
||||
("ShowStoppingPointMetrics", "0", 2, "0"),
|
||||
("ShowStorageLeft", "0", 3, "0"),
|
||||
("ShowStorageUsed", "0", 3, "0"),
|
||||
("Sidebar", "0", 0, "0"),
|
||||
("SignalMetrics", "0", 3, "0"),
|
||||
("SignalMetrics", "0", 2, "0"),
|
||||
("SLCConfirmation", "0", 0, "0"),
|
||||
("SLCConfirmationHigher", "0", 0, "0"),
|
||||
("SLCConfirmationLower", "0", 0, "0"),
|
||||
@@ -367,28 +386,31 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("SLCPriority2", "Map Data", 2, "Map Data"),
|
||||
("SLCPriority3", "Dashboard", 2, "Dashboard"),
|
||||
("SNGHack", "1", 2, "0"),
|
||||
("SpeedLimitChangedAlert", "0", 0, "0"),
|
||||
("SpeedLimitChangedAlert", "1", 0, "0"),
|
||||
("SpeedLimitController", "1", 0, "0"),
|
||||
("SpeedLimitFiller", "0", 0, "0"),
|
||||
("SpeedLimitSources", "0", 3, "0"),
|
||||
("SpeedLimitSources", "0", 2, "0"),
|
||||
("SshEnabled", "0", 0, "0"),
|
||||
("StartupMessageBottom", "Human-tested, frog-approved 🐸", 0, "Always keep hands on wheel and eyes on road"),
|
||||
("StartupMessageTop", "Hop in and buckle up!", 0, "Be ready to take over at any time"),
|
||||
("StandardFollow", "1.45", 2, "1.45"),
|
||||
("StandardJerkAcceleration", "100", 3, "100"),
|
||||
("StandardFollowHigh", "1.45", 2, "1.45"),
|
||||
("StandardJerkAcceleration", "50", 3, "50"),
|
||||
("StandardJerkDanger", "100", 3, "100"),
|
||||
("StandardJerkDeceleration", "100", 3, "100"),
|
||||
("StandardJerkSpeed", "100", 3, "100"),
|
||||
("StandardJerkSpeedDecrease", "100", 3, "100"),
|
||||
("StandardJerkDeceleration", "50", 3, "50"),
|
||||
("StandardJerkSpeed", "50", 3, "50"),
|
||||
("StandardJerkSpeedDecrease", "50", 3, "50"),
|
||||
("StandardPersonalityProfile", "1", 2, "0"),
|
||||
("StandbyMode", "0", 1, "0"),
|
||||
("StandbyMode", "0", 2, "0"),
|
||||
("StartAccel", "", 3, ""),
|
||||
("StartAccelStock", "", 3, ""),
|
||||
("StaticPedalsOnUI", "0", 1, "0"),
|
||||
("StaticPedalsOnUI", "0", 2, "0"),
|
||||
("SteerDelay", "", 3, ""),
|
||||
("SteerDelayStock", "", 3, ""),
|
||||
("SteerFriction", "", 3, ""),
|
||||
("SteerFrictionStock", "", 3, ""),
|
||||
("SteerOffset", "", 3, ""),
|
||||
("SteerOffsetStock", "", 3, ""),
|
||||
("SteerKP", "", 3, ""),
|
||||
("SteerKPStock", "", 3, ""),
|
||||
("SteerLatAccel", "", 3, ""),
|
||||
@@ -403,8 +425,6 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("TacoTune", "0", 2, "0"),
|
||||
("TacoTuneHacks", "0", 2, "0"),
|
||||
("TetheringEnabled", "0", 0, "0"),
|
||||
("ThemesDownloaded", "", 0, ""),
|
||||
("TinygradUpdateAvailable", "0", 1, "0"),
|
||||
("ToyotaDoors", "1", 0, "0"),
|
||||
("TrafficFollow", "0.5", 2, "0.5"),
|
||||
("TrafficJerkAcceleration", "50", 3, "50"),
|
||||
@@ -413,8 +433,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("TrafficJerkSpeed", "50", 3, "50"),
|
||||
("TrafficJerkSpeedDecrease", "50", 3, "50"),
|
||||
("TrafficPersonalityProfile", "1", 2, "0"),
|
||||
("TuningLevel", "0", 0, "0"),
|
||||
("TuningLevelConfirmed", "0", 0, "0"),
|
||||
("TuningLevel", "3", 0, "0"),
|
||||
("TuningLevelConfirmed", "1", 0, "0"),
|
||||
("TurnDesires", "0", 2, "0"),
|
||||
("UnlimitedLength", "1", 2, "0"),
|
||||
("UnlockDoors", "1", 0, "0"),
|
||||
@@ -431,7 +451,9 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("WarningImmediateVolume", "101", 2, "101"),
|
||||
("WarningSoftVolume", "101", 2, "101"),
|
||||
("WheelIcon", "frog", 0, "stock"),
|
||||
("WheelSpeed", "0", 2, "0")
|
||||
("WheelSpeed", "0", 2, "0"),
|
||||
("StopDistance", "6", 3, "6"),
|
||||
("RecoveryPower", "1.0", 2, "1.0")
|
||||
]
|
||||
|
||||
misc_tuning_levels: list[tuple[str, str | bytes, int, str]] = [
|
||||
@@ -526,6 +548,10 @@ class FrogPilotVariables:
|
||||
safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
|
||||
CP.safetyConfigs = [safety_config]
|
||||
|
||||
is_torque_car = CP.lateralTuning.which() == "torque"
|
||||
if not is_torque_car:
|
||||
CarInterfaceBase.configure_torque_tune(MOCK.MOCK, CP.lateralTuning)
|
||||
|
||||
fpmsg_bytes = params.get("FrogPilotCarParams" if started else "FrogPilotCarParamsPersistent", block=started)
|
||||
if fpmsg_bytes:
|
||||
with custom.FrogPilotCarParams.from_bytes(fpmsg_bytes) as fpcp_reader:
|
||||
@@ -534,40 +560,37 @@ class FrogPilotVariables:
|
||||
CarInterface, _, _ = interfaces[MOCK.MOCK]
|
||||
FPCP = CarInterface.get_frogpilot_params(MOCK.MOCK, gen_empty_fingerprint(), [], CP, toggle)
|
||||
|
||||
is_torque_car = FPCP.lateralTuning.which() == "torque"
|
||||
if not is_torque_car:
|
||||
CarInterfaceBase.configure_torque_tune(MOCK.MOCK, FPCP.lateralTuning)
|
||||
|
||||
toggle.always_on_lateral_set = bool(CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL)
|
||||
toggle.car_make = CP.carName
|
||||
toggle.car_model = CP.carFingerprint
|
||||
toggle.disable_openpilot_long = params.get_bool("DisableOpenpilotLongitudinal") if tuning_level >= level["DisableOpenpilotLongitudinal"] else default.get_bool("DisableOpenpilotLongitudinal")
|
||||
friction = FPCP.lateralTuning.torque.friction
|
||||
has_auto_tune = toggle.car_make in {"hyundai", "toyota"} and FPCP.lateralTuning.which() == "torque"
|
||||
friction = CP.lateralTuning.torque.friction
|
||||
has_auto_tune = toggle.car_make in {"hyundai", "toyota"} and CP.lateralTuning.which() == "torque"
|
||||
has_bsm = CP.enableBsm
|
||||
toggle.has_cc_long = toggle.car_make == "gm" and bool(CP.flags & GMFlags.CC_LONG.value)
|
||||
has_nnff = nnff_supported(toggle.car_model)
|
||||
toggle.has_pedal = CP.enableGasInterceptor
|
||||
has_radar = not CP.radarUnavailable
|
||||
toggle.has_sdsu = toggle.car_make == "toyota" and bool(CP.flags & ToyotaFlags.SMART_DSU.value)
|
||||
toggle.has_sascm = toggle.car_make == "gm" and bool(CP.flags & GMFlags.SASCM.value)
|
||||
has_sng = CP.autoResumeSng
|
||||
toggle.has_zss = toggle.car_make == "toyota" and bool(FPCP.fpFlags & ToyotaFrogPilotFlags.ZSS.value)
|
||||
is_angle_car = CP.steerControlType == car.CarParams.SteerControlType.angle
|
||||
latAccelFactor = FPCP.lateralTuning.torque.latAccelFactor
|
||||
latAccelFactor = CP.lateralTuning.torque.latAccelFactor
|
||||
longitudinalActuatorDelay = CP.longitudinalActuatorDelay
|
||||
toggle.openpilot_longitudinal = CP.openpilotLongitudinalControl and not toggle.disable_openpilot_long
|
||||
pcm_cruise = CP.pcmCruise
|
||||
startAccel = CP.startAccel
|
||||
stopAccel = CP.stopAccel
|
||||
steerActuatorDelay = CP.steerActuatorDelay
|
||||
steerKp = FPCP.lateralTuning.torque.kp
|
||||
steerKp = CP.lateralTuning.torque.kp
|
||||
steerRatio = CP.steerRatio
|
||||
toggle.stoppingDecelRate = CP.stoppingDecelRate
|
||||
taco_hacks_allowed = CP.safetyConfigs[0].safetyModel == SafetyModel.hyundaiCanfd
|
||||
toggle.use_lkas_for_aol = not toggle.openpilot_longitudinal and CP.safetyConfigs[0].safetyModel == SafetyModel.hyundaiCanfd
|
||||
toggle.vEgoStarting = CP.vEgoStarting
|
||||
toggle.vEgoStopping = CP.vEgoStopping
|
||||
|
||||
msg_bytes = params.get("LiveTorqueParameters")
|
||||
if msg_bytes:
|
||||
with log.LiveTorqueParametersData.from_bytes(msg_bytes) as LTP:
|
||||
@@ -594,6 +617,7 @@ class FrogPilotVariables:
|
||||
toggle.steerActuatorDelay = np.clip(params.get_float("SteerDelay"), 0.01, 1.0) if advanced_lateral_tuning and tuning_level >= level["SteerDelay"] else steerActuatorDelay
|
||||
toggle.use_custom_steerActuatorDelay = bool(round(toggle.steerActuatorDelay, 2) != round(steerActuatorDelay, 2))
|
||||
toggle.friction = np.clip(params.get_float("SteerFriction"), 0, 0.5) if advanced_lateral_tuning and tuning_level >= level["SteerFriction"] else friction
|
||||
toggle.steer_offset = np.clip(params.get_float("SteerOffset"), -0.2, 0.2) if advanced_lateral_tuning and tuning_level >= level["SteerOffset"] and toggle.car_make == "gm" else 0.0
|
||||
toggle.use_custom_friction = bool(round(toggle.friction, 2) != round(friction, 2)) and is_torque_car and not toggle.force_auto_tune or toggle.force_auto_tune_off
|
||||
toggle.steerKp = [[0], [np.clip(params.get_float("SteerKP"), steerKp * 0.5, steerKp * 1.5) if advanced_lateral_tuning and is_torque_car and tuning_level >= level["SteerKP"] else steerKp]]
|
||||
toggle.latAccelFactor = np.clip(params.get_float("SteerLatAccel"), latAccelFactor * 0.75, latAccelFactor * 1.25) if advanced_lateral_tuning and tuning_level >= level["SteerLatAccel"] else latAccelFactor
|
||||
@@ -602,6 +626,13 @@ class FrogPilotVariables:
|
||||
toggle.use_custom_steerRatio = bool(round(toggle.steerRatio, 2) != round(steerRatio, 2)) and not toggle.force_auto_tune or toggle.force_auto_tune_off
|
||||
|
||||
advanced_longitudinal_tuning = params.get_bool("AdvancedLongitudinalTune") if tuning_level >= level["AdvancedLongitudinalTune"] else default.get_bool("AdvancedLongitudinalTune")
|
||||
ev_vehicle = toggle.car_make == "gm" and toggle.car_model != "CHEVROLET_VOLT" and CP.carFingerprint in GM_EV_CAR or toggle.car_make == "hyundai" and CP.carFingerprint in HYUNDAI_EV_CAR
|
||||
ev_vehicle |= CP.transmissionType == TransmissionType.direct
|
||||
|
||||
if params.get("EVTuning") == b"":
|
||||
params.put_bool("EVTuning", ev_vehicle)
|
||||
|
||||
toggle.ev_tuning = params.get_bool("EVTuning") if advanced_longitudinal_tuning and tuning_level >= level["EVTuning"] else ev_vehicle
|
||||
toggle.longitudinalActuatorDelay = np.clip(params.get_float("LongitudinalActuatorDelay"), 0, 1) if advanced_longitudinal_tuning and tuning_level >= level["LongitudinalActuatorDelay"] else longitudinalActuatorDelay
|
||||
toggle.startAccel = np.clip(params.get_float("StartAccel"), 0, 4) if advanced_longitudinal_tuning and tuning_level >= level["StartAccel"] else startAccel
|
||||
toggle.stopAccel = np.clip(params.get_float("StopAccel"), -4, 0) if advanced_longitudinal_tuning and tuning_level >= level["StopAccel"] else stopAccel
|
||||
@@ -609,6 +640,10 @@ class FrogPilotVariables:
|
||||
toggle.vEgoStarting = np.clip(params.get_float("VEgoStarting"), 0.01, 1) if advanced_longitudinal_tuning and tuning_level >= level["VEgoStarting"] else toggle.vEgoStarting
|
||||
toggle.vEgoStopping = np.clip(params.get_float("VEgoStopping"), 0.01, 1) if advanced_longitudinal_tuning and tuning_level >= level["VEgoStopping"] else toggle.vEgoStopping
|
||||
|
||||
toggle.stop_distance = params.get_float("StopDistance") if advanced_longitudinal_tuning and tuning_level >= level["StopDistance"] else 6.0
|
||||
|
||||
toggle.recovery_power = np.clip(params.get_float("RecoveryPower"), 0.5, 2.0) if advanced_longitudinal_tuning and tuning_level >= level["RecoveryPower"] else 1.0
|
||||
|
||||
toggle.alert_volume_controller = params.get_bool("AlertVolumeControl") if tuning_level >= level["AlertVolumeControl"] else default.get_bool("AlertVolumeControl")
|
||||
toggle.disengage_volume = params.get_int("DisengageVolume") if toggle.alert_volume_controller and tuning_level >= level["DisengageVolume"] else default.get_int("DisengageVolume")
|
||||
toggle.engage_volume = params.get_int("EngageVolume") if toggle.alert_volume_controller and tuning_level >= level["EngageVolume"] else default.get_int("EngageVolume")
|
||||
@@ -624,7 +659,7 @@ class FrogPilotVariables:
|
||||
toggle.always_on_lateral_main = toggle.always_on_lateral_set and not toggle.use_lkas_for_aol and (params.get_bool("AlwaysOnLateralMain") if tuning_level >= level["AlwaysOnLateralMain"] else default.get_bool("AlwaysOnLateralMain"))
|
||||
toggle.always_on_lateral_pause_speed = params.get_int("PauseAOLOnBrake") if toggle.always_on_lateral_set and tuning_level >= level["PauseAOLOnBrake"] else default.get_int("PauseAOLOnBrake")
|
||||
|
||||
toggle.automatic_updates = (params.get_bool("AutomaticUpdates") if tuning_level >= level["AutomaticUpdates"] and (self.release_branch or self.vetting_branch) else default.get_bool("AutomaticUpdates")) and not BACKUP_PATH.is_file()
|
||||
toggle.automatic_updates = (params.get_bool("AutomaticUpdates") if tuning_level >= level["AutomaticUpdates"] else default.get_bool("AutomaticUpdates")) and not BACKUP_PATH.is_file()
|
||||
|
||||
toggle.car_model = params.get("CarModel", encoding="utf-8") or toggle.car_model
|
||||
|
||||
@@ -664,28 +699,31 @@ class FrogPilotVariables:
|
||||
toggle.aggressive_jerk_danger = np.clip(params.get_int("AggressiveJerkDanger") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkDanger"] else default.get_int("AggressiveJerkDanger") / 100
|
||||
toggle.aggressive_jerk_speed = np.clip(params.get_int("AggressiveJerkSpeed") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkSpeed"] else default.get_int("AggressiveJerkSpeed") / 100
|
||||
toggle.aggressive_jerk_speed_decrease = np.clip(params.get_int("AggressiveJerkSpeedDecrease") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkSpeedDecrease"] else default.get_int("AggressiveJerkSpeedDecrease") / 100
|
||||
toggle.aggressive_follow = np.clip(params.get_float("AggressiveFollow"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollow"] else default.get_float("AggressiveFollow")
|
||||
toggle.aggressive_follow = [np.clip(params.get_float("AggressiveFollow"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollow"] else default.get_float("AggressiveFollow"),
|
||||
np.clip(params.get_float("AggressiveFollowHigh"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollowHigh"] else default.get_float("AggressiveFollowHigh")]
|
||||
standard_profile = toggle.custom_personalities and (params.get_bool("StandardPersonalityProfile") if tuning_level >= level["StandardPersonalityProfile"] else default.get_bool("StandardPersonalityProfile"))
|
||||
toggle.standard_jerk_acceleration = np.clip(params.get_int("StandardJerkAcceleration") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkAcceleration"] else default.get_int("StandardJerkAcceleration") / 100
|
||||
toggle.standard_jerk_deceleration = np.clip(params.get_int("StandardJerkDeceleration") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkDeceleration"] else default.get_int("StandardJerkDeceleration") / 100
|
||||
toggle.standard_jerk_danger = np.clip(params.get_int("StandardJerkDanger") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkDanger"] else default.get_int("StandardJerkDanger") / 100
|
||||
toggle.standard_jerk_speed = np.clip(params.get_int("StandardJerkSpeed") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkSpeed"] else default.get_int("StandardJerkSpeed") / 100
|
||||
toggle.standard_jerk_speed_decrease = np.clip(params.get_int("StandardJerkSpeedDecrease") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkSpeedDecrease"] else default.get_int("StandardJerkSpeedDecrease") / 100
|
||||
toggle.standard_follow = np.clip(params.get_float("StandardFollow"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollow"] else default.get_float("StandardFollow")
|
||||
toggle.standard_follow = [np.clip(params.get_float("StandardFollow"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollow"] else default.get_float("StandardFollow"),
|
||||
np.clip(params.get_float("StandardFollowHigh"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollowHigh"] else default.get_float("StandardFollowHigh")]
|
||||
relaxed_profile = toggle.custom_personalities and (params.get_bool("RelaxedPersonalityProfile") if tuning_level >= level["RelaxedPersonalityProfile"] else default.get_bool("RelaxedPersonalityProfile"))
|
||||
toggle.relaxed_jerk_acceleration = np.clip(params.get_int("RelaxedJerkAcceleration") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkAcceleration"] else default.get_int("RelaxedJerkAcceleration") / 100
|
||||
toggle.relaxed_jerk_deceleration = np.clip(params.get_int("RelaxedJerkDeceleration") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkDeceleration"] else default.get_int("RelaxedJerkDeceleration") / 100
|
||||
toggle.relaxed_jerk_danger = np.clip(params.get_int("RelaxedJerkDanger") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkDanger"] else default.get_int("RelaxedJerkDanger") / 100
|
||||
toggle.relaxed_jerk_speed = np.clip(params.get_int("RelaxedJerkSpeed") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkSpeed"] else default.get_int("RelaxedJerkSpeed") / 100
|
||||
toggle.relaxed_jerk_speed_decrease = np.clip(params.get_int("RelaxedJerkSpeedDecrease") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkSpeedDecrease"] else default.get_int("RelaxedJerkSpeedDecrease") / 100
|
||||
toggle.relaxed_follow = np.clip(params.get_float("RelaxedFollow"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollow"] else default.get_float("RelaxedFollow")
|
||||
toggle.relaxed_follow = [np.clip(params.get_float("RelaxedFollow"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollow"] else default.get_float("RelaxedFollow"),
|
||||
np.clip(params.get_float("RelaxedFollowHigh"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollowHigh"] else default.get_float("RelaxedFollowHigh")]
|
||||
traffic_profile = toggle.custom_personalities and (params.get_bool("TrafficPersonalityProfile") if tuning_level >= level["TrafficPersonalityProfile"] else default.get_bool("TrafficPersonalityProfile"))
|
||||
toggle.traffic_mode_jerk_acceleration = [np.clip(params.get_int("TrafficJerkAcceleration") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkAcceleration"] else default.get_int("TrafficJerkAcceleration") / 100, toggle.aggressive_jerk_acceleration]
|
||||
toggle.traffic_mode_jerk_deceleration = [np.clip(params.get_int("TrafficJerkDeceleration") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkDeceleration"] else default.get_int("TrafficJerkDeceleration") / 100, toggle.aggressive_jerk_deceleration]
|
||||
toggle.traffic_mode_jerk_danger = [np.clip(params.get_int("TrafficJerkDanger") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkDanger"] else default.get_int("TrafficJerkDanger") / 100, toggle.aggressive_jerk_danger]
|
||||
toggle.traffic_mode_jerk_speed = [np.clip(params.get_int("TrafficJerkSpeed") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkSpeed"] else default.get_int("TrafficJerkSpeed") / 100, toggle.aggressive_jerk_speed]
|
||||
toggle.traffic_mode_jerk_speed_decrease = [np.clip(params.get_int("TrafficJerkSpeedDecrease") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkSpeedDecrease"] else default.get_int("TrafficJerkSpeedDecrease") / 100, toggle.aggressive_jerk_speed_decrease]
|
||||
toggle.traffic_mode_follow = [np.clip(params.get_float("TrafficFollow"), 0.5, MAX_T_FOLLOW) if traffic_profile and tuning_level >= level["TrafficFollow"] else default.get_float("TrafficFollow"), toggle.aggressive_follow]
|
||||
toggle.traffic_mode_follow = [np.clip(params.get_float("TrafficFollow"), 0.5, MAX_T_FOLLOW) if traffic_profile and tuning_level >= level["TrafficFollow"] else default.get_float("TrafficFollow"), toggle.aggressive_follow[0]]
|
||||
|
||||
custom_ui = params.get_bool("CustomUI") if tuning_level >= level["CustomUI"] else default.get_bool("CustomUI")
|
||||
toggle.acceleration_path = toggle.openpilot_longitudinal and (custom_ui and (params.get_bool("AccelerationPath") if tuning_level >= level["AccelerationPath"] else default.get_bool("AccelerationPath")) or toggle.debug_mode)
|
||||
@@ -814,33 +852,49 @@ class FrogPilotVariables:
|
||||
toggle.human_following = longitudinal_tuning and (params.get_bool("HumanFollowing") if tuning_level >= level["HumanFollowing"] else default.get_bool("HumanFollowing"))
|
||||
toggle.lead_detection_probability = np.clip(params.get_int("LeadDetectionThreshold") / 100, 0.25, 0.50) if longitudinal_tuning and tuning_level >= level["LeadDetectionThreshold"] else default.get_int("LeadDetectionThreshold") / 100
|
||||
toggle.max_desired_acceleration = np.clip(params.get_float("MaxDesiredAcceleration"), 0.1, 4.0) if longitudinal_tuning and tuning_level >= level["MaxDesiredAcceleration"] else default.get_float("MaxDesiredAcceleration")
|
||||
toggle.trailer_load_kg = (np.clip(params.get_int("TrailerLoad"), 0, 15000) if longitudinal_tuning and tuning_level >= level["TrailerLoad"] else default.get_int("TrailerLoad")) * CV.LB_TO_KG
|
||||
toggle.taco_tune = longitudinal_tuning and (params.get_bool("TacoTune") if tuning_level >= level["TacoTune"] else default.get_bool("TacoTune"))
|
||||
|
||||
toggle.available_models = (params.get("AvailableModels", encoding="utf-8") or "") + f",{DEFAULT_MODEL}"
|
||||
toggle.available_model_names = (params.get("AvailableModelNames", encoding="utf-8") or "") + f",{DEFAULT_MODEL_NAME}"
|
||||
downloaded_models = [model for model in toggle.available_models.split(",") if (MODELS_PATH / f"{model}.thneed").is_file() or all((MODELS_PATH / f"{model}_{filename}").is_file() for filename, _ in TINYGRAD_FILES)]
|
||||
model_versions = (params.get("ModelVersions", encoding="utf-8") or "") + f",{DEFAULT_MODEL_VERSION}"
|
||||
toggle.model_randomizer = params.get_bool("ModelRandomizer") if tuning_level >= level["ModelRandomizer"] else default.get_bool("ModelRandomizer")
|
||||
if toggle.model_randomizer:
|
||||
if not started:
|
||||
blacklisted_models = (params.get("BlacklistedModels", encoding="utf-8") or "").split(",")
|
||||
selectable_models = [model for model in downloaded_models if model not in blacklisted_models]
|
||||
toggle.model = random.choice(selectable_models) if selectable_models else DEFAULT_MODEL
|
||||
toggle.model_name = "Mystery Model 👻"
|
||||
toggle.model_version = model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)]
|
||||
else:
|
||||
model = ((params.get("Model", encoding="utf-8") if tuning_level >= level["Model"] else default.get("Model", encoding="utf-8")) or DEFAULT_MODEL).removesuffix("_default")
|
||||
if model in downloaded_models:
|
||||
toggle.model = model
|
||||
toggle.model_name = dict(zip(toggle.available_models.split(","), toggle.available_model_names.split(",")))[toggle.model]
|
||||
toggle.model_version = dict(zip(toggle.available_models.split(","), model_versions.split(",")))[toggle.model]
|
||||
toggle.available_models = params.get("AvailableModels", encoding="utf-8") or ""
|
||||
toggle.available_model_names = params.get("AvailableModelNames", encoding="utf-8") or ""
|
||||
toggle.available_model_series = params.get("AvailableModelSeries", encoding="utf-8") or ""
|
||||
toggle.community_favorites = params.get("CommunityFavorites", encoding="utf-8") or ""
|
||||
toggle.model_released_dates = params.get("ModelReleasedDates", encoding="utf-8") or ""
|
||||
toggle.model_versions = params.get("ModelVersions", encoding="utf-8") or ""
|
||||
toggle.sort_models_by_date = params.get_bool("SortModelsByDate") if tuning_level >= level["SortModelsByDate"] else default.get_bool("SortModelsByDate")
|
||||
toggle.user_favorites = params.get("UserFavorites", encoding="utf-8") or ""
|
||||
downloaded_models = [model for model in toggle.available_models.split(",") if any(MODELS_PATH.glob(f"{model}*"))]
|
||||
toggle.model_randomizer = downloaded_models and (params.get_bool("ModelRandomizer") if tuning_level >= level["ModelRandomizer"] else default.get_bool("ModelRandomizer"))
|
||||
if toggle.available_models and toggle.available_model_names and downloaded_models and toggle.model_versions:
|
||||
if DEFAULT_MODEL not in toggle.available_models.split(","):
|
||||
toggle.available_models += f",{DEFAULT_MODEL}"
|
||||
toggle.available_model_names += f",{DEFAULT_MODEL_NAME}"
|
||||
toggle.model_versions += f",{DEFAULT_MODEL_VERSION}"
|
||||
downloaded_models += [DEFAULT_MODEL]
|
||||
if toggle.model_randomizer:
|
||||
if not started:
|
||||
blacklisted_models = (params.get("BlacklistedModels", encoding="utf-8") or "").split(",")
|
||||
selectable_models = [model for model in downloaded_models if model not in blacklisted_models]
|
||||
toggle.model = random.choice(selectable_models) if selectable_models else default.get("Model", encoding="utf-8")
|
||||
toggle.model_name = "Mystery Model 👻"
|
||||
toggle.model_version = toggle.model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)]
|
||||
else:
|
||||
toggle.model = params.get("Model", encoding="utf-8") if tuning_level >= level["Model"] else default.get("Model", encoding="utf-8")
|
||||
if toggle.model in downloaded_models:
|
||||
toggle.model_name = toggle.available_model_names.split(",")[toggle.available_models.split(",").index(toggle.model)]
|
||||
toggle.model_version = toggle.model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)]
|
||||
else:
|
||||
toggle.model = default.get("Model", encoding="utf-8")
|
||||
toggle.model_name = toggle.available_model_names.split(",")[toggle.available_models.split(",").index(toggle.model)]
|
||||
toggle.model_version = toggle.model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)]
|
||||
else:
|
||||
toggle.model = DEFAULT_MODEL
|
||||
toggle.model_name = DEFAULT_MODEL_NAME
|
||||
toggle.model_version = DEFAULT_MODEL_VERSION
|
||||
toggle.classic_longitudinal = toggle.model_version in {"v1", "v2", "v3", "v4"}
|
||||
toggle.classic_model = toggle.model_version in {"v1", "v2", "v3", "v4"}
|
||||
toggle.classic_longitudinal = toggle.model_version in {"v1", "v2", "v3", "v4", "v5", "v6"}
|
||||
toggle.tinygrad_model = not toggle.classic_model and toggle.model_version not in {"v5", "v6"}
|
||||
toggle.tinygrad_model = toggle.model_version in {"v8", "v9", "v10", "v11", "v12"}
|
||||
toggle.tomb_raider = toggle.model == "space-lab"
|
||||
|
||||
toggle.model_ui = params.get_bool("ModelUI") if tuning_level >= level["ModelUI"] else default.get_bool("ModelUI")
|
||||
toggle.dynamic_path_width = toggle.model_ui and (params.get_bool("DynamicPathWidth") if tuning_level >= level["DynamicPathWidth"] else default.get_bool("DynamicPathWidth"))
|
||||
@@ -906,7 +960,6 @@ class FrogPilotVariables:
|
||||
toggle.screen_timeout = params.get_int("ScreenTimeout") if screen_management and tuning_level >= level["ScreenTimeout"] else default.get_int("ScreenTimeout")
|
||||
toggle.screen_timeout_onroad = params.get_int("ScreenTimeoutOnroad") if screen_management and tuning_level >= level["ScreenTimeoutOnroad"] else default.get_int("ScreenTimeoutOnroad")
|
||||
toggle.standby_mode = screen_management and (params.get_bool("StandbyMode") if tuning_level >= level["StandbyMode"] else default.get_bool("StandbyMode"))
|
||||
|
||||
toggle.sng_hack = toggle.openpilot_longitudinal and toggle.car_make == "toyota" and not toggle.has_pedal and not has_sng and (params.get_bool("SNGHack") if tuning_level >= level["SNGHack"] else default.get_bool("SNGHack"))
|
||||
|
||||
toggle.speed_limit_controller = toggle.openpilot_longitudinal and (params.get_bool("SpeedLimitController") if tuning_level >= level["SpeedLimitController"] else default.get_bool("SpeedLimitController"))
|
||||
@@ -955,5 +1008,7 @@ class FrogPilotVariables:
|
||||
|
||||
toggle.volt_sng = toggle.car_model == "CHEVROLET_VOLT" and (params.get_bool("VoltSNG") if tuning_level >= level["VoltSNG"] else default.get_bool("VoltSNG"))
|
||||
|
||||
toggle.gm_pedal_longitudinal = params.get_bool("GMPedalLongitudinal") if tuning_level >= level["GMPedalLongitudinal"] else default.get_bool("GMPedalLongitudinal")
|
||||
|
||||
params_memory.put("FrogPilotToggles", json.dumps(toggle.__dict__))
|
||||
params_memory.remove("FrogPilotTogglesUpdated")
|
||||
|
||||
@@ -9,7 +9,7 @@ from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.realtime import DT_MDL
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import A_CHANGE_COST, DANGER_ZONE_COST, J_EGO_COST, STOP_DISTANCE
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import A_CHANGE_COST, COMFORT_BRAKE, DANGER_ZONE_COST, J_EGO_COST
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import calculate_lane_width, calculate_road_curvature
|
||||
from openpilot.frogpilot.common.frogpilot_variables import CRUISING_SPEED, MINIMUM_LATERAL_ACCELERATION, PLANNER_TIME, THRESHOLD, params, params_memory
|
||||
@@ -65,6 +65,8 @@ class FrogPilotPlanner:
|
||||
self.cem.curve_detected = False
|
||||
self.cem.stop_sign_and_light(v_ego, sm, PLANNER_TIME - 2)
|
||||
|
||||
|
||||
|
||||
self.driving_in_curve = abs(self.lateral_acceleration) >= MINIMUM_LATERAL_ACCELERATION
|
||||
|
||||
self.frogpilot_events.update(v_cruise, sm, frogpilot_toggles)
|
||||
@@ -85,7 +87,7 @@ class FrogPilotPlanner:
|
||||
|
||||
params_memory.remove("LastGPSPosition")
|
||||
|
||||
self.lateral_acceleration = v_ego**2 * (sm["carState"].steeringAngleDeg - sm["liveParameters"].angleOffsetDeg) * CV.DEG_TO_RAD / (self.CP.steerRatio * self.CP.wheelbase)
|
||||
self.lateral_acceleration = v_ego**2 * sm["controlsState"].curvature
|
||||
|
||||
check_lane_width = frogpilot_toggles.adjacent_paths or frogpilot_toggles.adjacent_path_metrics or frogpilot_toggles.blind_spot_path or frogpilot_toggles.lane_detection
|
||||
if check_lane_width and v_ego >= frogpilot_toggles.minimum_lane_change_speed:
|
||||
@@ -115,7 +117,9 @@ class FrogPilotPlanner:
|
||||
|
||||
def update_lead_status(self):
|
||||
following_lead = self.lead_one.status
|
||||
following_lead &= self.lead_one.dRel < self.model_length + STOP_DISTANCE
|
||||
from frogpilot.common.frogpilot_variables import get_frogpilot_toggles
|
||||
fp_toggles = get_frogpilot_toggles()
|
||||
following_lead &= self.lead_one.dRel < self.model_length + fp_toggles.stop_distance
|
||||
|
||||
self.tracking_lead_filter.update(following_lead)
|
||||
return self.tracking_lead_filter.x >= THRESHOLD
|
||||
@@ -138,6 +142,8 @@ class FrogPilotPlanner:
|
||||
|
||||
frogpilotPlan.desiredFollowDistance = self.frogpilot_following.desired_follow_distance
|
||||
|
||||
frogpilotPlan.disableThrottle = self.frogpilot_following.disable_throttle
|
||||
|
||||
frogpilotPlan.experimentalMode = self.cem.experimental_mode or self.frogpilot_vcruise.slc.experimental_mode
|
||||
|
||||
frogpilotPlan.forcingStop = self.frogpilot_vcruise.forcing_stop
|
||||
|
||||
@@ -1,20 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.realtime import DT_MDL
|
||||
from openpilot.common.numpy_fast import interp
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED, THRESHOLD, params_memory
|
||||
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED, THRESHOLD, params_memory, scale_threshold
|
||||
|
||||
class ConditionalExperimentalMode:
|
||||
# ===== CONDITIONAL EXPERIMENTAL MODE SPEED-BASED TUNING =====
|
||||
# Speed ranges: [0-35, 35-55, 55-70, 70+ mph]
|
||||
|
||||
# FILTER TIME CONSTANTS (Lower = More responsive, Higher = Smoother)
|
||||
# [City, Urban Hwy, Rural Hwy, High Speed]
|
||||
FILTER_TIME_CURVES = [0.9, 0.8, 0.6, 0.5] # Faster detection at highway speeds
|
||||
FILTER_TIME_LEADS = [0.9, 0.8, 0.7, 0.5] # Less sensitive at 70+ mph for slow leads
|
||||
FILTER_TIME_LIGHTS = [0.9, 0.8, 0.75, 0.55] # Less sensitive at 60+ mph for stoplights
|
||||
|
||||
# HIGHWAY LIGHT DETECTION MULTIPLIERS
|
||||
# How much to increase model stop time at highway speeds
|
||||
LIGHT_BOOSTS = [1.0, 1.2, 1.1, 1.0] # Keep conservative boost for highest speeds
|
||||
LIGHT_SPEED_LOW = 50 * CV.MPH_TO_MS # 50 mph threshold
|
||||
LIGHT_SPEED_HIGH = 60 * CV.MPH_TO_MS # 60 mph threshold
|
||||
LIGHT_MAX_TIME = 9 # Balanced max time preserving city performance
|
||||
|
||||
# ===== END TUNING PARAMETERS =====
|
||||
|
||||
# Current active values
|
||||
FILTER_TIME_CURVE = 0.8
|
||||
FILTER_TIME_LEAD = 0.8
|
||||
FILTER_TIME_LIGHT = 0.8
|
||||
LIGHT_BOOST_LOW = 1.15
|
||||
LIGHT_BOOST_HIGH = 1.2
|
||||
|
||||
@staticmethod
|
||||
def get_speed_based_param(speed_mph, param_array):
|
||||
"""Get parameter value based on current speed using smooth interpolation between breakpoints [0, 35, 55, 70]"""
|
||||
return interp(speed_mph, [0, 35, 55, 70], param_array)
|
||||
|
||||
def __init__(self, FrogPilotPlanner):
|
||||
self.frogpilot_planner = FrogPilotPlanner
|
||||
|
||||
self.curvature_filter = FirstOrderFilter(0, 1, DT_MDL)
|
||||
self.slow_lead_filter = FirstOrderFilter(0, 1, DT_MDL)
|
||||
self.stop_light_filter = FirstOrderFilter(0, 0.5, DT_MDL)
|
||||
# Faster filters with hysteresis for better responsiveness
|
||||
self.curvature_filter = FirstOrderFilter(0, self.FILTER_TIME_CURVE, DT_MDL)
|
||||
self.slow_lead_filter = FirstOrderFilter(0, self.FILTER_TIME_LEAD, DT_MDL)
|
||||
self.stop_light_filter = FirstOrderFilter(0, self.FILTER_TIME_LIGHT, DT_MDL)
|
||||
|
||||
self.curve_detected = False
|
||||
self.experimental_mode = False
|
||||
self.stop_light_detected = False
|
||||
self.prev_experimental_mode = False # For hysteresis
|
||||
|
||||
def update(self, v_ego, sm, frogpilot_toggles):
|
||||
if frogpilot_toggles.experimental_mode_via_press:
|
||||
@@ -24,9 +58,26 @@ class ConditionalExperimentalMode:
|
||||
|
||||
if self.status_value not in {1, 2} and not sm["carState"].standstill:
|
||||
self.update_conditions(v_ego, sm, frogpilot_toggles)
|
||||
new_experimental_mode = self.check_conditions(v_ego, sm, frogpilot_toggles)
|
||||
|
||||
# Add hysteresis to prevent rapid toggling
|
||||
if new_experimental_mode and not self.prev_experimental_mode:
|
||||
# Require weaker conditions to turn on
|
||||
hysteresis_factor = 0.9
|
||||
elif not new_experimental_mode and self.prev_experimental_mode:
|
||||
# Require stronger conditions to turn off
|
||||
hysteresis_factor = 1.2
|
||||
else:
|
||||
hysteresis_factor = 1.0
|
||||
|
||||
# Apply hysteresis to key conditions
|
||||
if hasattr(self, 'slow_lead_detected'):
|
||||
self.slow_lead_detected = self.slow_lead_detected if hysteresis_factor == 1.0 else (self.slow_lead_filter.x >= scale_threshold(v_ego) * hysteresis_factor)
|
||||
if hasattr(self, 'curve_detected'):
|
||||
self.curve_detected = self.curve_detected if hysteresis_factor == 1.0 else (self.curvature_filter.x >= THRESHOLD * hysteresis_factor)
|
||||
|
||||
self.experimental_mode = self.check_conditions(v_ego, sm, frogpilot_toggles)
|
||||
|
||||
self.prev_experimental_mode = self.experimental_mode
|
||||
params_memory.put_int("CEStatus", self.status_value if self.experimental_mode else 0)
|
||||
else:
|
||||
self.experimental_mode = self.status_value == 2 or sm["carState"].standstill and self.experimental_mode and self.frogpilot_planner.model_stopped
|
||||
@@ -55,7 +106,7 @@ class ConditionalExperimentalMode:
|
||||
self.status_value = 8
|
||||
return True
|
||||
|
||||
if frogpilot_toggles.conditional_lead and self.slow_lead_detected:
|
||||
if frogpilot_toggles.conditional_lead and self.slow_lead_detected and v_ego <= 35.31:
|
||||
self.status_value = 9 if self.frogpilot_planner.lead_one.vLead < 1 else 10
|
||||
return True
|
||||
|
||||
@@ -71,30 +122,70 @@ class ConditionalExperimentalMode:
|
||||
|
||||
def update_conditions(self, v_ego, sm, frogpilot_toggles):
|
||||
self.curve_detection(v_ego, frogpilot_toggles)
|
||||
self.slow_lead(frogpilot_toggles)
|
||||
self.slow_lead(frogpilot_toggles, v_ego)
|
||||
self.stop_sign_and_light(v_ego, sm, frogpilot_toggles.conditional_model_stop_time)
|
||||
|
||||
def curve_detection(self, v_ego, frogpilot_toggles):
|
||||
self.curvature_filter.update(self.frogpilot_planner.road_curvature_detected or self.frogpilot_planner.driving_in_curve)
|
||||
self.curve_detected = self.curvature_filter.x >= THRESHOLD and v_ego > CRUISING_SPEED
|
||||
|
||||
def slow_lead(self, frogpilot_toggles):
|
||||
def slow_lead(self, frogpilot_toggles, v_ego):
|
||||
if self.frogpilot_planner.tracking_lead:
|
||||
slower_lead = frogpilot_toggles.conditional_slower_lead and self.frogpilot_planner.frogpilot_following.slower_lead
|
||||
stopped_lead = frogpilot_toggles.conditional_stopped_lead and self.frogpilot_planner.lead_one.vLead < 1
|
||||
lead_threshold = scale_threshold(v_ego)
|
||||
|
||||
# Adjust threshold based on lead probability for vision-only accuracy
|
||||
lead_prob = getattr(self.frogpilot_planner.lead_one, 'modelProb', 1.0)
|
||||
adjusted_threshold = lead_threshold * (1.0 + 0.2 * (1.0 - lead_prob)) # Higher threshold for lower confidence
|
||||
|
||||
self.slow_lead_filter.update(slower_lead or stopped_lead)
|
||||
self.slow_lead_detected = self.slow_lead_filter.x >= THRESHOLD
|
||||
self.slow_lead_detected = self.slow_lead_filter.x >= adjusted_threshold
|
||||
else:
|
||||
self.slow_lead_filter.x = 0
|
||||
self.slow_lead_detected = False
|
||||
|
||||
def stop_sign_and_light(self, v_ego, sm, model_time):
|
||||
if not sm["frogpilotCarState"].trafficModeEnabled:
|
||||
model_stopping = self.frogpilot_planner.model_length < v_ego * model_time
|
||||
speed_mph = v_ego * CV.MS_TO_MPH # Convert m/s to mph
|
||||
|
||||
# Interp for smooth scaling in 35-45 mph
|
||||
bp = [0, 35, 45]
|
||||
low_filter_time = 0.0 # No filtering under 35 mph
|
||||
tuned_filter_time_curves = self.FILTER_TIME_CURVES[1] # At 35-55 mph
|
||||
tuned_filter_time_leads = self.FILTER_TIME_LEADS[1]
|
||||
tuned_filter_time_lights = self.FILTER_TIME_LIGHTS[1]
|
||||
low_boost = 1.0
|
||||
tuned_boost = self.LIGHT_BOOSTS[1]
|
||||
low_cap_factor = 0.0 # No cap under 35 mph
|
||||
tuned_cap_factor = 1.0
|
||||
|
||||
filter_time_curves = interp(speed_mph, bp, [low_filter_time, low_filter_time, tuned_filter_time_curves])
|
||||
filter_time_leads = interp(speed_mph, bp, [low_filter_time, low_filter_time, tuned_filter_time_leads])
|
||||
filter_time_lights = interp(speed_mph, bp, [low_filter_time, low_filter_time, tuned_filter_time_lights])
|
||||
light_boost = interp(speed_mph, bp, [low_boost, low_boost, tuned_boost])
|
||||
cap_factor = interp(speed_mph, bp, [low_cap_factor, low_cap_factor, tuned_cap_factor])
|
||||
|
||||
# Update filter times with interp
|
||||
self.curvature_filter = FirstOrderFilter(self.curvature_filter.x, filter_time_curves, DT_MDL)
|
||||
self.slow_lead_filter = FirstOrderFilter(self.slow_lead_filter.x, filter_time_leads, DT_MDL)
|
||||
self.stop_light_filter = FirstOrderFilter(self.stop_light_filter.x, filter_time_lights, DT_MDL)
|
||||
|
||||
# Disable stoplight detection at very high speeds to prevent false positives
|
||||
if speed_mph > 75: # Disable above 75 mph
|
||||
self.stop_light_filter.x = 0
|
||||
self.stop_light_detected = False
|
||||
return
|
||||
|
||||
# Adjust model time with interp boost and gradual cap
|
||||
adjusted_model_time = model_time * light_boost
|
||||
if cap_factor > 0:
|
||||
adjusted_model_time = min(adjusted_model_time, self.LIGHT_MAX_TIME * cap_factor + model_time * (1 - cap_factor)) # Gradual cap
|
||||
|
||||
model_stopping = self.frogpilot_planner.model_length < v_ego * adjusted_model_time
|
||||
|
||||
self.stop_light_filter.update(self.frogpilot_planner.model_stopped or model_stopping)
|
||||
self.stop_light_detected = self.stop_light_filter.x >= THRESHOLD and not self.frogpilot_planner.tracking_lead
|
||||
self.stop_light_detected = self.stop_light_filter.x >= THRESHOLD**2 and not self.frogpilot_planner.tracking_lead
|
||||
else:
|
||||
self.stop_light_filter.x = 0
|
||||
self.stop_light_detected = False
|
||||
|
||||
@@ -1,33 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
import numpy as np
|
||||
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import CRUISE_MIN_ACCEL
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_planner import ACCEL_MIN, get_max_accel
|
||||
def cubic_interp(x, xp, fp):
|
||||
"""Cubic interpolation using NumPy's native operations for speed."""
|
||||
# Boundary conditions
|
||||
if x <= xp[0]:
|
||||
return fp[0]
|
||||
elif x >= xp[-1]:
|
||||
return fp[-1]
|
||||
|
||||
# Find interval
|
||||
i = np.searchsorted(xp, x) - 1
|
||||
i = max(0, min(i, len(xp)-2)) # clamp the index
|
||||
|
||||
# Normalized position
|
||||
t = (x - xp[i]) / float(xp[i+1] - xp[i])
|
||||
|
||||
# Hermite cubic formula
|
||||
return fp[i]*(1 - 3*t**2 + 2*t**3) + fp[i+1]*(3*t**2 - 2*t**3)
|
||||
|
||||
def akima_interp(x, xp, fp):
|
||||
"""Akima-inspired interpolation with reduced overshoot characteristics."""
|
||||
if x <= xp[0]:
|
||||
return fp[0]
|
||||
elif x >= xp[-1]:
|
||||
return fp[-1]
|
||||
|
||||
i = np.searchsorted(xp, x) - 1
|
||||
i = max(0, min(i, len(xp)-2)) # clamp the index
|
||||
|
||||
t = (x - xp[i]) / float(xp[i+1] - xp[i])
|
||||
|
||||
# Quintic polynomial to reduce overshoot
|
||||
t2 = t*t
|
||||
t4 = t2*t2
|
||||
t3 = t2*t
|
||||
return (fp[i]*(1 - 10*t3 + 15*t4 - 6*t3*t2)
|
||||
+ fp[i+1]*(10*t3 - 15*t4 + 6*t3*t2))
|
||||
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, get_max_accel
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT
|
||||
|
||||
A_CRUISE_MIN_ECO = CRUISE_MIN_ACCEL / 2
|
||||
A_CRUISE_MIN_SPORT = CRUISE_MIN_ACCEL * 2
|
||||
A_CRUISE_MIN_ECO = A_CRUISE_MIN / 2
|
||||
A_CRUISE_MIN_SPORT = A_CRUISE_MIN * 2
|
||||
|
||||
# MPH = [0.0, 11, 22, 34, 45, 56, 89]
|
||||
A_CRUISE_MAX_BP_CUSTOM = [0.0, 5., 10., 15., 20., 25., 40.]
|
||||
A_CRUISE_MAX_VALS_ECO = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2]
|
||||
A_CRUISE_MAX_VALS_SPORT = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6]
|
||||
# MPH = [0.0, 11, 22, 34, 45, 56, 89]
|
||||
A_CRUISE_MAX_BP_CUSTOM = [0.0, 5., 10., 15., 20., 25., 40.]
|
||||
A_CRUISE_MAX_VALS_ECO_EV = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
|
||||
A_CRUISE_MAX_VALS_SPORT_EV = [1.25, 1.25, 1.25, 1.25, 1.5, 1.5, 2.0]
|
||||
A_CRUISE_MAX_VALS_ECO_GAS = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2]
|
||||
A_CRUISE_MAX_VALS_SPORT_GAS = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6]
|
||||
|
||||
def get_max_accel_eco(v_ego):
|
||||
return float(np.interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_ECO))
|
||||
def get_max_accel_eco(v_ego, ev_tuning=True):
|
||||
cruise_vals = A_CRUISE_MAX_VALS_ECO_EV if ev_tuning else A_CRUISE_MAX_VALS_ECO_GAS
|
||||
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals))
|
||||
|
||||
def get_max_accel_sport(v_ego):
|
||||
return float(np.interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT))
|
||||
def get_max_accel_sport(v_ego, ev_tuning=True):
|
||||
cruise_vals = A_CRUISE_MAX_VALS_SPORT_EV if ev_tuning else A_CRUISE_MAX_VALS_SPORT_GAS
|
||||
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals))
|
||||
|
||||
def get_max_accel_low_speeds(max_accel, v_cruise):
|
||||
return float(np.interp(v_cruise, [0., CITY_SPEED_LIMIT / 2, CITY_SPEED_LIMIT], [max_accel / 4, max_accel / 2, max_accel]))
|
||||
return float(akima_interp(v_cruise, [0., CITY_SPEED_LIMIT / 2, CITY_SPEED_LIMIT], [max_accel / 4, max_accel / 2, max_accel]))
|
||||
|
||||
def get_max_accel_ramp_off(max_accel, v_cruise, v_ego):
|
||||
return float(np.interp(v_cruise - v_ego, [0., 1., 5.], [0., 0.5, max_accel]))
|
||||
return float(akima_interp(v_cruise - v_ego, [0., 1., 5., 10.], [0., 0.5, 1.0, max_accel]))
|
||||
|
||||
def get_max_allowed_accel(v_ego):
|
||||
return float(np.interp(v_ego, [0., 5., 20.], [4.0, 4.0, 2.0])) # ISO 15622:2018
|
||||
return float(akima_interp(v_ego, [0., 5., 20.], [4.0, 4.0, 2.0])) # ISO 15622:2018
|
||||
|
||||
class FrogPilotAcceleration:
|
||||
def __init__(self, FrogPilotPlanner):
|
||||
@@ -39,22 +79,23 @@ class FrogPilotAcceleration:
|
||||
def update(self, v_ego, sm, frogpilot_toggles):
|
||||
eco_gear = sm["frogpilotCarState"].ecoGear
|
||||
sport_gear = sm["frogpilotCarState"].sportGear
|
||||
ev_tuning = frogpilot_toggles.ev_tuning
|
||||
|
||||
if sm["frogpilotCarState"].trafficModeEnabled:
|
||||
self.max_accel = get_max_accel(v_ego)
|
||||
elif frogpilot_toggles.map_acceleration and (eco_gear or sport_gear):
|
||||
if eco_gear:
|
||||
self.max_accel = get_max_accel_eco(v_ego)
|
||||
self.max_accel = get_max_accel_eco(v_ego, ev_tuning)
|
||||
else:
|
||||
if frogpilot_toggles.acceleration_profile == 2:
|
||||
self.max_accel = get_max_accel_sport(v_ego)
|
||||
self.max_accel = get_max_accel_sport(v_ego, ev_tuning)
|
||||
else:
|
||||
self.max_accel = get_max_allowed_accel(v_ego)
|
||||
else:
|
||||
if frogpilot_toggles.acceleration_profile == 1:
|
||||
self.max_accel = get_max_accel_eco(v_ego)
|
||||
self.max_accel = get_max_accel_eco(v_ego, ev_tuning)
|
||||
elif frogpilot_toggles.acceleration_profile == 2:
|
||||
self.max_accel = get_max_accel_sport(v_ego)
|
||||
self.max_accel = get_max_accel_sport(v_ego, ev_tuning)
|
||||
elif frogpilot_toggles.acceleration_profile == 3:
|
||||
self.max_accel = get_max_allowed_accel(v_ego)
|
||||
else:
|
||||
@@ -64,9 +105,7 @@ class FrogPilotAcceleration:
|
||||
self.max_accel = min(get_max_accel_low_speeds(self.max_accel, self.frogpilot_planner.v_cruise), self.max_accel)
|
||||
self.max_accel = min(get_max_accel_ramp_off(self.max_accel, self.frogpilot_planner.v_cruise, v_ego), self.max_accel)
|
||||
|
||||
if self.frogpilot_planner.tracking_lead:
|
||||
self.min_accel = ACCEL_MIN
|
||||
elif sm["frogpilotCarState"].forceCoast:
|
||||
if sm["frogpilotCarState"].forceCoast:
|
||||
self.min_accel = A_CRUISE_MIN_ECO
|
||||
elif frogpilot_toggles.map_deceleration and (eco_gear or sport_gear):
|
||||
if eco_gear:
|
||||
@@ -79,4 +118,4 @@ class FrogPilotAcceleration:
|
||||
elif frogpilot_toggles.deceleration_profile == 2:
|
||||
self.min_accel = A_CRUISE_MIN_SPORT
|
||||
else:
|
||||
self.min_accel = CRUISE_MIN_ACCEL
|
||||
self.min_accel = A_CRUISE_MIN
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
import numpy as np
|
||||
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import COMFORT_BRAKE, STOP_DISTANCE, desired_follow_distance, get_jerk_factor, get_T_FOLLOW
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import COMFORT_BRAKE, desired_follow_distance, get_jerk_factor, get_T_FOLLOW
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT
|
||||
|
||||
TRAFFIC_MODE_BP = [0., CITY_SPEED_LIMIT]
|
||||
PERSONALITY_BP = [20. * CV.KPH_TO_MS, 90. * CV.KPH_TO_MS]
|
||||
|
||||
class FrogPilotFollowing:
|
||||
def __init__(self, FrogPilotPlanner):
|
||||
self.frogpilot_planner = FrogPilotPlanner
|
||||
|
||||
self.disable_throttle = False
|
||||
self.following_lead = False
|
||||
self.slower_lead = False
|
||||
|
||||
@@ -47,12 +50,16 @@ class FrogPilotFollowing:
|
||||
frogpilot_toggles.custom_personalities, sm["controlsState"].personality
|
||||
)
|
||||
|
||||
self.t_follow = get_T_FOLLOW(
|
||||
t_follow_param = get_T_FOLLOW(
|
||||
frogpilot_toggles.aggressive_follow,
|
||||
frogpilot_toggles.standard_follow,
|
||||
frogpilot_toggles.relaxed_follow,
|
||||
frogpilot_toggles.custom_personalities, sm["controlsState"].personality
|
||||
)
|
||||
if isinstance(t_follow_param, list):
|
||||
self.t_follow = float(np.interp(v_ego, PERSONALITY_BP, t_follow_param))
|
||||
else:
|
||||
self.t_follow = float(t_follow_param)
|
||||
else:
|
||||
self.base_acceleration_jerk = 0
|
||||
self.base_danger_jerk = 0
|
||||
@@ -63,7 +70,12 @@ class FrogPilotFollowing:
|
||||
self.danger_jerk = self.base_danger_jerk
|
||||
self.speed_jerk = self.base_speed_jerk
|
||||
|
||||
self.following_lead = self.frogpilot_planner.tracking_lead and self.frogpilot_planner.lead_one.dRel < (self.t_follow + 1) * v_ego
|
||||
self.following_lead = self.frogpilot_planner.tracking_lead and self.frogpilot_planner.lead_one.dRel < (self.t_follow * 2) * v_ego
|
||||
|
||||
|
||||
self.disable_throttle = self.frogpilot_planner.tracking_lead and not self.following_lead
|
||||
self.disable_throttle &= self.frogpilot_planner.lead_one.dRel + 6.0 < (self.t_follow * 2 * 2) * v_ego
|
||||
self.disable_throttle &= self.frogpilot_planner.lead_one.vLead < v_ego * 0.75
|
||||
|
||||
if sm["controlsState"].enabled and self.frogpilot_planner.tracking_lead:
|
||||
self.update_follow_values(self.frogpilot_planner.lead_one.dRel, v_ego, self.frogpilot_planner.lead_one.vLead, frogpilot_toggles)
|
||||
@@ -75,21 +87,20 @@ class FrogPilotFollowing:
|
||||
# Offset by FrogAi for FrogPilot for a more natural approach to a faster lead
|
||||
if frogpilot_toggles.human_following and v_lead > v_ego:
|
||||
distance_factor = max(lead_distance - (v_ego * self.t_follow), 1)
|
||||
accelerating_offset = float(np.clip(STOP_DISTANCE - v_ego, 1, distance_factor))
|
||||
|
||||
self.acceleration_jerk /= accelerating_offset
|
||||
self.speed_jerk /= accelerating_offset
|
||||
self.t_follow /= accelerating_offset
|
||||
from frogpilot.common.frogpilot_variables import get_frogpilot_toggles
|
||||
fp_toggles = get_frogpilot_toggles()
|
||||
acceleration_offset = float(np.clip(fp_toggles.stop_distance - v_ego, 1, distance_factor))
|
||||
self.acceleration_jerk /= acceleration_offset
|
||||
self.speed_jerk /= acceleration_offset
|
||||
self.t_follow /= acceleration_offset
|
||||
|
||||
# Offset by FrogAi for FrogPilot for a more natural approach to a slower lead
|
||||
if (frogpilot_toggles.conditional_slower_lead or frogpilot_toggles.human_following) and v_lead < v_ego:
|
||||
distance_factor = max(lead_distance - (v_lead * self.t_follow), 1)
|
||||
braking_offset = float(np.clip(min(v_ego - v_lead, v_lead) - COMFORT_BRAKE, 1, distance_factor))
|
||||
|
||||
if frogpilot_toggles.human_following:
|
||||
if not self.following_lead and v_lead > CITY_SPEED_LIMIT:
|
||||
far_lead_offset = max(lead_distance - (v_ego * self.t_follow) - STOP_DISTANCE, 0)
|
||||
else:
|
||||
far_lead_offset = 0
|
||||
from frogpilot.common.frogpilot_variables import get_frogpilot_toggles
|
||||
fp_toggles = get_frogpilot_toggles()
|
||||
far_lead_offset = max(lead_distance - (v_ego * self.t_follow) - fp_toggles.stop_distance, 0)
|
||||
self.t_follow /= braking_offset + far_lead_offset
|
||||
self.slower_lead = braking_offset > 1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,6 +18,7 @@ class FrogPilotVCruise:
|
||||
self.override_force_stop = False
|
||||
|
||||
self.override_force_stop_timer = 0
|
||||
self.force_stop_timer = 0.0
|
||||
|
||||
def update(self, gps_position, now, time_validated, v_cruise, v_ego, sm, frogpilot_toggles):
|
||||
force_stop = self.frogpilot_planner.cem.stop_light_detected and sm["controlsState"].enabled and frogpilot_toggles.force_stops
|
||||
@@ -58,16 +59,6 @@ class FrogPilotVCruise:
|
||||
|
||||
self.csc_target = v_cruise
|
||||
|
||||
# Mike's extended lead linear braking
|
||||
if self.frogpilot_planner.lead_one.vLead < v_ego > CRUISING_SPEED and sm["controlsState"].enabled and self.frogpilot_planner.tracking_lead and frogpilot_toggles.human_following:
|
||||
if not self.frogpilot_planner.frogpilot_following.following_lead:
|
||||
decel_rate = (v_ego - self.frogpilot_planner.lead_one.vLead)**2 / self.frogpilot_planner.lead_one.dRel
|
||||
self.braking_target = max(v_ego - (decel_rate * DT_MDL), self.frogpilot_planner.lead_one.vLead + CRUISING_SPEED)
|
||||
else:
|
||||
self.braking_target = v_cruise
|
||||
else:
|
||||
self.braking_target = v_cruise
|
||||
|
||||
# Pfeiferj's Speed Limit Controller
|
||||
self.slc.frogpilot_toggles = frogpilot_toggles
|
||||
|
||||
@@ -97,7 +88,7 @@ class FrogPilotVCruise:
|
||||
|
||||
self.tracked_model_length = self.frogpilot_planner.model_length
|
||||
|
||||
targets = [self.braking_target, self.csc_target, v_cruise]
|
||||
targets = [self.csc_target, v_cruise]
|
||||
if frogpilot_toggles.speed_limit_controller:
|
||||
targets.append(max(self.slc.overridden_speed, self.slc_target + self.slc_offset) - v_ego_diff)
|
||||
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
# Twilsonco's Lateral Neural Network Feedforward
|
||||
from collections import deque
|
||||
from difflib import SequenceMatcher
|
||||
from typing import NamedTuple
|
||||
|
||||
# Twilsonco's Lateral Neural Network Feedforward Controller
|
||||
import json
|
||||
import math
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
from collections import deque
|
||||
from difflib import SequenceMatcher
|
||||
from cereal import log
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.numpy_fast import interp
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
||||
from openpilot.selfdrive.controls.lib.pid import PIDController
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
from openpilot.selfdrive.modeld.constants import ModelConstants
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import NNFF_MODELS_PATH, get_nnff_model_files, params
|
||||
@@ -31,8 +34,7 @@ from openpilot.frogpilot.common.frogpilot_variables import NNFF_MODELS_PATH, get
|
||||
ACTIVATION_FUNCTION_NAMES = {'σ': 'sigmoid'}
|
||||
|
||||
LOW_SPEED_X = [0, 10, 20, 30]
|
||||
LOW_SPEED_Y = [15, 13, 10, 5]
|
||||
LOW_SPEED_Y_NN = [12, 3, 1, 0]
|
||||
LOW_SPEED_Y = [12, 3, 1, 0]
|
||||
|
||||
LAT_PLAN_MIN_IDX = 5
|
||||
|
||||
@@ -126,7 +128,7 @@ def get_nn_model_path(car, eps_firmware) -> str | None:
|
||||
def find_valid_model(*queries):
|
||||
for query in queries:
|
||||
path, score = best_model_path(query)
|
||||
if path and car in path and score >= 0.9:
|
||||
if path and score >= 0.9:
|
||||
return path
|
||||
return None
|
||||
|
||||
@@ -152,21 +154,18 @@ def sign(x):
|
||||
def similarity(s1: str, s2: str) -> float:
|
||||
return SequenceMatcher(None, s1, s2).ratio()
|
||||
|
||||
class LatControlInputs(NamedTuple):
|
||||
lateral_acceleration: float
|
||||
roll_compensation: float
|
||||
vego: float
|
||||
aego: float
|
||||
|
||||
class NeuralNetworkFeedforward:
|
||||
def __init__(self, CP, LatControlTorque):
|
||||
self.lat_control_torque = LatControlTorque
|
||||
|
||||
class LatControlNNFF(LatControl):
|
||||
def __init__(self, CP, CI, dt):
|
||||
super().__init__(CP, CI, dt)
|
||||
self.lat_torque_nn_model = get_nn_model(CP.carFingerprint, str(next((fw.fwVersion for fw in CP.carFw if fw.ecu == "eps"), "")).replace("\\", ""))
|
||||
self.nnff_loaded = self.lat_torque_nn_model is not None
|
||||
|
||||
self.torque_from_lateral_accel = self.lat_control_torque.torque_from_lateral_accel
|
||||
|
||||
self.use_steering_angle = self.lat_control_torque.torque_params.useSteeringAngle
|
||||
self.torque_params = CP.lateralTuning.torque
|
||||
self.pid = PIDController(self.torque_params.kp, self.torque_params.ki,
|
||||
pos_limit=self.steer_max, neg_limit=-self.steer_max)
|
||||
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
|
||||
self.use_steering_angle = self.torque_params.useSteeringAngle
|
||||
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
|
||||
|
||||
# Instantaneous lateral jerk changes very rapidly, making it not useful on its own,
|
||||
# however, we can "look ahead" to the future planned lateral jerk in order to gauge
|
||||
@@ -184,7 +183,6 @@ class NeuralNetworkFeedforward:
|
||||
self.lat_jerk_friction_factor = 0.4
|
||||
|
||||
# precompute time differences between ModelConstants.T_IDXS
|
||||
self.lateral_delay = CP.steerActuatorDelay
|
||||
self.t_diffs = np.diff(ModelConstants.T_IDXS)
|
||||
|
||||
self.pitch = FirstOrderFilter(0.0, 0.5, 0.01)
|
||||
@@ -196,7 +194,7 @@ class NeuralNetworkFeedforward:
|
||||
|
||||
# setup future time offsets
|
||||
self.future_times = [0.3, 0.6, 1.0, 1.5] # seconds in the future
|
||||
self.nn_future_times = [time + self.lateral_delay for time in self.future_times]
|
||||
self.nn_future_times = [time + CP.steerActuatorDelay for time in self.future_times]
|
||||
|
||||
# setup past time offsets
|
||||
self.past_times = [-0.3, -0.2, -0.1]
|
||||
@@ -207,97 +205,150 @@ class NeuralNetworkFeedforward:
|
||||
self.roll_deque = deque(maxlen=history_check_frames[0])
|
||||
|
||||
def update_live_delay(self, lateral_delay):
|
||||
self.lateral_delay = lateral_delay
|
||||
|
||||
self.nn_future_times = [time + self.lateral_delay for time in self.future_times]
|
||||
self.nn_future_times = [time + lateral_delay for time in self.future_times]
|
||||
self.past_future_len = len(self.past_times) + len(self.nn_future_times)
|
||||
|
||||
def compute_nnff(self, CS, VM, actual_lateral_accel, desired_lateral_accel, gravity_adjusted_lateral_accel, lateral_accel_deadzone, llk, measurement, model_data, params, pid_log, roll_compensation, setpoint, frogpilot_toggles):
|
||||
if self.use_steering_angle:
|
||||
actual_curvature_rate = -VM.calc_curvature(math.radians(CS.steeringRateDeg), CS.vEgo, 0.0)
|
||||
actual_lateral_jerk = actual_curvature_rate * CS.vEgo ** 2
|
||||
def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction):
|
||||
self.torque_params.latAccelFactor = latAccelFactor
|
||||
self.torque_params.latAccelOffset = latAccelOffset
|
||||
self.torque_params.friction = friction
|
||||
|
||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay, llk, model_data, frogpilot_toggles):
|
||||
pid_log = log.ControlsState.LateralTorqueState.new_message()
|
||||
if not active:
|
||||
output_torque = 0.0
|
||||
pid_log.active = False
|
||||
else:
|
||||
actual_lateral_jerk = 0.0
|
||||
actual_curvature_vm = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
|
||||
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
|
||||
if self.use_steering_angle:
|
||||
actual_curvature = actual_curvature_vm
|
||||
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
|
||||
else:
|
||||
actual_curvature_llk = llk.angularVelocityCalibrated.value[2] / CS.vEgo
|
||||
actual_curvature = interp(CS.vEgo, [2.0, 5.0], [actual_curvature_vm, actual_curvature_llk])
|
||||
curvature_deadzone = 0.0
|
||||
desired_lateral_accel = desired_curvature * CS.vEgo ** 2
|
||||
|
||||
model_good = model_data is not None and len(model_data.orientation.x) >= CONTROL_N
|
||||
# desired rate is the desired rate of change in the setpoint, not the absolute desired curvature
|
||||
# desired_lateral_jerk = desired_curvature_rate * CS.vEgo ** 2
|
||||
actual_lateral_accel = actual_curvature * CS.vEgo ** 2
|
||||
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2
|
||||
|
||||
if model_good:
|
||||
# prepare "look-ahead" desired lateral jerk
|
||||
lookahead = interp(CS.vEgo, self.friction_look_ahead_bp, self.friction_look_ahead_v)
|
||||
friction_upper_idx = next((idxs for idxs, value in enumerate(ModelConstants.T_IDXS) if value > lookahead), 16)
|
||||
low_speed_factor = interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2
|
||||
setpoint = desired_lateral_accel + low_speed_factor * desired_curvature
|
||||
measurement = actual_lateral_accel + low_speed_factor * actual_curvature
|
||||
gravity_adjusted_lateral_accel = desired_lateral_accel - roll_compensation
|
||||
if self.nnff_loaded and frogpilot_toggles.nnff or frogpilot_toggles.nnff_lite:
|
||||
if self.use_steering_angle:
|
||||
actual_curvature_rate = -VM.calc_curvature(math.radians(CS.steeringRateDeg), CS.vEgo, 0.0)
|
||||
actual_lateral_jerk = actual_curvature_rate * CS.vEgo ** 2
|
||||
else:
|
||||
actual_lateral_jerk = 0.0
|
||||
|
||||
predicted_lateral_jerk = get_predicted_lateral_jerk(model_data.acceleration.y, self.t_diffs)
|
||||
desired_lateral_jerk = (interp(self.lateral_delay, ModelConstants.T_IDXS, model_data.acceleration.y) - desired_lateral_accel) / self.lateral_delay
|
||||
model_good = model_data is not None and len(model_data.orientation.x) >= CONTROL_N
|
||||
|
||||
lookahead_lateral_jerk = get_lookahead_value(predicted_lateral_jerk[LAT_PLAN_MIN_IDX:friction_upper_idx], desired_lateral_jerk)
|
||||
if model_good:
|
||||
# prepare "look-ahead" desired lateral jerk
|
||||
lookahead = interp(CS.vEgo, self.friction_look_ahead_bp, self.friction_look_ahead_v)
|
||||
friction_upper_idx = next((idxs for idxs, value in enumerate(ModelConstants.T_IDXS) if value > lookahead), 16)
|
||||
|
||||
if not self.use_steering_angle or lookahead_lateral_jerk == 0.0:
|
||||
lookahead_lateral_jerk = 0.0
|
||||
actual_lateral_jerk = 0.0
|
||||
self.lat_accel_friction_factor = 1.0
|
||||
predicted_lateral_jerk = get_predicted_lateral_jerk(model_data.acceleration.y, self.t_diffs)
|
||||
desired_lateral_jerk = (interp(lat_delay, ModelConstants.T_IDXS, model_data.acceleration.y) - desired_lateral_accel) / lat_delay
|
||||
|
||||
lateral_jerk_setpoint = self.lat_jerk_friction_factor * lookahead_lateral_jerk
|
||||
lateral_jerk_measurement = self.lat_jerk_friction_factor * actual_lateral_jerk
|
||||
else:
|
||||
lateral_jerk_setpoint = 0
|
||||
lateral_jerk_measurement = 0
|
||||
lookahead_lateral_jerk = 0
|
||||
lookahead_lateral_jerk = get_lookahead_value(predicted_lateral_jerk[LAT_PLAN_MIN_IDX:friction_upper_idx], desired_lateral_jerk)
|
||||
|
||||
if self.lat_control_torque.nnff_loaded and model_good and frogpilot_toggles.nnff:
|
||||
# update past data
|
||||
pitch = 0
|
||||
roll = params.roll
|
||||
if len(llk.calibratedOrientationNED.value) > 1:
|
||||
pitch = self.pitch.update(llk.calibratedOrientationNED.value[1])
|
||||
roll = roll_pitch_adjust(roll, pitch)
|
||||
if not self.use_steering_angle or lookahead_lateral_jerk == 0.0:
|
||||
lookahead_lateral_jerk = 0.0
|
||||
actual_lateral_jerk = 0.0
|
||||
self.lat_accel_friction_factor = 1.0
|
||||
|
||||
self.roll_deque.append(roll)
|
||||
self.lateral_accel_desired_deque.append(desired_lateral_accel)
|
||||
lateral_jerk_setpoint = self.lat_jerk_friction_factor * lookahead_lateral_jerk
|
||||
lateral_jerk_measurement = self.lat_jerk_friction_factor * actual_lateral_jerk
|
||||
else:
|
||||
lateral_jerk_setpoint = 0
|
||||
lateral_jerk_measurement = 0
|
||||
lookahead_lateral_jerk = 0
|
||||
|
||||
# prepare past and future values
|
||||
# adjust future times to account for longitudinal acceleration
|
||||
adjusted_future_times = [time + 0.5 * CS.aEgo * (time / max(CS.vEgo, 1.0)) for time in self.nn_future_times]
|
||||
past_rolls = [self.roll_deque[min(len(self.roll_deque)-1, offset)] for offset in self.history_frame_offsets]
|
||||
future_rolls = [roll_pitch_adjust(interp(time, ModelConstants.T_IDXS, model_data.orientation.x) + roll, interp(time, ModelConstants.T_IDXS, model_data.orientation.y) + pitch) for time in adjusted_future_times]
|
||||
if self.nnff_loaded and model_good and frogpilot_toggles.nnff:
|
||||
# update past data
|
||||
pitch = 0
|
||||
roll = params.roll
|
||||
if len(llk.calibratedOrientationNED.value) > 1:
|
||||
pitch = self.pitch.update(llk.calibratedOrientationNED.value[1])
|
||||
roll = roll_pitch_adjust(roll, pitch)
|
||||
|
||||
past_lateral_accels_desired = [self.lateral_accel_desired_deque[min(len(self.lateral_accel_desired_deque)-1, offset)] for offset in self.history_frame_offsets]
|
||||
future_lateral_accels = [interp(time, ModelConstants.T_IDXS[:CONTROL_N], model_data.acceleration.y) for time in adjusted_future_times]
|
||||
self.roll_deque.append(roll)
|
||||
self.lateral_accel_desired_deque.append(desired_lateral_accel)
|
||||
|
||||
base_input = [CS.vEgo, roll]
|
||||
nnff_common = past_rolls + future_rolls
|
||||
# prepare past and future values
|
||||
# adjust future times to account for longitudinal acceleration
|
||||
adjusted_future_times = [time + 0.5 * CS.aEgo * (time / max(CS.vEgo, 1.0)) for time in self.nn_future_times]
|
||||
past_rolls = [self.roll_deque[min(len(self.roll_deque)-1, offset)] for offset in self.history_frame_offsets]
|
||||
future_rolls = [roll_pitch_adjust(interp(time, ModelConstants.T_IDXS, model_data.orientation.x) + roll, interp(time, ModelConstants.T_IDXS, model_data.orientation.y) + pitch) for time in adjusted_future_times]
|
||||
|
||||
# compute NNFF error response
|
||||
nnff_setpoint_input = base_input[:1] + [setpoint, lateral_jerk_setpoint] + base_input[1:] + [setpoint] * self.past_future_len + nnff_common
|
||||
nnff_measurement_input = base_input[:1] + [measurement, lateral_jerk_measurement] + base_input[1:] + [measurement] * self.past_future_len + nnff_common
|
||||
past_lateral_accels_desired = [self.lateral_accel_desired_deque[min(len(self.lateral_accel_desired_deque)-1, offset)] for offset in self.history_frame_offsets]
|
||||
future_lateral_accels = [interp(time, ModelConstants.T_IDXS[:CONTROL_N], model_data.acceleration.y) for time in adjusted_future_times]
|
||||
|
||||
torque_from_setpoint = self.lat_torque_nn_model.evaluate(nnff_setpoint_input)
|
||||
torque_from_measurement = self.lat_torque_nn_model.evaluate(nnff_measurement_input)
|
||||
base_input = [CS.vEgo, roll]
|
||||
nnff_common = past_rolls + future_rolls
|
||||
|
||||
pid_log.error = torque_from_setpoint - torque_from_measurement
|
||||
# compute NNFF error response
|
||||
nnff_setpoint_input = base_input[:1] + [setpoint, lateral_jerk_setpoint] + base_input[1:] + [setpoint] * self.past_future_len + nnff_common
|
||||
nnff_measurement_input = base_input[:1] + [measurement, lateral_jerk_measurement] + base_input[1:] + [measurement] * self.past_future_len + nnff_common
|
||||
|
||||
error_blend = interp(abs(desired_lateral_accel), [1.0, 2.0], [0.0, 1.0])
|
||||
if error_blend > 0.0: # blend in stronger error response when in high lat accel
|
||||
torque_from_error = self.lat_torque_nn_model.evaluate([CS.vEgo, setpoint - measurement, lateral_jerk_setpoint - lateral_jerk_measurement, 0.0])
|
||||
if sign(pid_log.error) == sign(torque_from_error) and abs(pid_log.error) < abs(torque_from_error):
|
||||
pid_log.error = pid_log.error * (1.0 - error_blend) + torque_from_error * error_blend
|
||||
torque_from_setpoint = self.lat_torque_nn_model.evaluate(nnff_setpoint_input)
|
||||
torque_from_measurement = self.lat_torque_nn_model.evaluate(nnff_measurement_input)
|
||||
|
||||
# compute feedforward (same as nn setpoint output)
|
||||
error = setpoint - measurement
|
||||
friction_input = self.lat_accel_friction_factor * error + self.lat_jerk_friction_factor * lookahead_lateral_jerk
|
||||
nn_input = [CS.vEgo, desired_lateral_accel, friction_input, roll] + past_lateral_accels_desired + future_lateral_accels + nnff_common
|
||||
ff = self.lat_torque_nn_model.evaluate(nn_input)
|
||||
pid_log.error = torque_from_setpoint - torque_from_measurement
|
||||
|
||||
# apply friction override for cars with low NN friction response
|
||||
if self.nn_friction_override:
|
||||
pid_log.error += self.torque_from_lateral_accel(0.0, self.lat_control_torque.torque_params)
|
||||
else:
|
||||
torque_from_measurement = self.torque_from_lateral_accel(measurement, self.lat_control_torque.torque_params)
|
||||
torque_from_setpoint = self.torque_from_lateral_accel(setpoint, self.lat_control_torque.torque_params)
|
||||
error_blend = interp(abs(desired_lateral_accel), [1.0, 2.0], [0.0, 1.0])
|
||||
if error_blend > 0.0: # blend in stronger error response when in high lat accel
|
||||
torque_from_error = self.lat_torque_nn_model.evaluate([CS.vEgo, setpoint - measurement, lateral_jerk_setpoint - lateral_jerk_measurement, 0.0])
|
||||
if sign(pid_log.error) == sign(torque_from_error) and abs(pid_log.error) < abs(torque_from_error):
|
||||
pid_log.error = pid_log.error * (1.0 - error_blend) + torque_from_error * error_blend
|
||||
|
||||
pid_log.error = float(torque_from_setpoint - torque_from_measurement)
|
||||
# compute feedforward (same as nn setpoint output)
|
||||
error = setpoint - measurement
|
||||
friction_input = self.lat_accel_friction_factor * error + self.lat_jerk_friction_factor * lookahead_lateral_jerk
|
||||
nn_input = [CS.vEgo, desired_lateral_accel, friction_input, roll] + past_lateral_accels_desired + future_lateral_accels + nnff_common
|
||||
ff = self.lat_torque_nn_model.evaluate(nn_input)
|
||||
|
||||
error = desired_lateral_accel - actual_lateral_accel
|
||||
friction_input = self.lat_accel_friction_factor * error + self.lat_jerk_friction_factor * lookahead_lateral_jerk
|
||||
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.lat_control_torque.torque_params)
|
||||
# apply friction override for cars with low NN friction response
|
||||
if self.nn_friction_override:
|
||||
pid_log.error += self.torque_from_lateral_accel(0.0, self.torque_params)
|
||||
else:
|
||||
torque_from_measurement = self.torque_from_lateral_accel(measurement, self.torque_params)
|
||||
torque_from_setpoint = self.torque_from_lateral_accel(setpoint, self.torque_params)
|
||||
|
||||
return pid_log, ff
|
||||
pid_log.error = float(torque_from_setpoint - torque_from_measurement)
|
||||
|
||||
error = desired_lateral_accel - actual_lateral_accel
|
||||
friction_input = self.lat_accel_friction_factor * error + self.lat_jerk_friction_factor * lookahead_lateral_jerk
|
||||
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params)
|
||||
else:
|
||||
torque_from_measurement = self.torque_from_lateral_accel(measurement, self.torque_params)
|
||||
torque_from_setpoint = self.torque_from_lateral_accel(setpoint, self.torque_params)
|
||||
|
||||
pid_log.error = float(torque_from_setpoint - torque_from_measurement)
|
||||
|
||||
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params)
|
||||
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
||||
output_torque = self.pid.update(pid_log.error,
|
||||
feedforward=ff,
|
||||
speed=CS.vEgo,
|
||||
freeze_integrator=freeze_integrator)
|
||||
|
||||
pid_log.active = True
|
||||
pid_log.p = float(self.pid.p)
|
||||
pid_log.i = float(self.pid.i)
|
||||
pid_log.d = float(self.pid.d)
|
||||
pid_log.f = float(self.pid.f)
|
||||
pid_log.output = float(-output_torque)
|
||||
pid_log.actualLateralAccel = float(actual_lateral_accel)
|
||||
pid_log.desiredLateralAccel = float(desired_lateral_accel)
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
|
||||
|
||||
# TODO left is positive in this convention
|
||||
return -output_torque, 0.0, pid_log
|
||||
|
||||
@@ -17,7 +17,7 @@ from openpilot.frogpilot.common.frogpilot_variables import MAPD_PATH, RESOURCES_
|
||||
VERSION = "v2"
|
||||
|
||||
GITHUB_VERSION_URL = f"https://github.com/{RESOURCES_REPO}/raw/Versions/mapd_version_{VERSION}.json"
|
||||
GITLAB_VERSION_URL = f"https://gitlab.com/{RESOURCES_REPO}/-/raw/Versions/mapd_version_{VERSION}.json"
|
||||
GITLAB_VERSION_URL = f"https://gitlab.com/firestar5683/FrogPilot-Resources/-/raw/Versions/mapd_version_{VERSION}.json"
|
||||
|
||||
VERSION_PATH = Path("/data/media/0/osm/mapd_version")
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -20,6 +20,30 @@ from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles
|
||||
|
||||
BASE_URL = "https://nominatim.openstreetmap.org"
|
||||
MINIMUM_POPULATION = 100_000
|
||||
SEARCH_RADIUS_DEGREES = 1.45
|
||||
|
||||
def get_population_value(population_str):
|
||||
if population_str is None:
|
||||
return None
|
||||
try:
|
||||
return int(str(population_str).replace(",", "").split(";")[0].strip())
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def search_nearby_major_cities(lat, lon, session, state_name, country_name):
|
||||
viewbox = f"{lon - SEARCH_RADIUS_DEGREES},{lat + SEARCH_RADIUS_DEGREES},{lon + SEARCH_RADIUS_DEGREES},{lat - SEARCH_RADIUS_DEGREES}"
|
||||
cities = (session.get(f"{BASE_URL}/search", params={
|
||||
"addressdetails": 1, "bounded": 1, "extratags": 1, "format": "jsonv2", "limit": 20, "q": "city", "viewbox": viewbox
|
||||
}, timeout=10).json() or [])
|
||||
|
||||
qualifying = [c for c in cities if (get_population_value((c.get("extratags") or {}).get("population")) or 0) >= MINIMUM_POPULATION]
|
||||
if not qualifying:
|
||||
return None
|
||||
|
||||
nearest = min(qualifying, key=lambda c: (float(c["lat"]) - lat) ** 2 + (float(c["lon"]) - lon) ** 2)
|
||||
addr = nearest.get("address") or {}
|
||||
return float(nearest["lat"]), float(nearest["lon"]), addr.get("city") or addr.get("town") or nearest.get("display_name", "").split(",")[0], state_name, country_name
|
||||
|
||||
|
||||
def get_city_center(latitude, longitude):
|
||||
try:
|
||||
@@ -44,14 +68,7 @@ def get_city_center(latitude, longitude):
|
||||
|
||||
if data:
|
||||
tags = data[0]
|
||||
population = (tags.get("extratags") or {}).get("population")
|
||||
|
||||
population_value = None
|
||||
if population is not None:
|
||||
try:
|
||||
population_value = int(str(population).replace(",", "").split(";")[0].strip())
|
||||
except Exception:
|
||||
population_value = None
|
||||
population_value = get_population_value((tags.get("extratags") or {}).get("population"))
|
||||
|
||||
if population_value is not None and population_value >= MINIMUM_POPULATION:
|
||||
latitude_value = float(tags["lat"])
|
||||
@@ -62,6 +79,10 @@ def get_city_center(latitude, longitude):
|
||||
|
||||
return latitude_value, longitude_value, city_label, state_name, country_name
|
||||
|
||||
nearby_result = search_nearby_major_cities(latitude, longitude, session, state_name, country_name)
|
||||
if nearby_result:
|
||||
return nearby_result
|
||||
|
||||
query = f"{state_name} state capital" if country_code == "us" else f"capital of {state_name}, {country_name}"
|
||||
response = session.get(f"{BASE_URL}/search", params={"addressdetails": 1, "extratags": 1, "format": "jsonv2", "limit": 5, "q": query}, timeout=10)
|
||||
response.raise_for_status()
|
||||
@@ -97,6 +118,19 @@ def get_city_center(latitude, longitude):
|
||||
print(f"Falling back to (0, 0) for {latitude}, {longitude}")
|
||||
return float(0.0), float(0.0), "N/A", "N/A", "N/A"
|
||||
|
||||
def update_branch_commits(now):
|
||||
points = []
|
||||
branch = get_build_metadata().channel # Current running branch
|
||||
try:
|
||||
response = requests.get(f"https://api.github.com/repos/firestar5683/StarPilot/commits/{branch}")
|
||||
response.raise_for_status()
|
||||
sha = response.json()["sha"]
|
||||
points.append(Point("branch_commits").field("commit", sha).tag("branch", branch).time(now))
|
||||
except Exception as e:
|
||||
print(f"Failed to fetch commit for {branch}: {e}")
|
||||
|
||||
return points
|
||||
|
||||
def is_up_to_date(build_metadata):
|
||||
remote_commit = run_cmd(["git", "ls-remote", "origin", build_metadata.channel], f"Fetched remote commit", "Failed to fetch remote commit", report=False)
|
||||
|
||||
@@ -116,11 +150,9 @@ def send_stats():
|
||||
if frogpilot_toggles.car_make == "mock":
|
||||
return
|
||||
|
||||
bucket = os.environ.get("STATS_BUCKET", "")
|
||||
org_ID = os.environ.get("STATS_ORG_ID", "")
|
||||
token = os.environ.get("STATS_TOKEN", "")
|
||||
url = os.environ.get("STATS_URL", "")
|
||||
|
||||
bucket = "StarPilot"
|
||||
org_ID = "StarPilot"
|
||||
url = "https://stats.firestar.link"
|
||||
frogpilot_stats = json.loads(params.get("FrogPilotStats") or "{}")
|
||||
|
||||
location = json.loads(params.get("LastGPSPosition") or "{}")
|
||||
@@ -144,15 +176,23 @@ def send_stats():
|
||||
|
||||
selected_theme = random.choice([item for item, count in most_common if count == max_count]).replace("-user_created", "").replace("_", " ")
|
||||
|
||||
point = (Point("user_stats")
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
user_point = (
|
||||
Point("user_stats")
|
||||
.tag("car_make", "GM" if frogpilot_toggles.car_make == "gm" else frogpilot_toggles.car_make.title())
|
||||
.tag("car_model", frogpilot_toggles.car_model)
|
||||
.tag("city", city)
|
||||
.tag("country", country)
|
||||
.tag("device", HARDWARE.get_device_type())
|
||||
.tag("driving_model", clean_model_name(frogpilot_toggles.model_name))
|
||||
.tag("state", state)
|
||||
.tag("theme", selected_theme.title())
|
||||
.tag("branch", build_metadata.channel)
|
||||
.tag("dongle_id", params.get("FrogPilotDongleId", encoding="utf-8"))
|
||||
|
||||
.field("blocked_user", frogpilot_toggles.block_user)
|
||||
.field("car_make", "GM" if frogpilot_toggles.car_make == "gm" else frogpilot_toggles.car_make.title())
|
||||
.field("car_model", frogpilot_toggles.car_model)
|
||||
.field("city", city)
|
||||
.field("country", country)
|
||||
.field("current_months_kilometers", int(frogpilot_stats.get("CurrentMonthsKilometers", 0)))
|
||||
.field("device", HARDWARE.get_device_type())
|
||||
.field("driving_model", clean_model_name(frogpilot_toggles.model_name))
|
||||
.field("event", 1)
|
||||
.field("frogpilot_drives", int(frogpilot_stats.get("FrogPilotDrives", 0)))
|
||||
.field("frogpilot_hours", float(frogpilot_stats.get("FrogPilotSeconds", 0)) / (60 * 60))
|
||||
@@ -162,13 +202,12 @@ def send_stats():
|
||||
.field("has_openpilot_longitudinal", frogpilot_toggles.openpilot_longitudinal)
|
||||
.field("has_pedal", frogpilot_toggles.has_pedal)
|
||||
.field("has_sdsu", frogpilot_toggles.has_sdsu)
|
||||
.field("has_sascm", frogpilot_toggles.has_sascm)
|
||||
.field("has_zss", frogpilot_toggles.has_zss)
|
||||
.field("latitude", latitude)
|
||||
.field("longitude", longitude)
|
||||
.field("rainbow_path", frogpilot_toggles.rainbow_path)
|
||||
.field("random_events", frogpilot_toggles.random_events)
|
||||
.field("state", state)
|
||||
.field("theme", selected_theme.title())
|
||||
.field("total_aol_seconds", float(frogpilot_stats.get("AOLTime", 0)))
|
||||
.field("total_lateral_seconds", float(frogpilot_stats.get("LateralTime", 0)))
|
||||
.field("total_longitudinal_seconds", float(frogpilot_stats.get("LongitudinalTime", 0)))
|
||||
@@ -177,13 +216,13 @@ def send_stats():
|
||||
.field("up_to_date", is_up_to_date(build_metadata))
|
||||
.field("using_stock_acc", not (frogpilot_toggles.has_cc_long or frogpilot_toggles.openpilot_longitudinal))
|
||||
|
||||
.tag("branch", build_metadata.channel)
|
||||
.tag("dongle_id", params.get("FrogPilotDongleId", encoding="utf-8"))
|
||||
|
||||
.time(datetime.now(timezone.utc))
|
||||
.time(now)
|
||||
)
|
||||
|
||||
InfluxDBClient(org=org_ID, token=token, url=url).write_api(write_options=SYNCHRONOUS).write(bucket=bucket, org=org_ID, record=point)
|
||||
all_points = [user_point] + update_branch_commits(now)
|
||||
|
||||
client = InfluxDBClient(org=org_ID, token=org_ID, url=url)
|
||||
client.write_api(write_options=SYNCHRONOUS).write(bucket=bucket, org=org_ID, record=all_points)
|
||||
print("Successfully sent FrogPilot stats!")
|
||||
except Exception as exception:
|
||||
print(f"Failed to send FrogPilot stats: {exception}")
|
||||
|
||||
@@ -13,11 +13,15 @@ class ModelConstants:
|
||||
META_T_IDXS = [2., 4., 6., 8., 10.]
|
||||
|
||||
# model inputs constants
|
||||
MODEL_FREQ = 20
|
||||
HISTORY_FREQ = 5
|
||||
HISTORY_LEN_SECONDS = 5
|
||||
TEMPORAL_SKIP = MODEL_FREQ // HISTORY_FREQ
|
||||
FULL_HISTORY_BUFFER_LEN = MODEL_FREQ * HISTORY_LEN_SECONDS
|
||||
INPUT_HISTORY_BUFFER_LEN = HISTORY_FREQ * HISTORY_LEN_SECONDS
|
||||
N_FRAMES = 2
|
||||
MODEL_RUN_FREQ = 20
|
||||
MODEL_CONTEXT_FREQ = 5 # "model_trained_fps"
|
||||
FULL_HISTORY_BUFFER_LEN = MODEL_RUN_FREQ * MODEL_CONTEXT_FREQ
|
||||
TEMPORAL_SKIP = MODEL_RUN_FREQ // MODEL_CONTEXT_FREQ
|
||||
|
||||
FEATURE_LEN = 512
|
||||
|
||||
|
||||
@@ -3,11 +3,25 @@ import capnp
|
||||
import numpy as np
|
||||
from cereal import log
|
||||
from openpilot.frogpilot.tinygrad_modeld.constants import ModelConstants, Plan, Meta
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import get_curvature_from_plan
|
||||
|
||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||
|
||||
ConfidenceClass = log.ModelDataV2.ConfidenceClass
|
||||
|
||||
# Return curvature for lateral action. If the model outputs desired_curvature and we're not in mlsim mode,
|
||||
# use it directly; otherwise derive from the plan using yaw and yaw-rate.
|
||||
def get_curvature_from_output(output: dict, plan: np.ndarray, v_ego: float, lat_action_t: float, mlsim: bool) -> float:
|
||||
if not mlsim:
|
||||
desired = output.get('desired_curvature')
|
||||
if desired is not None:
|
||||
return float(desired[0, 0])
|
||||
|
||||
# Use yaw (index 2) and yaw_rate (index 2)
|
||||
theta = plan[:, Plan.T_FROM_CURRENT_EULER][:, 2]
|
||||
theta_dot = plan[:, Plan.ORIENTATION_RATE][:, 2]
|
||||
return float(get_curvature_from_plan(theta, theta_dot, ModelConstants.T_IDXS, v_ego, lat_action_t))
|
||||
|
||||
|
||||
class PublishState:
|
||||
def __init__(self):
|
||||
@@ -82,15 +96,32 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
|
||||
modelV2.timestampEof = timestamp_eof
|
||||
modelV2.modelExecutionTime = model_execution_time
|
||||
|
||||
# normalize plan tensors to (IDX_N, WIDTH)
|
||||
plan_arr = net_output_data['plan'][0]
|
||||
plan_stds_arr = net_output_data['plan_stds'][0]
|
||||
|
||||
# plan
|
||||
fill_xyzt(modelV2.position, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.POSITION].T, *net_output_data['plan_stds'][0,:,Plan.POSITION].T)
|
||||
fill_xyzt(modelV2.velocity, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.VELOCITY].T)
|
||||
fill_xyzt(modelV2.acceleration, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ACCELERATION].T)
|
||||
fill_xyzt(modelV2.orientation, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.T_FROM_CURRENT_EULER].T)
|
||||
fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T)
|
||||
fill_xyzt(modelV2.position, ModelConstants.T_IDXS, *plan_arr[:,Plan.POSITION].T, *plan_stds_arr[:,Plan.POSITION].T)
|
||||
fill_xyzt(modelV2.velocity, ModelConstants.T_IDXS, *plan_arr[:,Plan.VELOCITY].T)
|
||||
fill_xyzt(modelV2.acceleration, ModelConstants.T_IDXS, *plan_arr[:,Plan.ACCELERATION].T)
|
||||
fill_xyzt(modelV2.orientation, ModelConstants.T_IDXS, *plan_arr[:,Plan.T_FROM_CURRENT_EULER].T)
|
||||
fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *plan_arr[:,Plan.ORIENTATION_RATE].T)
|
||||
|
||||
# temporal pose
|
||||
temporal_pose = modelV2.temporalPose
|
||||
if 'sim_pose' in net_output_data:
|
||||
temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist()
|
||||
temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist()
|
||||
temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist()
|
||||
temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist()
|
||||
else:
|
||||
temporal_pose.trans = plan_arr[0,Plan.VELOCITY].tolist()
|
||||
temporal_pose.transStd = plan_stds_arr[0,Plan.VELOCITY].tolist()
|
||||
temporal_pose.rot = plan_arr[0,Plan.ORIENTATION_RATE].tolist()
|
||||
temporal_pose.rotStd = plan_stds_arr[0,Plan.ORIENTATION_RATE].tolist()
|
||||
|
||||
# poly path
|
||||
fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T)
|
||||
fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *plan_arr[:,Plan.POSITION].T)
|
||||
|
||||
# action
|
||||
modelV2.action = action
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,13 +1,16 @@
|
||||
import numpy as np
|
||||
from openpilot.frogpilot.tinygrad_modeld.constants import ModelConstants
|
||||
|
||||
|
||||
def safe_exp(x, out=None):
|
||||
# -11 is around 10**14, more causes float16 overflow
|
||||
return np.exp(np.clip(x, -np.inf, 11), out=out)
|
||||
|
||||
|
||||
def sigmoid(x):
|
||||
return 1. / (1. + safe_exp(-x))
|
||||
|
||||
|
||||
def softmax(x, axis=-1):
|
||||
x -= np.max(x, axis=axis, keepdims=True)
|
||||
if x.dtype == np.float32 or x.dtype == np.float64:
|
||||
@@ -17,15 +20,15 @@ def softmax(x, axis=-1):
|
||||
x /= np.sum(x, axis=axis, keepdims=True)
|
||||
return x
|
||||
|
||||
|
||||
class Parser:
|
||||
def __init__(self, ignore_missing=False):
|
||||
self.ignore_missing = ignore_missing
|
||||
|
||||
def check_missing(self, outs, name):
|
||||
missing = name not in outs
|
||||
if missing and not self.ignore_missing:
|
||||
if name not in outs and not self.ignore_missing:
|
||||
raise ValueError(f"Missing output {name}")
|
||||
return missing
|
||||
return name not in outs
|
||||
|
||||
def parse_categorical_crossentropy(self, name, outs, out_shape=None):
|
||||
if self.check_missing(outs, name):
|
||||
@@ -85,45 +88,50 @@ class Parser:
|
||||
outs[name] = pred_mu_final.reshape(final_shape)
|
||||
outs[name + '_stds'] = pred_std_final.reshape(final_shape)
|
||||
|
||||
def is_mhp(self, outs, name, shape):
|
||||
if self.check_missing(outs, name):
|
||||
return False
|
||||
if outs[name].shape[1] == 2 * shape:
|
||||
return False
|
||||
return True
|
||||
def split_outputs(self, outs: dict[str, np.ndarray]) -> None:
|
||||
if 'lead' in outs:
|
||||
if outs['lead'].shape[1] == 2 * ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH:
|
||||
self.parse_mdn('lead', outs, in_N=0, out_N=0,
|
||||
out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH))
|
||||
else:
|
||||
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
|
||||
out_shape=(ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH))
|
||||
if 'plan' in outs:
|
||||
plan_mhp = outs['plan'].shape[1] != 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH
|
||||
plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0)
|
||||
self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N,
|
||||
out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
|
||||
if 'planplus' in outs:
|
||||
self.parse_mdn('planplus', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
|
||||
if 'lane_lines' in outs:
|
||||
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0,
|
||||
out_shape=(ModelConstants.NUM_LANE_LINES, ModelConstants.IDX_N, ModelConstants.LANE_LINES_WIDTH))
|
||||
if 'road_edges' in outs:
|
||||
self.parse_mdn('road_edges', outs, in_N=0, out_N=0,
|
||||
out_shape=(ModelConstants.NUM_ROAD_EDGES, ModelConstants.IDX_N, ModelConstants.LANE_LINES_WIDTH))
|
||||
if 'sim_pose' in outs:
|
||||
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
|
||||
if 'lane_lines_prob' in outs:
|
||||
self.parse_binary_crossentropy('lane_lines_prob', outs)
|
||||
if 'lead_prob' in outs:
|
||||
self.parse_binary_crossentropy('lead_prob', outs)
|
||||
|
||||
def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
|
||||
self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
|
||||
self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,))
|
||||
self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
|
||||
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
|
||||
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
|
||||
self.parse_binary_crossentropy('lane_lines_prob', outs)
|
||||
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH))
|
||||
self.split_outputs(outs)
|
||||
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN, ModelConstants.DESIRE_PRED_WIDTH))
|
||||
self.parse_binary_crossentropy('meta', outs)
|
||||
self.parse_binary_crossentropy('lead_prob', outs)
|
||||
lead_mhp = self.is_mhp(outs, 'lead', ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH)
|
||||
lead_in_N, lead_out_N = (ModelConstants.LEAD_MHP_N, ModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0)
|
||||
lead_out_shape = (ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) if lead_mhp else \
|
||||
(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH)
|
||||
self.parse_mdn('lead', outs, in_N=lead_in_N, out_N=lead_out_N, out_shape=lead_out_shape)
|
||||
return outs
|
||||
|
||||
def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
|
||||
plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH)
|
||||
plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0)
|
||||
self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
|
||||
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
|
||||
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
|
||||
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
|
||||
self.split_outputs(outs)
|
||||
if 'lat_planner_solution' in outs:
|
||||
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N, ModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
|
||||
if 'desired_curvature' in outs:
|
||||
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,))
|
||||
for k in ['lead_prob', 'lane_lines_prob']:
|
||||
self.parse_binary_crossentropy(k, outs)
|
||||
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,))
|
||||
self.parse_binary_crossentropy('lead_prob', outs)
|
||||
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
|
||||
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
|
||||
return outs
|
||||
|
||||
def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
import os
|
||||
from openpilot.system.hardware import TICI
|
||||
os.environ['DEV'] = 'QCOM' if TICI else 'LLVM'
|
||||
USBGPU = "USBGPU" in os.environ
|
||||
if USBGPU:
|
||||
os.environ['DEV'] = 'AMD'
|
||||
os.environ['AMD_IFACE'] = 'USB'
|
||||
from tinygrad.tensor import Tensor
|
||||
from tinygrad.dtype import dtypes
|
||||
import time
|
||||
@@ -14,6 +10,7 @@ import numpy as np
|
||||
import cereal.messaging as messaging
|
||||
from cereal import car, log
|
||||
from pathlib import Path
|
||||
from setproctitle import setproctitle
|
||||
from cereal.messaging import PubMaster, SubMaster
|
||||
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
@@ -22,49 +19,47 @@ from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.realtime import config_realtime_process, DT_MDL
|
||||
from openpilot.common.transformations.camera import DEVICE_CAMERAS
|
||||
from openpilot.common.transformations.model import get_warp_matrix
|
||||
from openpilot.system import sentry
|
||||
from openpilot.selfdrive.car.car_helpers import get_demo_car_params
|
||||
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value, get_curvature_from_plan
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan_tomb_raider, smooth_value
|
||||
from openpilot.frogpilot.tinygrad_modeld.parse_model_outputs import Parser
|
||||
from openpilot.frogpilot.tinygrad_modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
|
||||
from openpilot.frogpilot.tinygrad_modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState, get_curvature_from_output
|
||||
from openpilot.frogpilot.tinygrad_modeld.constants import ModelConstants, Plan
|
||||
from openpilot.frogpilot.tinygrad_modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
|
||||
from openpilot.frogpilot.tinygrad_modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import MODELS_PATH, get_frogpilot_toggles
|
||||
from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles, MODELS_PATH
|
||||
|
||||
|
||||
PROCESS_NAME = "frogpilot.tinygrad_modeld.tinygrad_modeld"
|
||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||
|
||||
VISION_PKL_PATH = Path(__file__).parent / 'models/driving_vision_tinygrad.pkl'
|
||||
POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
|
||||
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
|
||||
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
|
||||
|
||||
LAT_SMOOTH_SECONDS = 0.1
|
||||
LAT_SMOOTH_SECONDS = 0.0
|
||||
LONG_SMOOTH_SECONDS = 0.3
|
||||
MIN_LAT_CONTROL_SPEED = 0.3
|
||||
|
||||
|
||||
def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,
|
||||
lat_action_t: float, long_action_t: float, v_ego: float, use_curvature_from_plan: bool) -> log.ModelDataV2.Action:
|
||||
lat_action_t: float, long_action_t: float, v_ego: float, mlsim: bool, is_v9: bool, frogpilot_toggles) -> log.ModelDataV2.Action:
|
||||
plan = model_output['plan'][0]
|
||||
desired_accel, should_stop = get_accel_from_plan(plan[:,Plan.VELOCITY][:,0],
|
||||
plan[:,Plan.ACCELERATION][:,0],
|
||||
ModelConstants.T_IDXS,
|
||||
action_t=long_action_t)
|
||||
if 'planplus' in model_output:
|
||||
plan = plan + frogpilot_toggles.recovery_power*model_output['planplus'][0]
|
||||
cloudlog.error(f"planplus applied: shape {model_output['planplus'].shape}, RECOVERY_POWER {frogpilot_toggles.recovery_power}")
|
||||
desired_accel, should_stop = get_accel_from_plan_tomb_raider(plan[:,Plan.VELOCITY][:,0],
|
||||
plan[:,Plan.ACCELERATION][:,0],
|
||||
ModelConstants.T_IDXS,
|
||||
action_t=long_action_t)
|
||||
desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS)
|
||||
|
||||
if use_curvature_from_plan:
|
||||
desired_curvature = get_curvature_from_plan(plan[:,Plan.T_FROM_CURRENT_EULER][:,2],
|
||||
plan[:,Plan.ORIENTATION_RATE][:,2],
|
||||
ModelConstants.T_IDXS,
|
||||
v_ego,
|
||||
lat_action_t)
|
||||
if is_v9:
|
||||
# V9: use desired_curvature if present; otherwise do NOT fall back to plan
|
||||
if 'desired_curvature' in model_output:
|
||||
desired_curvature = float(model_output['desired_curvature'][0, 0])
|
||||
else:
|
||||
desired_curvature = prev_action.desiredCurvature
|
||||
else:
|
||||
desired_curvature = model_output['desired_curvature'][0, 0]
|
||||
|
||||
desired_curvature = get_curvature_from_output(model_output, plan, v_ego, lat_action_t, mlsim=mlsim)
|
||||
if v_ego > MIN_LAT_CONTROL_SPEED:
|
||||
desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS)
|
||||
else:
|
||||
@@ -83,113 +78,196 @@ class FrameMeta:
|
||||
if vipc is not None:
|
||||
self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof
|
||||
|
||||
class InputQueues:
|
||||
def __init__ (self, model_fps, env_fps, n_frames_input):
|
||||
assert env_fps % model_fps == 0
|
||||
assert env_fps >= model_fps
|
||||
self.model_fps = model_fps
|
||||
self.env_fps = env_fps
|
||||
self.n_frames_input = n_frames_input
|
||||
|
||||
self.dtypes = {}
|
||||
self.shapes = {}
|
||||
self.q = {}
|
||||
|
||||
def update_dtypes_and_shapes(self, input_dtypes, input_shapes) -> None:
|
||||
self.dtypes.update(input_dtypes)
|
||||
if self.env_fps == self.model_fps:
|
||||
self.shapes.update(input_shapes)
|
||||
else:
|
||||
for k in input_shapes:
|
||||
shape = list(input_shapes[k])
|
||||
if 'img' in k:
|
||||
n_channels = shape[1] // self.n_frames_input
|
||||
shape[1] = (self.env_fps // self.model_fps + (self.n_frames_input - 1)) * n_channels
|
||||
else:
|
||||
shape[1] = (self.env_fps // self.model_fps) * shape[1]
|
||||
self.shapes[k] = tuple(shape)
|
||||
|
||||
def reset(self) -> None:
|
||||
self.q = {k: np.zeros(self.shapes[k], dtype=self.dtypes[k]) for k in self.dtypes.keys()}
|
||||
|
||||
def enqueue(self, inputs:dict[str, np.ndarray]) -> None:
|
||||
for k in inputs.keys():
|
||||
if inputs[k].dtype != self.dtypes[k]:
|
||||
raise ValueError(f'supplied input <{k}({inputs[k].dtype})> has wrong dtype, expected {self.dtypes[k]}')
|
||||
input_shape = list(self.shapes[k])
|
||||
input_shape[1] = -1
|
||||
single_input = inputs[k].reshape(tuple(input_shape))
|
||||
sz = single_input.shape[1]
|
||||
self.q[k][:,:-sz] = self.q[k][:,sz:]
|
||||
self.q[k][:,-sz:] = single_input
|
||||
|
||||
def get(self, *names) -> dict[str, np.ndarray]:
|
||||
if self.env_fps == self.model_fps:
|
||||
return {k: self.q[k] for k in names}
|
||||
else:
|
||||
out = {}
|
||||
for k in names:
|
||||
shape = self.shapes[k]
|
||||
if 'img' in k:
|
||||
n_channels = shape[1] // (self.env_fps // self.model_fps + (self.n_frames_input - 1))
|
||||
out[k] = np.concatenate([self.q[k][:, s:s+n_channels] for s in np.linspace(0, shape[1] - n_channels, self.n_frames_input, dtype=int)], axis=1)
|
||||
elif 'pulse' in k:
|
||||
# any pulse within interval counts
|
||||
out[k] = self.q[k].reshape((shape[0], shape[1] * self.model_fps // self.env_fps, self.env_fps // self.model_fps, -1)).max(axis=2)
|
||||
else:
|
||||
idxs = np.arange(-1, -shape[1], -self.env_fps // self.model_fps)[::-1]
|
||||
out[k] = self.q[k][:, idxs]
|
||||
return out
|
||||
|
||||
class ModelState:
|
||||
frames: dict[str, DrivingModelFrame]
|
||||
inputs: dict[str, np.ndarray]
|
||||
output: np.ndarray
|
||||
prev_desire: np.ndarray # for tracking the rising edge of the pulse
|
||||
|
||||
def __init__(self, context: CLContext, model: str):
|
||||
with open(MODELS_PATH / f'{model}_driving_vision_metadata.pkl', 'rb') as f:
|
||||
vision_metadata = pickle.load(f)
|
||||
self.vision_input_shapes = vision_metadata['input_shapes']
|
||||
self.vision_input_names = list(self.vision_input_shapes.keys())
|
||||
self.vision_output_slices = vision_metadata['output_slices']
|
||||
vision_output_size = vision_metadata['output_shapes']['outputs'][1]
|
||||
def _build_policy_inputs(self, input_shapes: dict[str, tuple[int, ...]]) -> tuple[dict[str, np.ndarray], str | None]:
|
||||
numpy_inputs: dict[str, np.ndarray] = {}
|
||||
|
||||
with open(MODELS_PATH / f'{model}_driving_policy_metadata.pkl', 'rb') as f:
|
||||
policy_metadata = pickle.load(f)
|
||||
self.policy_input_shapes = policy_metadata['input_shapes']
|
||||
self.policy_output_slices = policy_metadata['output_slices']
|
||||
policy_output_size = policy_metadata['output_shapes']['outputs'][1]
|
||||
# Always-supported inputs (if model expects them)
|
||||
desire_key_init = next((k for k in input_shapes if k.startswith('desire')), None)
|
||||
if desire_key_init:
|
||||
numpy_inputs[desire_key_init] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32)
|
||||
if 'traffic_convention' in input_shapes:
|
||||
numpy_inputs['traffic_convention'] = np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32)
|
||||
if 'features_buffer' in input_shapes:
|
||||
numpy_inputs['features_buffer'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
|
||||
|
||||
self.desire_type = 'desire_pulse' if 'desire_pulse' in self.policy_input_shapes else 'desire'
|
||||
self.use_lateral_control_params = 'lateral_control_params' in self.policy_input_shapes
|
||||
# Optional inputs for non-v11 (and some v10/v9 variants)
|
||||
# Lateral control params
|
||||
if 'lateral_control_params' in input_shapes:
|
||||
numpy_inputs['lateral_control_params'] = np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32)
|
||||
|
||||
self.frames = {name: DrivingModelFrame(context, ModelConstants.MODEL_RUN_FREQ//ModelConstants.MODEL_CONTEXT_FREQ) for name in self.vision_input_names}
|
||||
# Previous desired curvature: handle both singular and plural key names across model versions
|
||||
prev_desired_curv_key = None
|
||||
if 'prev_desired_curv' in input_shapes:
|
||||
prev_desired_curv_key = 'prev_desired_curv'
|
||||
numpy_inputs['prev_desired_curv'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
|
||||
elif 'prev_desired_curvs' in input_shapes:
|
||||
prev_desired_curv_key = 'prev_desired_curvs'
|
||||
numpy_inputs['prev_desired_curvs'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
|
||||
|
||||
return numpy_inputs, prev_desired_curv_key
|
||||
|
||||
def __init__(self, context: CLContext):
|
||||
# Dynamically build paths based on current model ID
|
||||
params = Params()
|
||||
model_id = params.get("Model", encoding="utf-8")
|
||||
|
||||
# Try to get ModelVersion, but handle case where parameter doesn't exist
|
||||
model_version = None
|
||||
try:
|
||||
model_version = params.get("ModelVersion", encoding="utf-8")
|
||||
except Exception as e:
|
||||
cloudlog.warning(f"ModelVersion parameter not available: {e}")
|
||||
|
||||
model_dir = MODELS_PATH
|
||||
# For the default "bd2" model, use built-in files from the models directory
|
||||
if model_id == "bd2":
|
||||
models_dir = Path(__file__).parent / "models"
|
||||
VISION_PKL_PATH = models_dir / "driving_vision_tinygrad.pkl"
|
||||
POLICY_PKL_PATH = models_dir / "driving_policy_tinygrad.pkl"
|
||||
OFF_POLICY_PKL_PATH = models_dir / "driving_off_policy_tinygrad.pkl"
|
||||
VISION_METADATA_PATH = models_dir / "driving_vision_metadata.pkl"
|
||||
POLICY_METADATA_PATH = models_dir / "driving_policy_metadata.pkl"
|
||||
OFF_POLICY_METADATA_PATH = models_dir / "driving_off_policy_metadata.pkl"
|
||||
else:
|
||||
VISION_PKL_PATH = model_dir / f"{model_id}_driving_vision_tinygrad.pkl"
|
||||
POLICY_PKL_PATH = model_dir / f"{model_id}_driving_policy_tinygrad.pkl"
|
||||
OFF_POLICY_PKL_PATH = model_dir / f"{model_id}_driving_off_policy_tinygrad.pkl"
|
||||
VISION_METADATA_PATH = model_dir / f"{model_id}_driving_vision_metadata.pkl"
|
||||
POLICY_METADATA_PATH = model_dir / f"{model_id}_driving_policy_metadata.pkl"
|
||||
OFF_POLICY_METADATA_PATH = model_dir / f"{model_id}_driving_off_policy_metadata.pkl"
|
||||
|
||||
# If ModelVersion is not set or not available, try to determine it from available model data
|
||||
if not model_version:
|
||||
cloudlog.warning(f"ModelVersion not available for model {model_id}, attempting to determine from model data")
|
||||
try:
|
||||
# Try to get version from the model versions JSON file
|
||||
versions_file = model_dir / ".model_versions.json"
|
||||
if versions_file.is_file():
|
||||
import json
|
||||
with open(versions_file, "r") as f:
|
||||
version_map = json.load(f)
|
||||
if model_id in version_map:
|
||||
model_version = version_map[model_id]
|
||||
cloudlog.warning(f"Determined model version from JSON: {model_version}")
|
||||
else:
|
||||
cloudlog.error("Model versions JSON file not found, defaulting to v8")
|
||||
model_version = "v8"
|
||||
except Exception as e:
|
||||
cloudlog.error(f"Failed to determine model version: {e}, defaulting to v8")
|
||||
model_version = "v8"
|
||||
|
||||
try:
|
||||
with open(VISION_METADATA_PATH, 'rb') as f:
|
||||
vision_metadata = pickle.load(f)
|
||||
except FileNotFoundError:
|
||||
cloudlog.error(f"Missing metadata {VISION_METADATA_PATH}, downloading...")
|
||||
from openpilot.frogpilot.assets.model_manager import ModelManager
|
||||
ModelManager().download_model(model_id)
|
||||
with open(VISION_METADATA_PATH, 'rb') as f:
|
||||
vision_metadata = pickle.load(f)
|
||||
self.vision_input_shapes = vision_metadata['input_shapes']
|
||||
self.vision_input_names = list(self.vision_input_shapes.keys())
|
||||
self.vision_output_slices = vision_metadata['output_slices']
|
||||
vision_output_size = vision_metadata['output_shapes']['outputs'][1]
|
||||
|
||||
try:
|
||||
with open(POLICY_METADATA_PATH, 'rb') as f:
|
||||
policy_metadata = pickle.load(f)
|
||||
except FileNotFoundError:
|
||||
cloudlog.error(f"Missing metadata {POLICY_METADATA_PATH}, downloading...")
|
||||
from openpilot.frogpilot.assets.model_manager import ModelManager
|
||||
ModelManager().download_model(model_id)
|
||||
with open(POLICY_METADATA_PATH, 'rb') as f:
|
||||
policy_metadata = pickle.load(f)
|
||||
self.policy_input_shapes = policy_metadata['input_shapes']
|
||||
self.policy_output_slices = policy_metadata['output_slices']
|
||||
policy_output_size = policy_metadata['output_shapes']['outputs'][1]
|
||||
# Add policy_generation attribute after loading policy_metadata
|
||||
self.policy_generation = model_version or "v8"
|
||||
self.is_v11 = (self.policy_generation == "v11")
|
||||
self.is_v9 = (self.policy_generation == "v9")
|
||||
self.mlsim = (self.policy_generation in ("v8", "v10", "v11", "v12"))
|
||||
|
||||
self.frames = {name: DrivingModelFrame(context, ModelConstants.TEMPORAL_SKIP) for name in self.vision_input_names}
|
||||
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
|
||||
|
||||
self.full_prev_desired_curv = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
|
||||
self.temporal_idxs = slice(-1-(ModelConstants.TEMPORAL_SKIP*(ModelConstants.FULL_HISTORY_BUFFER_LEN-1)), None, ModelConstants.TEMPORAL_SKIP)
|
||||
self.full_features_buffer = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
|
||||
self.full_desire = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32)
|
||||
self.temporal_idxs = slice(-1-(ModelConstants.TEMPORAL_SKIP*(ModelConstants.INPUT_HISTORY_BUFFER_LEN-1)), None, ModelConstants.TEMPORAL_SKIP)
|
||||
|
||||
|
||||
# policy inputs (built dynamically to support all generations)
|
||||
self.numpy_inputs, self.prev_desired_curv_key = self._build_policy_inputs(self.policy_input_shapes)
|
||||
|
||||
# Off-policy model (optional)
|
||||
self.off_policy_enabled = False
|
||||
self.off_policy_input_shapes: dict[str, tuple[int, ...]] = {}
|
||||
self.off_policy_output_slices: dict[str, slice] = {}
|
||||
self.off_policy_numpy_inputs: dict[str, np.ndarray] = {}
|
||||
self.off_policy_prev_desired_curv_key: str | None = None
|
||||
self.off_policy_desire_key: str | None = None
|
||||
self.off_policy_inputs: dict[str, Tensor] | None = None
|
||||
self.off_policy_output: np.ndarray | None = None
|
||||
|
||||
off_policy_metadata = None
|
||||
if self.policy_generation == "v12" or OFF_POLICY_METADATA_PATH.is_file() or OFF_POLICY_PKL_PATH.is_file():
|
||||
try:
|
||||
with open(OFF_POLICY_METADATA_PATH, 'rb') as f:
|
||||
off_policy_metadata = pickle.load(f)
|
||||
except FileNotFoundError:
|
||||
cloudlog.error(f"Missing metadata {OFF_POLICY_METADATA_PATH}, downloading...")
|
||||
from openpilot.frogpilot.assets.model_manager import ModelManager
|
||||
ModelManager().download_model(model_id)
|
||||
try:
|
||||
with open(OFF_POLICY_METADATA_PATH, 'rb') as f:
|
||||
off_policy_metadata = pickle.load(f)
|
||||
except FileNotFoundError:
|
||||
cloudlog.warning(f"Off-policy metadata still missing: {OFF_POLICY_METADATA_PATH}")
|
||||
|
||||
if off_policy_metadata is not None:
|
||||
self.off_policy_input_shapes = off_policy_metadata['input_shapes']
|
||||
self.off_policy_output_slices = off_policy_metadata['output_slices']
|
||||
off_policy_output_size = off_policy_metadata['output_shapes']['outputs'][1]
|
||||
self.off_policy_numpy_inputs, self.off_policy_prev_desired_curv_key = self._build_policy_inputs(self.off_policy_input_shapes)
|
||||
self.off_policy_desire_key = next((k for k in self.off_policy_numpy_inputs if k.startswith('desire')), None)
|
||||
self.off_policy_inputs = {k: Tensor(v, device='NPY').realize() for k, v in self.off_policy_numpy_inputs.items()}
|
||||
self.off_policy_output = np.zeros(off_policy_output_size, dtype=np.float32)
|
||||
try:
|
||||
with open(OFF_POLICY_PKL_PATH, "rb") as f:
|
||||
self.off_policy_run = pickle.load(f)
|
||||
self.off_policy_enabled = True
|
||||
except FileNotFoundError:
|
||||
cloudlog.warning(f"Missing off-policy model {OFF_POLICY_PKL_PATH}, skipping off-policy")
|
||||
|
||||
# Optional temporal buffer for previous desired curvature (allocate only if any model expects it)
|
||||
if self.prev_desired_curv_key is not None or self.off_policy_prev_desired_curv_key is not None:
|
||||
self.full_prev_desired_curv = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
|
||||
|
||||
# policy inputs
|
||||
self.numpy_inputs = {k: np.zeros(self.policy_input_shapes[k], dtype=np.float32) for k in self.policy_input_shapes}
|
||||
self.full_input_queues = InputQueues(ModelConstants.MODEL_CONTEXT_FREQ, ModelConstants.MODEL_RUN_FREQ, ModelConstants.N_FRAMES)
|
||||
for k in [self.desire_type, 'features_buffer']:
|
||||
self.full_input_queues.update_dtypes_and_shapes({k: self.numpy_inputs[k].dtype}, {k: self.numpy_inputs[k].shape})
|
||||
self.full_input_queues.reset()
|
||||
|
||||
# img buffers are managed in openCL transform code
|
||||
self.vision_inputs: dict[str, Tensor] = {}
|
||||
self.vision_output = np.zeros(vision_output_size, dtype=np.float32)
|
||||
self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
|
||||
self.policy_output = np.zeros(policy_output_size, dtype=np.float32)
|
||||
self.parser = Parser(ignore_missing=True)
|
||||
self.parser = Parser()
|
||||
self.off_policy_parser = Parser(ignore_missing=True)
|
||||
|
||||
with open(MODELS_PATH / f'{model}_driving_vision_tinygrad.pkl', "rb") as f:
|
||||
with open(VISION_PKL_PATH, "rb") as f:
|
||||
self.vision_run = pickle.load(f)
|
||||
|
||||
with open(MODELS_PATH / f'{model}_driving_policy_tinygrad.pkl', "rb") as f:
|
||||
with open(POLICY_PKL_PATH, "rb") as f:
|
||||
self.policy_run = pickle.load(f)
|
||||
|
||||
@property
|
||||
def desire_key(self) -> str:
|
||||
return next(key for key in self.numpy_inputs if key.startswith('desire'))
|
||||
|
||||
def slice_outputs(self, model_outputs: np.ndarray, output_slices: dict[str, slice]) -> dict[str, np.ndarray]:
|
||||
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in output_slices.items()}
|
||||
return parsed_model_outputs
|
||||
@@ -197,15 +275,32 @@ class ModelState:
|
||||
def run(self, bufs: dict[str, VisionBuf], transforms: dict[str, np.ndarray],
|
||||
inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None:
|
||||
# Model decides when action is completed, so desire input is just a pulse triggered on rising edge
|
||||
inputs[self.desire_type][0] = 0
|
||||
new_desire = np.where(inputs[self.desire_type] - self.prev_desire > .99, inputs[self.desire_type], 0)
|
||||
self.prev_desire[:] = inputs[self.desire_type]
|
||||
inputs[self.desire_key][0] = 0
|
||||
new_desire = np.where(inputs[self.desire_key] - self.prev_desire > .99, inputs[self.desire_key], 0)
|
||||
self.prev_desire[:] = inputs[self.desire_key]
|
||||
|
||||
if self.use_lateral_control_params:
|
||||
self.full_desire[0,:-1] = self.full_desire[0,1:]
|
||||
self.full_desire[0,-1] = new_desire
|
||||
self.numpy_inputs[self.desire_key][:] = self.full_desire.reshape((1,ModelConstants.INPUT_HISTORY_BUFFER_LEN,ModelConstants.TEMPORAL_SKIP,-1)).max(axis=2)
|
||||
if self.off_policy_enabled and self.off_policy_desire_key is not None:
|
||||
self.off_policy_numpy_inputs[self.off_policy_desire_key][:] = self.numpy_inputs[self.desire_key]
|
||||
|
||||
if 'traffic_convention' in self.numpy_inputs:
|
||||
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
|
||||
if self.off_policy_enabled and 'traffic_convention' in self.off_policy_numpy_inputs:
|
||||
self.off_policy_numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
|
||||
|
||||
if 'lateral_control_params' in self.numpy_inputs:
|
||||
self.numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
|
||||
if self.off_policy_enabled and 'lateral_control_params' in self.off_policy_numpy_inputs:
|
||||
self.off_policy_numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
|
||||
|
||||
if prepare_only:
|
||||
return None
|
||||
|
||||
imgs_cl = {name: self.frames[name].prepare(bufs[name], transforms[name].flatten()) for name in self.vision_input_names}
|
||||
|
||||
if TICI and not USBGPU:
|
||||
if TICI:
|
||||
# The imgs tensors are backed by opencl memory, only need init once
|
||||
for key in imgs_cl:
|
||||
if key not in self.vision_inputs:
|
||||
@@ -215,54 +310,66 @@ class ModelState:
|
||||
frame_input = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.vision_input_shapes[key])
|
||||
self.vision_inputs[key] = Tensor(frame_input, dtype=dtypes.uint8).realize()
|
||||
|
||||
if prepare_only:
|
||||
return None
|
||||
|
||||
self.vision_output = self.vision_run(**self.vision_inputs).contiguous().realize().uop.base.buffer.numpy()
|
||||
vision_outputs_dict = self.parser.parse_vision_outputs(self.slice_outputs(self.vision_output, self.vision_output_slices))
|
||||
|
||||
self.full_input_queues.enqueue({'features_buffer': vision_outputs_dict['hidden_state'], self.desire_type: new_desire})
|
||||
for k in [self.desire_type, 'features_buffer']:
|
||||
self.numpy_inputs[k][:] = self.full_input_queues.get(k)[k]
|
||||
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
|
||||
self.full_features_buffer[0,:-1] = self.full_features_buffer[0,1:]
|
||||
self.full_features_buffer[0,-1] = vision_outputs_dict['hidden_state'][0, :]
|
||||
if 'features_buffer' in self.numpy_inputs:
|
||||
self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs]
|
||||
if self.off_policy_enabled and 'features_buffer' in self.off_policy_numpy_inputs:
|
||||
self.off_policy_numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs]
|
||||
|
||||
self.policy_output = self.policy_run(**self.policy_inputs).contiguous().realize().uop.base.buffer.numpy()
|
||||
policy_outputs_dict = self.parser.parse_policy_outputs(self.slice_outputs(self.policy_output, self.policy_output_slices))
|
||||
|
||||
if self.use_lateral_control_params:
|
||||
# TODO model only uses last value now
|
||||
# TODO model only uses last value now
|
||||
if hasattr(self, 'full_prev_desired_curv') and 'desired_curvature' in policy_outputs_dict:
|
||||
self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:]
|
||||
self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :]
|
||||
self.numpy_inputs['prev_desired_curv'][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs]
|
||||
|
||||
if self.prev_desired_curv_key is not None:
|
||||
# v9 models expect zeros for prev_desired_curv(s); others use history
|
||||
if self.is_v9:
|
||||
self.numpy_inputs[self.prev_desired_curv_key][:] = 0 * self.full_prev_desired_curv[0, self.temporal_idxs]
|
||||
else:
|
||||
self.numpy_inputs[self.prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
|
||||
|
||||
if self.off_policy_enabled and self.off_policy_prev_desired_curv_key is not None:
|
||||
if self.is_v9:
|
||||
self.off_policy_numpy_inputs[self.off_policy_prev_desired_curv_key][:] = 0 * self.full_prev_desired_curv[0, self.temporal_idxs]
|
||||
else:
|
||||
self.off_policy_numpy_inputs[self.off_policy_prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
|
||||
|
||||
combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict}
|
||||
if self.off_policy_enabled:
|
||||
self.off_policy_output = self.off_policy_run(**self.off_policy_inputs).contiguous().realize().uop.base.buffer.numpy()
|
||||
off_policy_outputs_dict = self.off_policy_parser.parse_policy_outputs(
|
||||
self.slice_outputs(self.off_policy_output, self.off_policy_output_slices)
|
||||
)
|
||||
combined_outputs_dict.update(off_policy_outputs_dict)
|
||||
if SEND_RAW_PRED:
|
||||
combined_outputs_dict['raw_pred'] = np.concatenate([self.vision_output.copy(), self.policy_output.copy()])
|
||||
raw_pred = [self.vision_output.copy(), self.policy_output.copy()]
|
||||
if self.off_policy_enabled and self.off_policy_output is not None:
|
||||
raw_pred.append(self.off_policy_output.copy())
|
||||
combined_outputs_dict['raw_pred'] = np.concatenate(raw_pred)
|
||||
|
||||
return combined_outputs_dict
|
||||
|
||||
|
||||
def main(demo=False):
|
||||
# FrogPilot variables
|
||||
frogpilot_toggles = get_frogpilot_toggles()
|
||||
cloudlog.warning("modeld init")
|
||||
|
||||
model_name = frogpilot_toggles.model
|
||||
model_version = frogpilot_toggles.model_version
|
||||
use_curvature_from_plan = frogpilot_toggles.model_version != "v7"
|
||||
sentry.set_tag("daemon", PROCESS_NAME)
|
||||
cloudlog.bind(daemon=PROCESS_NAME)
|
||||
setproctitle(PROCESS_NAME)
|
||||
config_realtime_process(7, 54)
|
||||
|
||||
cloudlog.warning("tinygrad_modeld init")
|
||||
|
||||
if not USBGPU:
|
||||
# USB GPU currently saturates a core so can't do this yet,
|
||||
# also need to move the aux USB interrupts for good timings
|
||||
config_realtime_process(7, 54)
|
||||
|
||||
st = time.monotonic()
|
||||
cloudlog.warning("setting up CL context")
|
||||
cl_context = CLContext()
|
||||
cloudlog.warning("CL context ready; loading model")
|
||||
model = ModelState(cl_context, model_name)
|
||||
cloudlog.warning(f"models loaded in {time.monotonic() - st:.1f}s, tinygrad_modeld starting")
|
||||
model = ModelState(cl_context)
|
||||
cloudlog.warning("models loaded, modeld starting")
|
||||
|
||||
# visionipc clients
|
||||
while True:
|
||||
@@ -295,7 +402,7 @@ def main(demo=False):
|
||||
params = Params()
|
||||
|
||||
# setup filter to track dropped frames
|
||||
frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_RUN_FREQ)
|
||||
frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_FREQ)
|
||||
frame_id = 0
|
||||
last_vipc_frame_id = 0
|
||||
run_count = 0
|
||||
@@ -322,6 +429,9 @@ def main(demo=False):
|
||||
|
||||
DH = DesireHelper()
|
||||
|
||||
# FrogPilot variables
|
||||
frogpilot_toggles = get_frogpilot_toggles()
|
||||
|
||||
while True:
|
||||
# Keep receiving frames until we are at least 1 frame ahead of previous extra frame
|
||||
while meta_main.timestamp_sof < meta_extra.timestamp_sof + 25000000:
|
||||
@@ -391,11 +501,14 @@ def main(demo=False):
|
||||
|
||||
bufs = {name: buf_extra if 'big' in name else buf_main for name in model.vision_input_names}
|
||||
transforms = {name: model_transform_extra if 'big' in name else model_transform_main for name in model.vision_input_names}
|
||||
|
||||
inputs:dict[str, np.ndarray] = {
|
||||
model.desire_type: vec_desire,
|
||||
model.desire_key: vec_desire,
|
||||
'traffic_convention': traffic_convention,
|
||||
**({'lateral_control_params': lateral_control_params} if model.use_lateral_control_params else {}),
|
||||
}
|
||||
# Include optional inputs only if the loaded model expects them
|
||||
if 'lateral_control_params' in model.numpy_inputs:
|
||||
inputs['lateral_control_params'] = lateral_control_params
|
||||
|
||||
mt1 = time.perf_counter()
|
||||
model_output = model.run(bufs, transforms, inputs, prepare_only)
|
||||
@@ -408,7 +521,7 @@ def main(demo=False):
|
||||
drivingdata_send = messaging.new_message('drivingModelData')
|
||||
posenet_send = messaging.new_message('cameraOdometry')
|
||||
|
||||
action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego, use_curvature_from_plan)
|
||||
action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego, model.mlsim, model.is_v9, frogpilot_toggles)
|
||||
prev_action = action
|
||||
fill_model_msg(drivingdata_send, modelv2_send, model_output, action,
|
||||
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
|
||||
@@ -432,7 +545,7 @@ def main(demo=False):
|
||||
pm.send('cameraOdometry', posenet_send)
|
||||
last_vipc_frame_id = meta_main.frame_id
|
||||
|
||||
# Update FrogPilot variables
|
||||
# Update FrogPilot parameters
|
||||
if sm['frogpilotPlan'].togglesUpdated:
|
||||
frogpilot_toggles = get_frogpilot_toggles()
|
||||
|
||||
@@ -444,4 +557,7 @@ if __name__ == "__main__":
|
||||
args = parser.parse_args()
|
||||
main(demo=args.demo)
|
||||
except KeyboardInterrupt:
|
||||
cloudlog.warning("got SIGINT")
|
||||
cloudlog.warning(f"child {PROCESS_NAME} got SIGINT")
|
||||
except Exception:
|
||||
sentry.capture_exception()
|
||||
raise
|
||||
|
||||
@@ -0,0 +1,766 @@
|
||||
#include "frogpilot/ui/qt/offroad/expandable_multi_option_dialog.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QScrollBar>
|
||||
#include <QTimer>
|
||||
#include <QHBoxLayout>
|
||||
#include <QSpacerItem>
|
||||
#include <QLayout>
|
||||
#include <QLayoutItem>
|
||||
#include <QGridLayout>
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
#include <QSizePolicy>
|
||||
#include <QSet>
|
||||
#include <QVector>
|
||||
#include <QSignalBlocker>
|
||||
#include <QScroller>
|
||||
#include <QPointer>
|
||||
#include <QObject>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||
|
||||
ExpandableMultiOptionDialog::ExpandableMultiOptionDialog(const QString &prompt_text,
|
||||
const QMap<QString, QStringList> &seriesToModels,
|
||||
const QString ¤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"(
|
||||
QFrame { background-color: #1B1B1B; }
|
||||
QPushButton {
|
||||
height: 135;
|
||||
padding: 0px 50px;
|
||||
text-align: left;
|
||||
font-size: 55px;
|
||||
font-weight: 300;
|
||||
border-radius: 10px;
|
||||
background-color: #4F4F4F;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
QPushButton.model-option:checked {
|
||||
background-color: #465BEA !important;
|
||||
border: 3px solid #FFFFFF !important;
|
||||
color: white !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
QPushButton:hover { background-color: #5A5A5A; }
|
||||
QPushButton.model-option:checked:hover { background-color: #5A6BEA; }
|
||||
QPushButton:pressed {
|
||||
background-color: #3049F4;
|
||||
}
|
||||
QPushButton.model-option:checked:pressed {
|
||||
background-color: #3049F4;
|
||||
border: 3px solid #CCCCCC;
|
||||
}
|
||||
QPushButton.series-header {
|
||||
background-color: #333333;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
padding-left: 80px;
|
||||
}
|
||||
QPushButton.series-header:hover { background-color: #404040; }
|
||||
QPushButton.favorite-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-size: 60px;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
min-width: 80px;
|
||||
max-width: 80px;
|
||||
}
|
||||
QPushButton.favorite-button:hover { background-color: #404040; }
|
||||
QComboBox {
|
||||
background-color: #4F4F4F;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
font-size: 50px;
|
||||
color: white;
|
||||
min-width: 200px;
|
||||
}
|
||||
QComboBox:hover { background-color: #5A5A5A; }
|
||||
QComboBox::drop-down {
|
||||
border: none;
|
||||
width: 50px;
|
||||
}
|
||||
QComboBox::down-arrow {
|
||||
image: url("../../frogpilot/assets/toggle_icons/icon_dropdown.png");
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
QComboBox QAbstractItemView {
|
||||
background-color: #4F4F4F;
|
||||
border: 2px solid #FFFFFF;
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
selection-background-color: #465BEA;
|
||||
font-size: 50px;
|
||||
}
|
||||
)");
|
||||
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(container);
|
||||
main_layout->setContentsMargins(55, 50, 55, 50);
|
||||
|
||||
QLabel *title = new QLabel(prompt_text, this);
|
||||
title->setStyleSheet("font-size: 70px; font-weight: 500;");
|
||||
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
|
||||
main_layout->addSpacing(25);
|
||||
|
||||
// Sort controls - simple cycling button
|
||||
QHBoxLayout *sortLayout = new QHBoxLayout();
|
||||
sortLayout->setContentsMargins(0, 0, 0, 0);
|
||||
sortLayout->setSpacing(20);
|
||||
sortLayout->addStretch(); // Push to the right
|
||||
|
||||
QLabel *sortLabel = new QLabel(tr("Sort by:"), this);
|
||||
sortLabel->setStyleSheet("font-size: 50px; color: white;");
|
||||
sortLayout->addWidget(sortLabel);
|
||||
|
||||
QPushButton *sortButton = new QPushButton(tr("Alphabetical"), this);
|
||||
sortButton->setStyleSheet(R"(
|
||||
QPushButton {
|
||||
background-color: #4F4F4F;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 10px;
|
||||
padding: 10px 20px;
|
||||
font-size: 50px;
|
||||
color: white;
|
||||
min-width: 250px;
|
||||
text-align: center;
|
||||
}
|
||||
QPushButton:hover { background-color: #5A5A5A; }
|
||||
)");
|
||||
|
||||
// Set initial button text based on sort mode
|
||||
if (currentSortMode == "date") {
|
||||
sortButton->setText(tr("Date (Newest)"));
|
||||
} else if (currentSortMode == "date_oldest") {
|
||||
sortButton->setText(tr("Date (Oldest)"));
|
||||
} else if (currentSortMode == "favorites") {
|
||||
sortButton->setText(tr("Favorites First"));
|
||||
} else {
|
||||
sortButton->setText(tr("Alphabetical"));
|
||||
}
|
||||
|
||||
QWidget *sortWidget = new QWidget(container);
|
||||
sortWidget->setLayout(sortLayout);
|
||||
sortWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
sortLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
sortWidget->setStyleSheet("background: transparent;");
|
||||
|
||||
sortLayout->addWidget(sortButton);
|
||||
|
||||
auto updateSortOverlayGeometry = [sortWidget, sortLayout]() {
|
||||
if (!sortWidget) return;
|
||||
const QSize hint = sortLayout->sizeHint();
|
||||
sortWidget->setFixedSize(hint);
|
||||
};
|
||||
updateSortOverlayGeometry();
|
||||
|
||||
QObject::connect(sortButton, &QPushButton::clicked, [this, sortButton, updateSortOverlayGeometry]() {
|
||||
if (currentSortMode == "alphabetical") {
|
||||
currentSortMode = "date";
|
||||
sortButton->setText(tr("Date (Newest)"));
|
||||
} else if (currentSortMode == "date") {
|
||||
currentSortMode = "date_oldest";
|
||||
sortButton->setText(tr("Date (Oldest)"));
|
||||
} else if (currentSortMode == "date_oldest") {
|
||||
currentSortMode = "favorites";
|
||||
sortButton->setText(tr("Favorites First"));
|
||||
} else {
|
||||
currentSortMode = "alphabetical";
|
||||
sortButton->setText(tr("Alphabetical"));
|
||||
}
|
||||
updateSortOverlayGeometry();
|
||||
updateSorting();
|
||||
});
|
||||
|
||||
listWidgetContainer = new QWidget(this);
|
||||
listLayout = new QVBoxLayout(listWidgetContainer);
|
||||
listLayout->setSpacing(10);
|
||||
listLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
confirmButton = new QPushButton(tr("Select"));
|
||||
confirmButton->setObjectName("confirm_btn");
|
||||
confirmButton->setEnabled(!selectionKey.isEmpty());
|
||||
|
||||
scrollView = new ScrollView(listWidgetContainer, this);
|
||||
scrollView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
if (scrollView->viewport()) {
|
||||
scrollView->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true);
|
||||
}
|
||||
|
||||
QWidget *listContainer = new QWidget(container);
|
||||
QGridLayout *overlayLayout = new QGridLayout(listContainer);
|
||||
overlayLayout->setContentsMargins(0, 0, 0, 0);
|
||||
overlayLayout->setSpacing(0);
|
||||
overlayLayout->addWidget(scrollView, 0, 0);
|
||||
overlayLayout->setRowStretch(0, 1);
|
||||
overlayLayout->setColumnStretch(0, 1);
|
||||
overlayLayout->addWidget(sortWidget, 0, 0, Qt::AlignRight | Qt::AlignTop);
|
||||
|
||||
// Create series headers and their expandable content
|
||||
rebuildModelList(seriesToModels.keys(), seriesToModels);
|
||||
|
||||
main_layout->addWidget(listContainer);
|
||||
main_layout->addSpacing(35);
|
||||
|
||||
// Cancel + confirm buttons
|
||||
QHBoxLayout *blayout = new QHBoxLayout;
|
||||
main_layout->addLayout(blayout);
|
||||
blayout->setSpacing(50);
|
||||
|
||||
QPushButton *cancel_btn = new QPushButton(tr("Cancel"));
|
||||
QObject::connect(cancel_btn, &QPushButton::clicked, this, &ConfirmationDialog::reject);
|
||||
QObject::connect(confirmButton, &QPushButton::clicked, this, &ConfirmationDialog::accept);
|
||||
blayout->addWidget(cancel_btn);
|
||||
blayout->addWidget(confirmButton);
|
||||
|
||||
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
||||
outer_layout->setContentsMargins(50, 50, 50, 50);
|
||||
outer_layout->addWidget(container);
|
||||
|
||||
// Initial sorting
|
||||
updateSorting();
|
||||
}
|
||||
|
||||
void ExpandableMultiOptionDialog::toggleSeries(const QString &series, QPushButton *headerButton) {
|
||||
if (!headerButton) return;
|
||||
|
||||
QWidget *container = seriesWidgets.value(series, nullptr);
|
||||
if (!container) return;
|
||||
|
||||
bool expanded = seriesExpanded[series];
|
||||
QString seriesName = series;
|
||||
|
||||
if (expanded) {
|
||||
container->hide();
|
||||
seriesExpanded[series] = false;
|
||||
headerButton->setText("▶ " + seriesName);
|
||||
} else {
|
||||
container->show();
|
||||
seriesExpanded[series] = true;
|
||||
headerButton->setText("▼ " + seriesName);
|
||||
|
||||
// Auto-scroll to place the series at the top of the viewport when expanded
|
||||
if (scrollView) {
|
||||
QPointer<QPushButton> headerPtr(headerButton);
|
||||
QPointer<ScrollView> scrollPtr(scrollView);
|
||||
QTimer::singleShot(50, [headerPtr, scrollPtr]() {
|
||||
if (!scrollPtr || !headerPtr) return;
|
||||
QWidget *contents = scrollPtr->widget();
|
||||
if (!contents) return;
|
||||
if (QScrollBar *vScrollBar = scrollPtr->verticalScrollBar()) {
|
||||
QPoint headerTop = headerPtr->mapTo(contents, QPoint(0, 0));
|
||||
int targetValue = qMax(headerTop.y() - 20, 0);
|
||||
vScrollBar->setValue(targetValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update the button's appearance
|
||||
headerButton->update();
|
||||
}
|
||||
|
||||
QString ExpandableMultiOptionDialog::getSelection(const QString &prompt_text,
|
||||
const QMap<QString, QStringList> &seriesToModels,
|
||||
const QString ¤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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QMap>
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
#include <QComboBox>
|
||||
#include <QMenu>
|
||||
|
||||
#include "selfdrive/ui/qt/widgets/input.h"
|
||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||
|
||||
class QPushButton;
|
||||
class ExpandableMultiOptionDialog : public DialogBase {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ExpandableMultiOptionDialog(const QString &prompt_text, const QMap<QString, QStringList> &seriesToModels,
|
||||
const QString ¤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 QStringList &userFavorites = QStringList(),
|
||||
const QStringList &communityFavorites = QStringList(),
|
||||
const QMap<QString, QString> &modelReleasedDates = QMap<QString, QString>(),
|
||||
const QMap<QString, QString> &modelFileToNameMap = QMap<QString, QString>(),
|
||||
const QString &initialSortMode = QString());
|
||||
QString selection;
|
||||
|
||||
QString getCurrentSortMode() const { return currentSortMode; }
|
||||
QStringList getUserFavorites() const;
|
||||
|
||||
private:
|
||||
void toggleSeries(const QString &series, QPushButton *headerButton);
|
||||
void toggleFavorite(const QString &modelKey);
|
||||
void updateSorting();
|
||||
void rebuildModelList(const QStringList &orderedSeries, const QMap<QString, QStringList> &newSeriesToModels);
|
||||
void createModelButton(const QString &modelKey, const QString &modelName, const QString &displayName,
|
||||
QVBoxLayout *layout);
|
||||
void refreshFavoriteIcons();
|
||||
void updateButtonStyles();
|
||||
void stopActiveScroll();
|
||||
void stopActiveScrollForInteraction();
|
||||
|
||||
QMap<QString, QStringList> seriesToModels;
|
||||
QMap<QString, QStringList> baseSeriesToModels;
|
||||
QMap<QString, QWidget*> seriesWidgets;
|
||||
QMap<QString, bool> seriesExpanded;
|
||||
QMap<QString, QList<QPushButton*>> modelButtons;
|
||||
QMap<QString, QList<QPushButton*>> favoriteButtons;
|
||||
|
||||
QStringList userFavorites;
|
||||
QStringList communityFavorites;
|
||||
QMap<QString, QString> modelReleasedDates;
|
||||
QMap<QString, QString> modelFileToNameMap;
|
||||
QMap<QString, QString> modelNameToFileMap;
|
||||
QMap<QString, QString> displayOverrides;
|
||||
|
||||
QString currentSortMode;
|
||||
QString currentSelection;
|
||||
QString currentSelectionKey;
|
||||
QString selectionKey;
|
||||
|
||||
ScrollView *scrollView = nullptr;
|
||||
QVBoxLayout *listLayout = nullptr;
|
||||
QPushButton *confirmButton = nullptr;
|
||||
QWidget *listWidgetContainer = nullptr;
|
||||
QPointer<QPushButton> currentSelectionButton;
|
||||
};
|
||||
@@ -269,6 +269,7 @@ void FrogPilotSettingsWindow::updateVariables() {
|
||||
hasPedal = CP.getEnableGasInterceptor();
|
||||
hasRadar = !CP.getRadarUnavailable();
|
||||
hasSDSU = frogpilot_toggles.value("has_sdsu").toBool();
|
||||
hasSASCM = frogpilot_toggles.value("has_sascm").toBool();
|
||||
hasSNG = hasOpenpilotLongitudinal && CP.getAutoResumeSng();
|
||||
hasZSS = frogpilot_toggles.value("has_zss").toBool();
|
||||
isAngleCar = CP.getSteerControlType() == cereal::CarParams::SteerControlType::ANGLE;
|
||||
@@ -277,20 +278,26 @@ void FrogPilotSettingsWindow::updateVariables() {
|
||||
isHKG = carMake == "hyundai";
|
||||
isHKGCanFd = isHKG && safetyModel == cereal::CarParams::SafetyModel::HYUNDAI_CANFD;
|
||||
isSubaru = carMake == "subaru";
|
||||
isTorqueCar = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::TORQUE;
|
||||
isToyota = carMake == "toyota";
|
||||
isTSK = CP.getSecOcRequired();
|
||||
isVolt = carFingerprint == "CHEVROLET_VOLT";
|
||||
longitudinalActuatorDelay = CP.getLongitudinalActuatorDelay();
|
||||
startAccel = CP.getStartAccel();
|
||||
steerActuatorDelay = CP.getSteerActuatorDelay();
|
||||
steerOffset = 0.0f;
|
||||
steerKp = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::PID ? CP.getLateralTuning().getPid().getKpV()[0] : 0.6;
|
||||
steerRatio = CP.getSteerRatio();
|
||||
stopAccel = CP.getStopAccel();
|
||||
stoppingDecelRate = CP.getStoppingDecelRate();
|
||||
vEgoStarting = CP.getVEgoStarting();
|
||||
vEgoStopping = CP.getVEgoStopping();
|
||||
friction = CP.getLateralTuning().getTorque().getFriction();
|
||||
latAccelFactor = CP.getLateralTuning().getTorque().getLatAccelFactor();
|
||||
|
||||
float currentDelayStock = params.getFloat("SteerDelayStock");
|
||||
float currentFrictionStock = params.getFloat("SteerFrictionStock");
|
||||
float currentSteerOffsetStock = params.getFloat("SteerOffsetStock");
|
||||
float currentKPStock = params.getFloat("SteerKPStock");
|
||||
float currentLatAccelStock = params.getFloat("SteerLatAccelStock");
|
||||
float currentLongDelayStock = params.getFloat("LongitudinalActuatorDelayStock");
|
||||
@@ -315,6 +322,13 @@ void FrogPilotSettingsWindow::updateVariables() {
|
||||
params.putFloat("SteerFrictionStock", friction);
|
||||
}
|
||||
|
||||
if (currentSteerOffsetStock != steerOffset) {
|
||||
if (params.getFloat("SteerOffset") == currentSteerOffsetStock) {
|
||||
params.putFloat("SteerOffset", steerOffset);
|
||||
}
|
||||
params.putFloat("SteerOffsetStock", steerOffset);
|
||||
}
|
||||
|
||||
if (currentKPStock != steerKp && steerKp != 0) {
|
||||
if (params.getFloat("SteerKP") == currentKPStock || currentKPStock == 0) {
|
||||
params.putFloat("SteerKP", steerKp);
|
||||
@@ -387,12 +401,18 @@ void FrogPilotSettingsWindow::updateVariables() {
|
||||
|
||||
canUsePedal = FPCP.getCanUsePedal();
|
||||
canUseSDSU = FPCP.getCanUseSDSU();
|
||||
friction = FPCP.getLateralTuning().getTorque().getFriction();
|
||||
hasAutoTune = (carMake == "hyundai" || carMake == "toyota") && FPCP.getLateralTuning().which() == cereal::FrogPilotCarParams::LateralTuning::TORQUE;
|
||||
isTorqueCar = FPCP.getLateralTuning().which() == cereal::FrogPilotCarParams::LateralTuning::TORQUE;
|
||||
latAccelFactor = FPCP.getLateralTuning().getTorque().getLatAccelFactor();
|
||||
canUseSASCM = FPCP.getCanUseSASCM();
|
||||
openpilotLongitudinalControlDisabled = FPCP.getOpenpilotLongitudinalControlDisabled();
|
||||
steerKp = FPCP.getLateralTuning().getTorque().getKp();
|
||||
}
|
||||
|
||||
std::string liveTorqueParameters = params.get("LiveTorqueParameters");
|
||||
if (!liveTorqueParameters.empty()) {
|
||||
AlignedBuffer aligned_buf;
|
||||
capnp::FlatArrayMessageReader reader(aligned_buf.align(liveTorqueParameters.data(), liveTorqueParameters.size()));
|
||||
cereal::Event::Reader event = reader.getRoot<cereal::Event>();
|
||||
cereal::LiveTorqueParametersData::Reader LTP = event.getLiveTorqueParameters();
|
||||
|
||||
hasAutoTune = LTP.getUseParams();
|
||||
}
|
||||
|
||||
isC3 = util::read_file("/sys/firmware/devicetree/base/model").find("tici") != std::string::npos;
|
||||
|
||||
@@ -13,6 +13,7 @@ public:
|
||||
|
||||
bool canUsePedal = false;
|
||||
bool canUseSDSU = false;
|
||||
bool canUseSASCM = false;
|
||||
bool forceOpenDescriptions = false;
|
||||
bool hasAutoTune = true;
|
||||
bool hasBSM = true;
|
||||
@@ -24,6 +25,7 @@ public:
|
||||
bool hasPedal = false;
|
||||
bool hasRadar = true;
|
||||
bool hasSDSU = false;
|
||||
bool hasSASCM = false;
|
||||
bool hasSNG = false;
|
||||
bool hasZSS = false;
|
||||
bool isAngleCar = false;
|
||||
@@ -45,6 +47,7 @@ public:
|
||||
float longitudinalActuatorDelay;
|
||||
float startAccel;
|
||||
float steerActuatorDelay;
|
||||
float steerOffset;
|
||||
float steerKp;
|
||||
float steerRatio;
|
||||
float stopAccel;
|
||||
|
||||
@@ -39,11 +39,12 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
|
||||
|
||||
const std::vector<std::tuple<QString, QString, QString, QString>> lateralToggles {
|
||||
{"AdvancedLateralTune", tr("Advanced Lateral Tuning"), tr("<b>Advanced steering control changes to fine-tune how openpilot drives.</b>"), "../../frogpilot/assets/toggle_icons/icon_advanced_lateral_tune.png"},
|
||||
{"SteerDelay", steerActuatorDelay != 0 ? QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(steerActuatorDelay, 'f', 2)) : tr("Actuator Delay"), tr("<b>The time between openpilot's steering command and the vehicle's response.</b> Increase if the vehicle reacts late; decrease if it feels jumpy. Auto-learned by default."), ""},
|
||||
{"SteerFriction", friction != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(friction, 'f', 2)) : tr("Friction"), tr("<b>Compensates for steering friction.</b> Increase if the wheel sticks near center; decrease if it jitters. Auto-learned by default."), ""},
|
||||
{"SteerKP", steerKp != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKp, 'f', 2)) : tr("Kp Factor"), tr("<b>How strongly openpilot corrects lane position.</b> Higher is tighter but twitchier; lower is smoother but slower. Auto-learned by default."), ""},
|
||||
{"SteerLatAccel", latAccelFactor != 0 ? QString(tr("Lateral Acceleration (Default: %1)")).arg(QString::number(latAccelFactor, 'f', 2)) : tr("Lateral Acceleration"), tr("<b>Maps steering torque to turning response.</b> Increase for sharper turns; decrease for gentler steering. Auto-learned by default."), ""},
|
||||
{"SteerRatio", steerRatio != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatio, 'f', 2)) : tr("Steer Ratio"), tr("<b>The relationship between steering wheel rotation and road wheel angle.</b> Increase if steering feels too quick or twitchy; decrease if it feels too slow or weak. Auto-learned by default."), ""},
|
||||
{"SteerDelay", parent->steerActuatorDelay != 0 ? QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(parent->steerActuatorDelay, 'f', 2)) : tr("Actuator Delay"), tr("<b>The time between openpilot's steering command and the vehicle's response.</b> Increase if the vehicle reacts late; decrease if it feels jumpy. Auto-learned by default."), ""},
|
||||
{"SteerFriction", parent->friction != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(parent->friction, 'f', 2)) : tr("Friction"), tr("<b>Compensates for steering friction.</b> Increase if the wheel sticks near center; decrease if it jitters. Auto-learned by default."), ""},
|
||||
{"SteerOffset", parent->steerOffset != 0 ? QString(tr("Steer Offset (Default: %1)")).arg(QString::number(parent->steerOffset, 'f', 3)) : tr("Steer Offset"), tr("<b>Offsets steering torque to help compensate for alignment or tire issues.</b> More negative pulls the car right; more positive pulls it left. Most users should not need to touch this."), ""},
|
||||
{"SteerKP", parent->steerKp != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2)) : tr("Kp Factor"), tr("<b>How strongly openpilot corrects lane position.</b> Higher is tighter but twitchier; lower is smoother but slower. Auto-learned by default."), ""},
|
||||
{"SteerLatAccel", parent->latAccelFactor != 0 ? QString(tr("Lateral Acceleration (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2)) : tr("Lateral Acceleration"), tr("<b>Maps steering torque to turning response.</b> Increase for sharper turns; decrease for gentler steering. Auto-learned by default."), ""},
|
||||
{"SteerRatio", parent->steerRatio != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(parent->steerRatio, 'f', 2)) : tr("Steer Ratio"), tr("<b>The relationship between steering wheel rotation and road wheel angle.</b> Increase if steering feels too quick or twitchy; decrease if it feels too slow or weak. Auto-learned by default."), ""},
|
||||
{"ForceAutoTune", tr("Force Auto-Tune On"), tr("<b>Force-enable openpilot's live auto-tuning for \"Friction\" and \"Lateral Acceleration\".</b>"), ""},
|
||||
{"ForceAutoTuneOff", tr("Force Auto-Tune Off"), tr("<b>Force-disable openpilot's live auto-tuning for \"Friction\" and \"Lateral Acceleration\" and use the set value instead.</b>"), ""},
|
||||
{"ForceTorqueController", tr("Force Torque Controller"), tr("<b>Use torque-based steering control instead of angle-based control for smoother lane keeping, especially in curves.</b>"), ""},
|
||||
@@ -87,15 +88,18 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
|
||||
} else if (param == "SteerFriction") {
|
||||
std::vector<QString> steerFrictionButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 0.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerFrictionButton, false, false);
|
||||
} else if (param == "SteerOffset") {
|
||||
std::vector<QString> steerOffsetButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, -0.2, 0.2, QString(), std::map<float, QString>(), 0.005, false, {}, steerOffsetButton, false, false);
|
||||
} else if (param == "SteerKP") {
|
||||
std::vector<QString> steerKPButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerKp * 0.5, steerKp * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerKPButton, false, false);
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->steerKp * 0.5, parent->steerKp * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerKPButton, false, false);
|
||||
} else if (param == "SteerLatAccel") {
|
||||
std::vector<QString> steerLatAccelButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, latAccelFactor * 0.75, latAccelFactor * 1.25, QString(), std::map<float, QString>(), 0.01, false, {}, steerLatAccelButton, false, false);
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->latAccelFactor * 0.75, parent->latAccelFactor * 1.25, QString(), std::map<float, QString>(), 0.01, false, {}, steerLatAccelButton, false, false);
|
||||
} else if (param == "SteerRatio") {
|
||||
std::vector<QString> steerRatioButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerRatio * 0.5, steerRatio * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerRatioButton, false, false);
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->steerRatio * 0.5, parent->steerRatio * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerRatioButton, false, false);
|
||||
|
||||
} else if (param == "AlwaysOnLateral") {
|
||||
FrogPilotManageControl *aolToggle = new FrogPilotManageControl(param, title, desc, icon);
|
||||
@@ -191,12 +195,6 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
|
||||
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
} else if (key == "NNFF" || key == "NNFFLite") {
|
||||
if (!isTorqueCar) {
|
||||
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
}
|
||||
} else if (key != "AlwaysOnLateral") {
|
||||
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
|
||||
Hardware::reboot();
|
||||
@@ -207,41 +205,49 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
|
||||
}
|
||||
|
||||
steerDelayToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerDelay"]);
|
||||
QObject::connect(steerDelayToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
|
||||
QObject::connect(steerDelayToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Actuator Delay</b> to its default value?"), this)) {
|
||||
params.putFloat("SteerDelay", steerActuatorDelay);
|
||||
params.putFloat("SteerDelay", parent->steerActuatorDelay);
|
||||
steerDelayToggle->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
steerFrictionToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerFriction"]);
|
||||
QObject::connect(steerFrictionToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
|
||||
QObject::connect(steerFrictionToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Friction</b> to its default value?"), this)) {
|
||||
params.putFloat("SteerFriction", friction);
|
||||
params.putFloat("SteerFriction", parent->friction);
|
||||
steerFrictionToggle->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
steerOffsetToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerOffset"]);
|
||||
QObject::connect(steerOffsetToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Steer Offset</b> to its default value?"), this)) {
|
||||
params.putFloat("SteerOffset", parent->steerOffset);
|
||||
steerOffsetToggle->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
steerKPToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerKP"]);
|
||||
QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
|
||||
QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Kp Factor</b> to its default value?"), this)) {
|
||||
params.putFloat("SteerKP", steerKp);
|
||||
params.putFloat("SteerKP", parent->steerKp);
|
||||
steerKPToggle->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
steerLatAccelToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerLatAccel"]);
|
||||
QObject::connect(steerLatAccelToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
|
||||
QObject::connect(steerLatAccelToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Lateral Accel</b> to its default value?"), this)) {
|
||||
params.putFloat("SteerLatAccel", latAccelFactor);
|
||||
params.putFloat("SteerLatAccel", parent->latAccelFactor);
|
||||
steerLatAccelToggle->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
steerRatioToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerRatio"]);
|
||||
QObject::connect(steerRatioToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
|
||||
QObject::connect(steerRatioToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Steer Ratio</b> to its default value?"), this)) {
|
||||
params.putFloat("SteerRatio", steerRatio);
|
||||
params.putFloat("SteerRatio", parent->steerRatio);
|
||||
steerRatioToggle->refresh();
|
||||
}
|
||||
});
|
||||
@@ -258,27 +264,16 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
|
||||
|
||||
void FrogPilotLateralPanel::showEvent(QShowEvent *event) {
|
||||
frogpilotToggleLevels = parent->frogpilotToggleLevels;
|
||||
friction = parent->friction;
|
||||
hasAutoTune = parent->hasAutoTune;
|
||||
hasNNFFLog = parent->hasNNFFLog;
|
||||
hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal;
|
||||
isAngleCar = parent->isAngleCar;
|
||||
isHKGCanFd = parent->isHKGCanFd;
|
||||
isTorqueCar = parent->isTorqueCar;
|
||||
latAccelFactor = parent->latAccelFactor;
|
||||
steerActuatorDelay = parent->steerActuatorDelay;
|
||||
steerKp = parent->steerKp;
|
||||
steerRatio = parent->steerRatio;
|
||||
tuningLevel = parent->tuningLevel;
|
||||
|
||||
steerDelayToggle->setTitle(QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(steerActuatorDelay, 'f', 2)));
|
||||
steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(friction, 'f', 2)));
|
||||
steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKp, 'f', 2)));
|
||||
steerKPToggle->updateControl(steerKp * 0.5, steerKp * 1.5);
|
||||
steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(latAccelFactor, 'f', 2)));
|
||||
steerLatAccelToggle->updateControl(latAccelFactor * 0.75, latAccelFactor * 1.25);
|
||||
steerRatioToggle->setTitle(QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatio, 'f', 2)));
|
||||
steerRatioToggle->updateControl(steerRatio * 0.5, steerRatio * 1.5);
|
||||
steerDelayToggle->setTitle(QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(parent->steerActuatorDelay, 'f', 2)));
|
||||
steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(parent->friction, 'f', 2)));
|
||||
steerOffsetToggle->setTitle(QString(tr("Steer Offset (Default: %1)")).arg(QString::number(parent->steerOffset, 'f', 3)));
|
||||
steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2)));
|
||||
steerKPToggle->updateControl(parent->steerKp * 0.5, parent->steerKp * 1.5);
|
||||
steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2)));
|
||||
steerLatAccelToggle->updateControl(parent->latAccelFactor * 0.75, parent->latAccelFactor * 1.25);
|
||||
steerRatioToggle->setTitle(QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(parent->steerRatio, 'f', 2)));
|
||||
steerRatioToggle->updateControl(parent->steerRatio * 0.5, parent->steerRatio * 1.5);
|
||||
|
||||
updateToggles();
|
||||
}
|
||||
@@ -358,41 +353,40 @@ void FrogPilotLateralPanel::updateToggles() {
|
||||
}
|
||||
}
|
||||
|
||||
bool forcingAutoTune = !hasAutoTune && params.getBool("ForceAutoTune");
|
||||
bool forcingAutoTuneOff = hasAutoTune && params.getBool("ForceAutoTuneOff");
|
||||
bool forcingTorqueController = !isAngleCar && params.getBool("ForceTorqueController");
|
||||
bool usingNNFF = hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
|
||||
bool forcingAutoTuneOff = parent->hasAutoTune && params.getBool("ForceAutoTuneOff");
|
||||
bool forcingTorqueController = !parent->isAngleCar && params.getBool("ForceTorqueController");
|
||||
bool usingNNFF = parent->hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
if (parentKeys.contains(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool setVisible = tuningLevel >= frogpilotToggleLevels[key].toDouble();
|
||||
bool setVisible = parent->tuningLevel >= frogpilotToggleLevels[key].toDouble();
|
||||
|
||||
if (key == "AlwaysOnLateralLKAS") {
|
||||
setVisible &= isHKGCanFd;
|
||||
setVisible &= !hasOpenpilotLongitudinal;
|
||||
setVisible &= parent->isHKGCanFd;
|
||||
setVisible &= !parent->hasOpenpilotLongitudinal;
|
||||
}
|
||||
|
||||
else if (key == "AlwaysOnLateralMain") {
|
||||
setVisible &= !isHKGCanFd;
|
||||
setVisible |= hasOpenpilotLongitudinal;
|
||||
setVisible &= !parent->isHKGCanFd;
|
||||
setVisible |= parent->hasOpenpilotLongitudinal;
|
||||
}
|
||||
|
||||
else if (key == "ForceAutoTune") {
|
||||
setVisible &= !hasAutoTune;
|
||||
setVisible &= !isAngleCar;
|
||||
setVisible &= isTorqueCar || forcingTorqueController;
|
||||
setVisible &= !parent->hasAutoTune;
|
||||
setVisible &= !parent->isAngleCar;
|
||||
setVisible &= parent->isTorqueCar || forcingTorqueController;
|
||||
}
|
||||
|
||||
else if (key == "ForceAutoTuneOff") {
|
||||
setVisible &= hasAutoTune;
|
||||
setVisible &= parent->hasAutoTune;
|
||||
}
|
||||
|
||||
else if (key == "ForceTorqueController") {
|
||||
setVisible &= !isAngleCar;
|
||||
setVisible &= !isTorqueCar;
|
||||
setVisible &= !parent->isAngleCar;
|
||||
setVisible &= !parent->isTorqueCar;
|
||||
}
|
||||
|
||||
else if (key == "LaneChangeTime") {
|
||||
@@ -404,42 +398,47 @@ void FrogPilotLateralPanel::updateToggles() {
|
||||
}
|
||||
|
||||
else if (key == "NNFF") {
|
||||
setVisible &= hasNNFFLog;
|
||||
setVisible &= !isAngleCar;
|
||||
setVisible &= parent->hasNNFFLog;
|
||||
setVisible &= !parent->isAngleCar;
|
||||
}
|
||||
|
||||
else if (key == "NNFFLite") {
|
||||
setVisible &= !usingNNFF;
|
||||
setVisible &= !isAngleCar;
|
||||
setVisible &= !parent->isAngleCar;
|
||||
}
|
||||
|
||||
else if (key == "SteerDelay") {
|
||||
setVisible &= steerActuatorDelay != 0;
|
||||
setVisible &= parent->steerActuatorDelay != 0;
|
||||
}
|
||||
|
||||
else if (key == "SteerFriction") {
|
||||
setVisible &= friction != 0;
|
||||
setVisible &= hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
|
||||
setVisible &= isTorqueCar || forcingTorqueController;
|
||||
setVisible &= parent->friction != 0;
|
||||
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
|
||||
setVisible &= parent->isTorqueCar || forcingTorqueController;
|
||||
setVisible &= !usingNNFF;
|
||||
}
|
||||
|
||||
else if (key == "SteerOffset") {
|
||||
setVisible &= parent->isGM;
|
||||
setVisible &= parent->isTorqueCar || forcingTorqueController;
|
||||
setVisible &= !usingNNFF;
|
||||
}
|
||||
|
||||
else if (key == "SteerKP") {
|
||||
setVisible &= steerKp != 0;
|
||||
setVisible &= hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
|
||||
setVisible &= isTorqueCar || forcingTorqueController;
|
||||
setVisible &= parent->steerKp != 0;
|
||||
setVisible &= !parent->isAngleCar;
|
||||
}
|
||||
|
||||
else if (key == "SteerLatAccel") {
|
||||
setVisible &= latAccelFactor != 0;
|
||||
setVisible &= hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
|
||||
setVisible &= isTorqueCar || forcingTorqueController;
|
||||
setVisible &= parent->latAccelFactor != 0;
|
||||
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
|
||||
setVisible &= parent->isTorqueCar || forcingTorqueController;
|
||||
setVisible &= !usingNNFF;
|
||||
}
|
||||
|
||||
else if (key == "SteerRatio") {
|
||||
setVisible &= steerRatio != 0;
|
||||
setVisible &= hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
|
||||
setVisible &= parent->steerRatio != 0;
|
||||
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
|
||||
}
|
||||
|
||||
toggle->setVisible(setVisible);
|
||||
|
||||
@@ -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,39 +1,15 @@
|
||||
#include "frogpilot/ui/qt/offroad/model_settings.h"
|
||||
|
||||
bool hasAllTinygradFiles(const QDir &modelDir, const QString &modelKey) {
|
||||
QStringList tinygradSuffixes = {
|
||||
"_driving_policy_metadata.pkl",
|
||||
"_driving_policy_tinygrad.pkl",
|
||||
"_driving_vision_metadata.pkl",
|
||||
"_driving_vision_tinygrad.pkl"
|
||||
};
|
||||
|
||||
for (const QString &suffix : tinygradSuffixes) {
|
||||
if (!modelDir.exists(modelKey + suffix)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString normalizeModelKey(QString key) {
|
||||
key = key.toLower();
|
||||
if (key.endsWith("_default")) {
|
||||
key.chop(QString("_default").size());
|
||||
}
|
||||
return key;
|
||||
}
|
||||
#include "frogpilot/ui/qt/offroad/expandable_multi_option_dialog.h"
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QPushButton>
|
||||
#include <QDialog>
|
||||
#include <algorithm>
|
||||
|
||||
FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
QJsonObject shownDescriptions = QJsonDocument::fromJson(QString::fromStdString(params.get("ShownToggleDescriptions")).toUtf8()).object();
|
||||
QString className = this->metaObject()->className();
|
||||
|
||||
if (!shownDescriptions.value(className).toBool(false)) {
|
||||
forceOpenDescriptions = true;
|
||||
shownDescriptions.insert(className, true);
|
||||
params.put("ShownToggleDescriptions", QJsonDocument(shownDescriptions).toJson(QJsonDocument::Compact).toStdString());
|
||||
}
|
||||
|
||||
QStackedLayout *modelLayout = new QStackedLayout();
|
||||
addItem(modelLayout);
|
||||
|
||||
@@ -50,58 +26,92 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
modelLayout->addWidget(modelLabelsPanel);
|
||||
|
||||
const std::vector<std::tuple<QString, QString, QString, QString>> modelToggles {
|
||||
{"AutomaticallyDownloadModels", tr("Automatically Download New Models"), tr("<b>Automatically download new driving models</b> as they become available."), ""},
|
||||
{"DeleteModel", tr("Delete Driving Models"), tr("<b>Delete downloaded driving models</b> to free up storage space."), ""},
|
||||
{"DownloadModel", tr("Download Driving Models"), tr("<b>Manually download driving models</b> to the device."), ""},
|
||||
{"ModelRandomizer", tr("Model Randomizer"), tr("<b>Select a random driving model each drive</b> and use feedback prompts at the end of the drive to help find the model that best suits you!"), ""},
|
||||
{"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("<b>Add or remove driving models from the \"Model Randomizer\" blacklist.</b>"), ""},
|
||||
{"ManageScores", tr("Manage Model Ratings"), tr("<b>View or reset saved model ratings</b> used by the \"Model Randomizer\"."), ""},
|
||||
{"SelectModel", tr("Select Driving Model"), tr("<b>Choose which driving model openpilot uses.</b>"), ""},
|
||||
{"UpdateTinygrad", tr("Update Model Manager"), tr("<b>Update the \"Model Manager\"</b> to support the latest models."), ""}
|
||||
{"AutomaticallyDownloadModels", tr("Automatically Download New Models"), tr("Automatically download new driving models as they become available."), ""},
|
||||
{"DeleteModel", tr("Delete Driving Models"), tr("Delete driving models from the device."), ""},
|
||||
{"DownloadModel", tr("Download Driving Models"), tr("Download driving models to the device."), ""},
|
||||
{"ModelRandomizer", tr("Model Randomizer"), tr("Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs."), ""},
|
||||
{"RecoveryPower", tr("Recovery Power"), tr("Adjust the strength of planplus lane recovery corrections (0.5 to 2.0)."), ""},
|
||||
{"StopDistance", tr("Stop Distance"), tr("Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6."), ""},
|
||||
{"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("Add or remove models from the <b>Model Randomizer</b>'s blacklist list."), ""},
|
||||
{"ManageScores", tr("Manage Model Ratings"), tr("Reset or view the saved ratings for the driving models."), ""},
|
||||
{"SelectModel", tr("Select Driving Model"), tr("Select the active driving model."), ""},
|
||||
};
|
||||
|
||||
FrogPilotParamValueButtonControl *recoveryPowerToggle = nullptr;
|
||||
FrogPilotParamValueButtonControl *stopDistanceToggle = nullptr;
|
||||
|
||||
for (const auto &[param, title, desc, icon] : modelToggles) {
|
||||
AbstractControl *modelToggle;
|
||||
|
||||
if (param == "DeleteModel") {
|
||||
deleteModelButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DELETE"), tr("DELETE ALL")});
|
||||
QObject::connect(deleteModelButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
QStringList deletableModels;
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QString base = QFileInfo(file).baseName();
|
||||
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
|
||||
if (base.startsWith(modelKey)) {
|
||||
QString modelName = modelFileToNameMapProcessed.value(modelKey);
|
||||
if (!deletableModels.contains(modelName)) {
|
||||
deletableModels.append(modelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
QMap<QString, QString> deletableModelsMap = getDeletableModelDisplayNames();
|
||||
noModelsDownloaded = deletableModelsMap.isEmpty();
|
||||
|
||||
if (noModelsDownloaded) {
|
||||
return;
|
||||
}
|
||||
deletableModels.removeAll(processModelName(currentModel));
|
||||
deletableModels.removeAll(modelFileToNameMapProcessed.value(normalizeModelKey(QString::fromStdString(params_default.get("Model")))));
|
||||
noModelsDownloaded = deletableModels.isEmpty();
|
||||
|
||||
if (id == 0) {
|
||||
QString modelToDelete = MultiOptionDialog::getSelection(tr("Select a driving model to delete"), deletableModels, "", this);
|
||||
if (!modelToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the \"%1\" model?").arg(modelToDelete), tr("Delete"), this)) {
|
||||
QString modelFile = modelFileToNameMapProcessed.key(modelToDelete);
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QString base = QFileInfo(file).baseName();
|
||||
if (base.startsWith(modelFile)) {
|
||||
QFile::remove(modelDir.filePath(file));
|
||||
// Group deletable models by series and keep a lookup for selected names
|
||||
QMap<QString, QStringList> deletableSeriesToModels;
|
||||
QMap<QString, QString> displayNameToKey;
|
||||
QMap<QString, QString> deletableFileToNameMap;
|
||||
for (auto it = deletableModelsMap.constBegin(); it != deletableModelsMap.constEnd(); ++it) {
|
||||
const QString &modelKey = it.key();
|
||||
const QString &displayName = it.value();
|
||||
QString series = modelSeriesMap.value(modelKey, tr("Custom Series"));
|
||||
deletableSeriesToModels[series].append(displayName);
|
||||
displayNameToKey.insert(displayName, modelKey);
|
||||
deletableFileToNameMap.insert(modelKey, displayName);
|
||||
}
|
||||
|
||||
// Sort models within each series
|
||||
for (QString &series : deletableSeriesToModels.keys()) {
|
||||
QStringList &models = deletableSeriesToModels[series];
|
||||
models.removeDuplicates();
|
||||
std::sort(models.begin(), models.end());
|
||||
}
|
||||
|
||||
QString savedSortMode = QString::fromStdString(params.get("ModelSortMode"));
|
||||
if (savedSortMode.isEmpty()) savedSortMode = "alphabetical";
|
||||
|
||||
QString modelToDelete = ExpandableMultiOptionDialog::getSelection(tr("Select a driving model to delete"), deletableSeriesToModels, "", this,
|
||||
QStringList(), QStringList(), QMap<QString, QString>(),
|
||||
deletableFileToNameMap, savedSortMode);
|
||||
if (!modelToDelete.isEmpty()) {
|
||||
QString modelKey = displayNameToKey.value(modelToDelete);
|
||||
if (modelKey.isEmpty()) {
|
||||
QString processedName = processModelName(modelToDelete);
|
||||
for (auto it = deletableModelsMap.constBegin(); it != deletableModelsMap.constEnd(); ++it) {
|
||||
if (processModelName(it.value()) == processedName) {
|
||||
modelKey = it.key();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allModelsDownloaded = false;
|
||||
if (!modelKey.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the \"%1\" model?").arg(modelToDelete), tr("Delete"), this)) {
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QString base = QFileInfo(file).baseName();
|
||||
if (base.startsWith(modelKey)) {
|
||||
QFile::remove(modelDir.filePath(file));
|
||||
}
|
||||
}
|
||||
|
||||
allModelsDownloaded = false;
|
||||
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
|
||||
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
|
||||
}
|
||||
}
|
||||
} else if (id == 1) {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete all of your downloaded driving models?"), tr("Delete"), this)) {
|
||||
const QList<QString> deletableKeys = deletableModelsMap.keys();
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QString base = QFileInfo(file).baseName();
|
||||
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
|
||||
QString modelName = modelFileToNameMapProcessed.value(modelKey);
|
||||
if (deletableModels.contains(modelName) && base.startsWith(modelKey)) {
|
||||
for (const QString &modelKey : deletableKeys) {
|
||||
if (base.startsWith(modelKey)) {
|
||||
QFile::remove(modelDir.filePath(file));
|
||||
break;
|
||||
}
|
||||
@@ -110,6 +120,7 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
|
||||
allModelsDownloaded = false;
|
||||
noModelsDownloaded = true;
|
||||
deleteModelButton->setEnabled(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -117,45 +128,96 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
} else if (param == "DownloadModel") {
|
||||
downloadModelButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DOWNLOAD"), tr("DOWNLOAD ALL")});
|
||||
QObject::connect(downloadModelButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
if (tinygradUpdate) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Tinygrad is out of date and must be updated before you can download new models. Update now?"), this)) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Updating Tinygrad will delete all existing Tinygrad-based models which will need to be re-downloaded. Proceed?"), this)) {
|
||||
params_memory.putBool("UpdateTinygrad", true);
|
||||
params_memory.put("ModelDownloadProgress", "Downloading...");
|
||||
|
||||
updateTinygradButton->setText(0, tr("CANCEL"));
|
||||
updateTinygradButton->setValue(tr("Updating..."));
|
||||
|
||||
updatingTinygrad = true;
|
||||
}
|
||||
}
|
||||
} else if (id == 0) {
|
||||
if (id == 0) {
|
||||
if (modelDownloading) {
|
||||
params_memory.putBool("CancelModelDownload", true);
|
||||
|
||||
cancellingDownload = true;
|
||||
} else {
|
||||
QStringList downloadableModels = availableModelNames;
|
||||
for (const QString &modelKey : modelFileToNameMap.keys()) {
|
||||
QString modelName = modelFileToNameMap.value(modelKey);
|
||||
if (modelDir.exists(modelKey + ".thneed") || hasAllTinygradFiles(modelDir, modelKey)) {
|
||||
downloadableModels.removeAll(modelName);
|
||||
}
|
||||
} else {
|
||||
QMap<QString, QStringList> downloadableSeriesToModels;
|
||||
QStringList downloadableModelNames;
|
||||
|
||||
for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
|
||||
const QString &modelKey = it.key();
|
||||
const QString &modelName = it.value();
|
||||
if (modelName.isEmpty() || isModelInstalled(modelKey)) {
|
||||
continue;
|
||||
}
|
||||
allModelsDownloaded = downloadableModels.isEmpty();
|
||||
|
||||
QString modelToDownload = MultiOptionDialog::getSelection(tr("Select a driving model to download"), downloadableModels, "", this);
|
||||
QString series = modelSeriesMap.value(modelKey, tr("Custom Series"));
|
||||
downloadableSeriesToModels[series].append(modelName);
|
||||
if (!downloadableModelNames.contains(modelName)) {
|
||||
downloadableModelNames.append(modelName);
|
||||
}
|
||||
}
|
||||
|
||||
allModelsDownloaded = downloadableModelNames.isEmpty();
|
||||
if (allModelsDownloaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (QString &series : downloadableSeriesToModels.keys()) {
|
||||
QStringList &models = downloadableSeriesToModels[series];
|
||||
models.removeDuplicates();
|
||||
std::sort(models.begin(), models.end());
|
||||
}
|
||||
|
||||
QStringList userFavorites = QString::fromStdString(params.get("UserFavorites")).split(",");
|
||||
userFavorites.removeAll("");
|
||||
|
||||
QStringList communityFavorites = QString::fromStdString(params.get("CommunityFavorites")).split(",");
|
||||
communityFavorites.removeAll("");
|
||||
|
||||
QString savedSortMode = QString::fromStdString(params.get("ModelSortMode"));
|
||||
if (savedSortMode.isEmpty()) savedSortMode = "alphabetical";
|
||||
|
||||
ExpandableMultiOptionDialog dialog(
|
||||
tr("Select a driving model to download"),
|
||||
downloadableSeriesToModels,
|
||||
"",
|
||||
this,
|
||||
userFavorites,
|
||||
communityFavorites,
|
||||
modelReleasedDates,
|
||||
modelFileToNameMap,
|
||||
savedSortMode);
|
||||
|
||||
int dialogResult = dialog.exec();
|
||||
|
||||
QString sortMode = dialog.getCurrentSortMode();
|
||||
QStringList newUserFavs = dialog.getUserFavorites();
|
||||
params.put("ModelSortMode", sortMode.toStdString());
|
||||
params.put("UserFavorites", newUserFavs.join(",").toStdString());
|
||||
userFavorites = newUserFavs;
|
||||
|
||||
if (dialogResult == QDialog::Accepted) {
|
||||
QString modelToDownload = dialog.selection;
|
||||
if (!modelToDownload.isEmpty()) {
|
||||
params_memory.put("ModelToDownload", modelFileToNameMap.key(modelToDownload).toStdString());
|
||||
params_memory.put("ModelDownloadProgress", "Downloading...");
|
||||
QString modelKey = modelFileToNameMap.key(modelToDownload);
|
||||
params_memory.put("ModelToDownload", modelKey.toStdString());
|
||||
// Also persist the version for this downloaded model if known
|
||||
{
|
||||
QFile vf("/data/models/.model_versions.json");
|
||||
if (vf.open(QIODevice::ReadOnly)) {
|
||||
auto doc = QJsonDocument::fromJson(vf.readAll());
|
||||
if (doc.isObject()) {
|
||||
auto obj = doc.object();
|
||||
if (obj.contains(modelKey)) {
|
||||
params.put("ModelVersion", obj.value(modelKey).toString().toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
params_memory.put("ModelDownloadProgress", "Downloading...");
|
||||
|
||||
downloadModelButton->setText(0, tr("CANCEL"));
|
||||
downloadModelButton->setText(0, tr("CANCEL"));
|
||||
|
||||
downloadModelButton->setValue("Downloading...");
|
||||
downloadModelButton->setValue("Downloading...");
|
||||
|
||||
downloadModelButton->setVisibleButton(1, false);
|
||||
downloadModelButton->setVisibleButton(1, false);
|
||||
|
||||
modelDownloading = true;
|
||||
modelDownloading = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (id == 1) {
|
||||
@@ -179,8 +241,8 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
});
|
||||
modelToggle = downloadModelButton;
|
||||
} else if (param == "ManageBlacklistedModels") {
|
||||
FrogPilotButtonsControl *blacklistButton = new FrogPilotButtonsControl(title, desc, icon, {tr("ADD"), tr("REMOVE"), tr("REMOVE ALL")});
|
||||
QObject::connect(blacklistButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
FrogPilotButtonsControl *blacklistBtn = new FrogPilotButtonsControl(title, desc, icon, {tr("ADD"), tr("REMOVE"), tr("REMOVE ALL")});
|
||||
QObject::connect(blacklistBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
QStringList blacklistedModels = QString::fromStdString(params.get("BlacklistedModels")).split(",");
|
||||
blacklistedModels.removeAll("");
|
||||
|
||||
@@ -193,9 +255,22 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
}
|
||||
|
||||
if (blacklistableModels.size() <= 1) {
|
||||
ConfirmationDialog::alert(tr("There are no more driving models to blacklist. The only available model is \"%1\"!").arg(blacklistableModels.first()), this);
|
||||
ConfirmationDialog::alert(tr("There are no more models to blacklist! The only available model is \"%1\"!").arg(blacklistableModels.first()), this);
|
||||
} else {
|
||||
QString modelToBlacklist = MultiOptionDialog::getSelection(tr("Select a driving model to add to the blacklist"), blacklistableModels, "", this);
|
||||
// Group blacklistable models by series
|
||||
QMap<QString, QStringList> blacklistableSeriesToModels;
|
||||
for (const QString &modelName : blacklistableModels) {
|
||||
QString modelKey = modelFileToNameMapProcessed.key(modelName);
|
||||
QString series = modelSeriesMap.value(modelKey, "Custom Series");
|
||||
blacklistableSeriesToModels[series].append(modelName);
|
||||
}
|
||||
|
||||
// Sort models within each series
|
||||
for (QString &series : blacklistableSeriesToModels.keys()) {
|
||||
blacklistableSeriesToModels[series].sort();
|
||||
}
|
||||
|
||||
QString modelToBlacklist = ExpandableMultiOptionDialog::getSelection(tr("Select a model to add to the blacklist"), blacklistableSeriesToModels, "", this);
|
||||
if (!modelToBlacklist.isEmpty()) {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to add the \"%1\" model to the blacklist?").arg(modelToBlacklist), tr("Add"), this)) {
|
||||
blacklistedModels.append(modelFileToNameMapProcessed.key(modelToBlacklist));
|
||||
@@ -210,9 +285,21 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
QString modelName = modelFileToNameMapProcessed.value(model);
|
||||
whitelistableModels.append(modelName);
|
||||
}
|
||||
whitelistableModels.sort();
|
||||
|
||||
QString modelToWhitelist = MultiOptionDialog::getSelection(tr("Select a driving model to remove from the blacklist"), whitelistableModels, "", this);
|
||||
// Group whitelistable models by series
|
||||
QMap<QString, QStringList> whitelistableSeriesToModels;
|
||||
for (const QString &modelName : whitelistableModels) {
|
||||
QString modelKey = modelFileToNameMapProcessed.key(modelName);
|
||||
QString series = modelSeriesMap.value(modelKey, "Custom Series");
|
||||
whitelistableSeriesToModels[series].append(modelName);
|
||||
}
|
||||
|
||||
// Sort models within each series
|
||||
for (QString &series : whitelistableSeriesToModels.keys()) {
|
||||
whitelistableSeriesToModels[series].sort();
|
||||
}
|
||||
|
||||
QString modelToWhitelist = ExpandableMultiOptionDialog::getSelection(tr("Select a model to remove from the blacklist"), whitelistableSeriesToModels, "", this);
|
||||
if (!modelToWhitelist.isEmpty()) {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to remove the \"%1\" model from the blacklist?").arg(modelToWhitelist), tr("Remove"), this)) {
|
||||
blacklistedModels.removeAll(modelFileToNameMapProcessed.key(modelToWhitelist));
|
||||
@@ -221,18 +308,18 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
}
|
||||
}
|
||||
} else if (id == 2) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove all of your blacklisted driving models?"), this)) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove all of your blacklisted models?"), this)) {
|
||||
params.remove("BlacklistedModels");
|
||||
params_cache.remove("BlacklistedModels");
|
||||
}
|
||||
}
|
||||
});
|
||||
modelToggle = blacklistButton;
|
||||
modelToggle = blacklistBtn;
|
||||
} else if (param == "ManageScores") {
|
||||
FrogPilotButtonsControl *manageScoresButton = new FrogPilotButtonsControl(title, desc, icon, {tr("RESET"), tr("VIEW")});
|
||||
QObject::connect(manageScoresButton, &FrogPilotButtonsControl::buttonClicked, [modelLayout, modelLabelsList, modelLabelsPanel, this](int id) {
|
||||
FrogPilotButtonsControl *manageScoresBtn = new FrogPilotButtonsControl(title, desc, icon, {tr("RESET"), tr("VIEW")});
|
||||
QObject::connect(manageScoresBtn, &FrogPilotButtonsControl::buttonClicked, [this, modelLayout, modelLabelsList, modelLabelsPanel](int id) {
|
||||
if (id == 0) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Reset all model drives and ratings? This clears your drive history and collected feedback!"), this)) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to reset all of your model drives and scores?"), this)) {
|
||||
params.remove("ModelDrivesAndScores");
|
||||
params_cache.remove("ModelDrivesAndScores");
|
||||
}
|
||||
@@ -244,82 +331,116 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
modelLayout->setCurrentWidget(modelLabelsPanel);
|
||||
}
|
||||
});
|
||||
modelToggle = manageScoresButton;
|
||||
modelToggle = manageScoresBtn;
|
||||
} else if (param == "SelectModel") {
|
||||
selectModelButton = new ButtonControl(title, tr("SELECT"), desc);
|
||||
QObject::connect(selectModelButton, &ButtonControl::clicked, [this]() {
|
||||
QStringList selectableModels;
|
||||
// Group models by series for the enhanced dialog
|
||||
QMap<QString, QStringList> seriesToModels;
|
||||
QMap<QString, QString> installedModelFileToNameMap;
|
||||
QMap<QString, QString> installedReleasedDates;
|
||||
|
||||
// Add all available models by series
|
||||
for (const QString &modelKey : modelFileToNameMap.keys()) {
|
||||
if (!isModelInstalled(modelKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString modelName = modelFileToNameMap.value(modelKey);
|
||||
if (modelName.contains("(Default)")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (modelDir.exists(modelKey + ".thneed") || hasAllTinygradFiles(modelDir, modelKey)) {
|
||||
selectableModels.append(modelName);
|
||||
installedModelFileToNameMap.insert(modelKey, modelName);
|
||||
if (modelReleasedDates.contains(modelKey)) {
|
||||
installedReleasedDates.insert(modelKey, modelReleasedDates.value(modelKey));
|
||||
}
|
||||
|
||||
QString series = modelSeriesMap.value(modelKey, "Custom Series");
|
||||
seriesToModels[series].append(modelName);
|
||||
}
|
||||
selectableModels.sort();
|
||||
selectableModels.prepend(modelFileToNameMap.value(normalizeModelKey(QString::fromStdString(params_default.get("Model")))));
|
||||
|
||||
QString modelToSelect = MultiOptionDialog::getSelection(tr("Select a Model — 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"), selectableModels, currentModel, this);
|
||||
if (!modelToSelect.isEmpty()) {
|
||||
currentModel = modelToSelect;
|
||||
// Sort models alphabetically within each series
|
||||
for (QString &series : seriesToModels.keys()) {
|
||||
seriesToModels[series].sort();
|
||||
}
|
||||
|
||||
params.put("Model", modelFileToNameMap.key(modelToSelect).toStdString());
|
||||
// Add default model to the beginning of its series
|
||||
QString defaultModelName = modelFileToNameMap.value(QString::fromStdString(params_default.get("Model")));
|
||||
QString defaultSeries = modelSeriesMap.value(QString::fromStdString(params_default.get("Model")), "Custom Series");
|
||||
if (seriesToModels.contains(defaultSeries) && seriesToModels[defaultSeries].contains(defaultModelName)) {
|
||||
seriesToModels[defaultSeries].removeAll(defaultModelName);
|
||||
seriesToModels[defaultSeries].prepend(defaultModelName);
|
||||
}
|
||||
|
||||
updateFrogPilotToggles();
|
||||
// Prepare favorites and dates for the enhanced dialog
|
||||
QStringList userFavs = QString::fromStdString(params.get("UserFavorites")).split(",");
|
||||
userFavs.removeAll("");
|
||||
|
||||
if (started) {
|
||||
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
}
|
||||
selectModelButton->setValue(modelToSelect);
|
||||
QStringList communityFavs = QString::fromStdString(params.get("CommunityFavorites")).split(",");
|
||||
communityFavs.removeAll("");
|
||||
|
||||
QStringList deletableModels;
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QString base = QFileInfo(file).baseName();
|
||||
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
|
||||
if (base.startsWith(modelKey)) {
|
||||
QString modelName = modelFileToNameMapProcessed.value(modelKey);
|
||||
if (!deletableModels.contains(modelName)) {
|
||||
deletableModels.append(modelName);
|
||||
// Create dialog instance to access sort mode and favorites after selection
|
||||
QString savedSortMode = QString::fromStdString(params.get("ModelSortMode"));
|
||||
if (savedSortMode.isEmpty()) savedSortMode = "alphabetical";
|
||||
|
||||
ExpandableMultiOptionDialog dialog(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"),
|
||||
seriesToModels, currentModel, this,
|
||||
userFavs, communityFavs, installedReleasedDates, installedModelFileToNameMap, savedSortMode);
|
||||
|
||||
int dialogResult = dialog.exec();
|
||||
|
||||
// Persist sort mode and user favorites even if no selection was made
|
||||
QString sortMode = dialog.getCurrentSortMode();
|
||||
QStringList newUserFavs = dialog.getUserFavorites();
|
||||
params.put("ModelSortMode", sortMode.toStdString());
|
||||
params.put("UserFavorites", newUserFavs.join(",").toStdString());
|
||||
|
||||
if (dialogResult == QDialog::Accepted) {
|
||||
QString modelToSelect = dialog.selection;
|
||||
if (!modelToSelect.isEmpty()) {
|
||||
currentModel = modelToSelect;
|
||||
|
||||
params.put("Model", modelFileToNameMap.key(modelToSelect).toStdString());
|
||||
// Sync ModelVersion with the selected model if known
|
||||
{
|
||||
QString modelKey = modelFileToNameMap.key(modelToSelect);
|
||||
QFile vf("/data/models/.model_versions.json");
|
||||
if (vf.open(QIODevice::ReadOnly)) {
|
||||
auto doc = QJsonDocument::fromJson(vf.readAll());
|
||||
if (doc.isObject()) {
|
||||
auto obj = doc.object();
|
||||
if (obj.contains(modelKey)) {
|
||||
params.put("ModelVersion", obj.value(modelKey).toString().toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateFrogPilotToggles();
|
||||
|
||||
if (started) {
|
||||
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
}
|
||||
selectModelButton->setValue(modelToSelect);
|
||||
|
||||
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
|
||||
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
|
||||
}
|
||||
deletableModels.removeAll(processModelName(currentModel));
|
||||
deletableModels.removeAll(modelFileToNameMapProcessed.value(normalizeModelKey(QString::fromStdString(params_default.get("Model")))));
|
||||
noModelsDownloaded = deletableModels.isEmpty();
|
||||
}
|
||||
});
|
||||
modelToggle = selectModelButton;
|
||||
|
||||
} else if (param == "UpdateTinygrad") {
|
||||
updateTinygradButton = new FrogPilotButtonsControl(title, desc, icon, {tr("UPDATE")});
|
||||
QObject::connect(updateTinygradButton, &FrogPilotButtonsControl::buttonClicked, [this]() {
|
||||
if (updatingTinygrad) {
|
||||
params_memory.putBool("CancelModelDownload", true);
|
||||
|
||||
updateTinygradButton->setEnabled(false);
|
||||
updateTinygradButton->setValue(tr("Cancelling..."));
|
||||
|
||||
cancellingDownload = true;
|
||||
} else {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Updating Tinygrad will delete existing Tinygrad-based driving models and need to be re-downloaded. Proceed?"), this)) {
|
||||
params_memory.putBool("UpdateTinygrad", true);
|
||||
params_memory.put("ModelDownloadProgress", "Downloading...");
|
||||
|
||||
updateTinygradButton->setText(0, tr("CANCEL"));
|
||||
updateTinygradButton->setValue(tr("Updating..."));
|
||||
|
||||
updatingTinygrad = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
modelToggle = updateTinygradButton;
|
||||
|
||||
} else if (param == "RecoveryPower") {
|
||||
std::vector<QString> recoveryPowerButton{"Reset"};
|
||||
modelToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0.5, 2.0, QString(), std::map<float, QString>(), 0.1, false, {}, recoveryPowerButton, false, false);
|
||||
recoveryPowerToggle = static_cast<FrogPilotParamValueButtonControl*>(modelToggle);
|
||||
} else if (param == "StopDistance") {
|
||||
std::vector<QString> stopDistanceButton{"Reset"};
|
||||
modelToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 4.0, 10.0, QString(), std::map<float, QString>(), 0.1, false, {}, stopDistanceButton, false, false);
|
||||
stopDistanceToggle = static_cast<FrogPilotParamValueButtonControl*>(modelToggle);
|
||||
} else {
|
||||
modelToggle = new ParamControl(param, title, desc, icon);
|
||||
}
|
||||
@@ -328,21 +449,16 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
|
||||
modelList->addItem(modelToggle);
|
||||
|
||||
QObject::connect(modelToggle, &AbstractControl::hideDescriptionEvent, [this]() {
|
||||
update();
|
||||
});
|
||||
QObject::connect(modelToggle, &AbstractControl::showDescriptionEvent, [this]() {
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
openDescriptions(forceOpenDescriptions, toggles);
|
||||
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles["ModelRandomizer"]), &ToggleControl::toggleFlipped, [this](bool state) {
|
||||
updateToggles();
|
||||
|
||||
if (state && !allModelsDownloaded) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("The \"Model Randomizer\" works only with downloaded models. Download all models now?"), this)) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("The \"Model Randomizer\" only works with downloaded models. Do you want to download all the driving models?"), this)) {
|
||||
params_memory.putBool("DownloadAllModels", true);
|
||||
params_memory.put("ModelDownloadProgress", "Downloading...");
|
||||
|
||||
@@ -353,13 +469,120 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubPanel, [modelLayout, modelPanel, this] {
|
||||
openDescriptions(forceOpenDescriptions, toggles);
|
||||
modelLayout->setCurrentWidget(modelPanel);
|
||||
});
|
||||
if (recoveryPowerToggle) {
|
||||
QObject::connect(recoveryPowerToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this, recoveryPowerToggle]() {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset your <b>Recovery Power</b> to the default of 1.0?"), tr("Reset"), this)) {
|
||||
params.putFloat("RecoveryPower", 1.0);
|
||||
recoveryPowerToggle->refresh();
|
||||
updateFrogPilotToggles();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (stopDistanceToggle) {
|
||||
QObject::connect(stopDistanceToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this, stopDistanceToggle]() {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset your <b>Stop Distance</b> to the default of 6 meters?"), tr("Reset"), this)) {
|
||||
params.putFloat("StopDistance", 6.0);
|
||||
stopDistanceToggle->refresh();
|
||||
updateFrogPilotToggles();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubPanel, [modelLayout, modelPanel] {modelLayout->setCurrentWidget(modelPanel);});
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotModelPanel::updateState);
|
||||
}
|
||||
|
||||
bool FrogPilotModelPanel::isModelInstalled(const QString &key) const {
|
||||
if (key.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_thneed = false;
|
||||
bool has_policy_meta = false;
|
||||
bool has_policy_tg = false;
|
||||
bool has_vision_meta = false;
|
||||
bool has_vision_tg = false;
|
||||
bool has_off_policy_meta = false;
|
||||
bool has_off_policy_tg = false;
|
||||
bool foundAny = false;
|
||||
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QFileInfo fi(modelDir.filePath(file));
|
||||
const QString base = fi.baseName();
|
||||
const QString ext = fi.suffix();
|
||||
|
||||
if (!(base.startsWith(key) || base.startsWith(key + "_"))) continue;
|
||||
|
||||
foundAny = true;
|
||||
|
||||
if (ext == "thneed") {
|
||||
has_thneed = true;
|
||||
} else if (ext == "pkl") {
|
||||
if (base.contains("_driving_policy_metadata")) {
|
||||
has_policy_meta = true;
|
||||
} else if (base.contains("_driving_policy_tinygrad")) {
|
||||
has_policy_tg = true;
|
||||
} else if (base.contains("_driving_off_policy_metadata")) {
|
||||
has_off_policy_meta = true;
|
||||
} else if (base.contains("_driving_off_policy_tinygrad")) {
|
||||
has_off_policy_tg = true;
|
||||
} else if (base.contains("_driving_vision_metadata")) {
|
||||
has_vision_meta = true;
|
||||
} else if (base.contains("_driving_vision_tinygrad")) {
|
||||
has_vision_tg = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has_thneed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (has_policy_meta && has_policy_tg && has_vision_meta && has_vision_tg) {
|
||||
if (has_off_policy_meta || has_off_policy_tg) {
|
||||
return has_off_policy_meta && has_off_policy_tg;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return foundAny;
|
||||
}
|
||||
|
||||
QMap<QString, QString> FrogPilotModelPanel::getDeletableModelDisplayNames() {
|
||||
QMap<QString, QString> deletable;
|
||||
|
||||
QString defaultModelKey = QString::fromStdString(params_default.get("Model"));
|
||||
QString defaultModelName = modelFileToNameMap.value(defaultModelKey);
|
||||
QString processedDefault = processModelName(defaultModelName);
|
||||
QString processedCurrent = processModelName(currentModel);
|
||||
|
||||
for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
|
||||
const QString &modelKey = it.key();
|
||||
const QString &displayName = it.value();
|
||||
if (displayName.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isModelInstalled(modelKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString processedName = processModelName(displayName);
|
||||
if (!processedCurrent.isEmpty() && processedName == processedCurrent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!processedDefault.isEmpty() && processedName == processedDefault) {
|
||||
continue;
|
||||
}
|
||||
|
||||
deletable.insert(modelKey, displayName);
|
||||
}
|
||||
|
||||
return deletable;
|
||||
}
|
||||
|
||||
void FrogPilotModelPanel::showEvent(QShowEvent *event) {
|
||||
FrogPilotUIState &fs = *frogpilotUIState();
|
||||
UIState &s = *uiState();
|
||||
@@ -368,68 +591,90 @@ void FrogPilotModelPanel::showEvent(QShowEvent *event) {
|
||||
tuningLevel = parent->tuningLevel;
|
||||
|
||||
allModelsDownloading = params_memory.getBool("DownloadAllModels");
|
||||
modelDownloading = !params_memory.get("ModelDownloadProgress").empty();
|
||||
tinygradUpdate = params.getBool("TinygradUpdateAvailable");
|
||||
updatingTinygrad = params_memory.getBool("UpdateTinygrad");
|
||||
|
||||
modelDownloading &= !updatingTinygrad;
|
||||
modelDownloading = !params_memory.get("ModelToDownload").empty();
|
||||
|
||||
QStringList availableModels = QString::fromStdString(params.get("AvailableModels")).split(",");
|
||||
availableModels.sort();
|
||||
availableModelNames = QString::fromStdString(params.get("AvailableModelNames")).split(",");
|
||||
availableModelNames.sort();
|
||||
availableModelSeries = QString::fromStdString(params.get("AvailableModelSeries")).split(",");
|
||||
QStringList releasedDatesParam = QString::fromStdString(params.get("ModelReleasedDates")).split(",");
|
||||
QStringList communityFavsParam = QString::fromStdString(params.get("CommunityFavorites")).split(",");
|
||||
QStringList userFavsParam = QString::fromStdString(params.get("UserFavorites")).split(",");
|
||||
|
||||
// Build a simple model->version map for quick lookups elsewhere
|
||||
{
|
||||
QStringList versionList = QString::fromStdString(params.get("ModelVersions")).split(",");
|
||||
QJsonObject versionObj;
|
||||
int verCount = qMin(availableModels.size(), versionList.size());
|
||||
for (int i = 0; i < verCount; ++i) {
|
||||
versionObj.insert(availableModels[i], versionList[i]);
|
||||
}
|
||||
QFile out("/data/models/.model_versions.json");
|
||||
if (out.open(QIODevice::WriteOnly)) {
|
||||
out.write(QJsonDocument(versionObj).toJson());
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
modelFileToNameMap.clear();
|
||||
modelFileToNameMapProcessed.clear();
|
||||
for (int i = 0; i < qMin(availableModels.size(), availableModelNames.size()); ++i) {
|
||||
modelFileToNameMap.insert(availableModels[i], availableModelNames[i]);
|
||||
modelFileToNameMapProcessed.insert(availableModels[i], processModelName(availableModelNames[i]));
|
||||
}
|
||||
|
||||
QStringList downloadableModels = availableModelNames;
|
||||
for (const QString &modelKey : modelFileToNameMap.keys()) {
|
||||
QString modelName = modelFileToNameMap.value(modelKey);
|
||||
if (modelDir.exists(modelKey + ".thneed") || hasAllTinygradFiles(modelDir, modelKey)) {
|
||||
downloadableModels.removeAll(modelName);
|
||||
modelSeriesMap.clear();
|
||||
modelReleasedDates.clear();
|
||||
int size = qMin(availableModels.size(), availableModelNames.size());
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const QString modelKey = availableModels[i].trimmed();
|
||||
const QString modelName = availableModelNames[i].trimmed();
|
||||
if (modelKey.isEmpty() || modelName.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
allModelsDownloaded = downloadableModels.isEmpty();
|
||||
|
||||
QStringList deletableModels;
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QString base = QFileInfo(file).baseName();
|
||||
for (const QString &modelKey : modelFileToNameMapProcessed.keys()) {
|
||||
if (base.startsWith(modelKey)) {
|
||||
QString modelName = modelFileToNameMapProcessed.value(modelKey);
|
||||
if (!deletableModels.contains(modelName)) {
|
||||
deletableModels.append(modelName);
|
||||
}
|
||||
QString series;
|
||||
if (i < availableModelSeries.size()) {
|
||||
series = availableModelSeries[i].trimmed();
|
||||
}
|
||||
if (series.isEmpty()) {
|
||||
series = tr("Custom Series");
|
||||
}
|
||||
|
||||
modelFileToNameMap.insert(modelKey, modelName);
|
||||
modelFileToNameMapProcessed.insert(modelKey, processModelName(modelName));
|
||||
modelSeriesMap.insert(modelKey, series);
|
||||
|
||||
if (i < releasedDatesParam.size()) {
|
||||
const QString released = releasedDatesParam[i].trimmed();
|
||||
if (!released.isEmpty()) {
|
||||
this->modelReleasedDates.insert(modelKey, released);
|
||||
}
|
||||
}
|
||||
}
|
||||
deletableModels.removeAll(processModelName(currentModel));
|
||||
deletableModels.removeAll(modelFileToNameMapProcessed.value(normalizeModelKey(QString::fromStdString(params_default.get("Model")))));
|
||||
noModelsDownloaded = deletableModels.isEmpty();
|
||||
allModelsDownloaded = true;
|
||||
for (auto it = modelFileToNameMap.constBegin(); it != modelFileToNameMap.constEnd(); ++it) {
|
||||
if (it.value().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!isModelInstalled(it.key())) {
|
||||
allModelsDownloaded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QString modelKey = normalizeModelKey(QString::fromStdString(params.get("Model")));
|
||||
if (!modelDir.exists(modelKey + ".thneed") && !hasAllTinygradFiles(modelDir, modelKey)) {
|
||||
modelKey = normalizeModelKey(QString::fromStdString(params_default.get("Model")));
|
||||
QString modelKey = QString::fromStdString(params.get("Model"));
|
||||
if (!isModelInstalled(modelKey)) {
|
||||
modelKey = QString::fromStdString(params_default.get("Model"));
|
||||
}
|
||||
currentModel = modelFileToNameMap.value(modelKey);
|
||||
selectModelButton->setValue(currentModel);
|
||||
|
||||
noModelsDownloaded = getDeletableModelDisplayNames().isEmpty();
|
||||
|
||||
bool parked = !s.scene.started || fs.frogpilot_scene.parked || fs.frogpilot_toggles.value("frogs_go_moo").toBool();
|
||||
|
||||
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
|
||||
|
||||
downloadModelButton->setEnabledButtons(0, !allModelsDownloaded && !allModelsDownloading && !cancellingDownload && !updatingTinygrad && fs.frogpilot_scene.online && parked);
|
||||
downloadModelButton->setEnabledButtons(1, !allModelsDownloaded && !modelDownloading && !cancellingDownload && !updatingTinygrad && fs.frogpilot_scene.online && parked);
|
||||
downloadModelButton->setEnabledButtons(0, !allModelsDownloaded && !allModelsDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked);
|
||||
downloadModelButton->setEnabledButtons(1, !allModelsDownloaded && !modelDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked);
|
||||
|
||||
downloadModelButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : "Not parked") : tr("Offline..."));
|
||||
|
||||
updateTinygradButton->setEnabled(!modelDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked && tinygradUpdate);
|
||||
updateTinygradButton->setValue(tinygradUpdate ? tr("Update available!") : tr("Up to date!"));
|
||||
|
||||
started = s.scene.started;
|
||||
|
||||
updateToggles();
|
||||
@@ -444,32 +689,27 @@ void FrogPilotModelPanel::updateState(const UIState &s, const FrogPilotUIState &
|
||||
|
||||
if (allModelsDownloading || modelDownloading) {
|
||||
QString progress = QString::fromStdString(params_memory.get("ModelDownloadProgress"));
|
||||
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|missing|offline", QRegularExpression::CaseInsensitiveOption));
|
||||
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption));
|
||||
|
||||
if (progress != "Downloading...") {
|
||||
downloadModelButton->setValue(progress);
|
||||
}
|
||||
|
||||
if (progress == "All models downloaded!" || progress == "Downloaded!" && !allModelsDownloading || downloadFailed) {
|
||||
if (progress == "All models downloaded!" && allModelsDownloading || progress == "Downloaded!" && modelDownloading || downloadFailed) {
|
||||
finalizingDownload = true;
|
||||
|
||||
QTimer::singleShot(2500, [progress, this]() {
|
||||
QTimer::singleShot(2500, [this, progress]() {
|
||||
allModelsDownloaded = progress == "All models downloaded!";
|
||||
allModelsDownloading = false;
|
||||
cancellingDownload = false;
|
||||
finalizingDownload = false;
|
||||
modelDownloading = false;
|
||||
noModelsDownloaded = false;
|
||||
|
||||
QStringList downloadableModels = availableModelNames;
|
||||
for (const QString &modelKey : modelFileToNameMap.keys()) {
|
||||
QString modelName = modelFileToNameMap.value(modelKey);
|
||||
if (modelDir.exists(modelKey + ".thneed") || hasAllTinygradFiles(modelDir, modelKey)) {
|
||||
downloadableModels.removeAll(modelName);
|
||||
}
|
||||
}
|
||||
allModelsDownloaded = downloadableModels.isEmpty();
|
||||
|
||||
params_memory.remove("CancelModelDownload");
|
||||
params_memory.remove("DownloadAllModels");
|
||||
params_memory.remove("ModelDownloadProgress");
|
||||
params_memory.remove("ModelToDownload");
|
||||
|
||||
downloadModelButton->setEnabled(true);
|
||||
downloadModelButton->setValue("");
|
||||
@@ -479,58 +719,20 @@ void FrogPilotModelPanel::updateState(const UIState &s, const FrogPilotUIState &
|
||||
downloadModelButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : "Not parked") : tr("Offline..."));
|
||||
}
|
||||
|
||||
if (updatingTinygrad) {
|
||||
QString progress = QString::fromStdString(params_memory.get("ModelDownloadProgress"));
|
||||
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|missing|offline", QRegularExpression::CaseInsensitiveOption));
|
||||
|
||||
if (progress != "Downloading...") {
|
||||
updateTinygradButton->setValue(progress);
|
||||
}
|
||||
|
||||
if (progress == "Updated!" && updatingTinygrad || downloadFailed) {
|
||||
finalizingDownload = true;
|
||||
|
||||
QTimer::singleShot(2500, [progress, this]() {
|
||||
modelDownloading = !params_memory.get("ModelDownloadProgress").empty();
|
||||
|
||||
if (modelDownloading) {
|
||||
downloadModelButton->setText(1, tr("CANCEL"));
|
||||
|
||||
downloadModelButton->setValue("Downloading...");
|
||||
|
||||
downloadModelButton->setVisibleButton(0, false);
|
||||
} else {
|
||||
cancellingDownload = false;
|
||||
}
|
||||
|
||||
tinygradUpdate = params.getBool("TinygradUpdateAvailable");
|
||||
|
||||
finalizingDownload = false;
|
||||
updatingTinygrad = false;
|
||||
|
||||
updateTinygradButton->setEnabled(tinygradUpdate);
|
||||
updateTinygradButton->setText(0, tr("UPDATE"));
|
||||
updateTinygradButton->setValue(tinygradUpdate ? tr("Update available!") : tr("Up to date!"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
deleteModelButton->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
|
||||
|
||||
downloadModelButton->setText(0, modelDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
|
||||
downloadModelButton->setText(1, allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD ALL"));
|
||||
|
||||
downloadModelButton->setEnabledButtons(0, !allModelsDownloaded && !allModelsDownloading && !cancellingDownload && !finalizingDownload && !updatingTinygrad && fs.frogpilot_scene.online && parked);
|
||||
downloadModelButton->setEnabledButtons(1, !allModelsDownloaded && !modelDownloading && !cancellingDownload && !finalizingDownload && !updatingTinygrad && fs.frogpilot_scene.online && parked);
|
||||
downloadModelButton->setEnabledButtons(0, !allModelsDownloaded && !allModelsDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked);
|
||||
downloadModelButton->setEnabledButtons(1, !allModelsDownloaded && !modelDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked);
|
||||
|
||||
downloadModelButton->setVisibleButton(0, !allModelsDownloading);
|
||||
downloadModelButton->setVisibleButton(1, !modelDownloading);
|
||||
|
||||
updateTinygradButton->setEnabled(!modelDownloading && !cancellingDownload && !cancellingDownload && !finalizingDownload && fs.frogpilot_scene.online && parked && tinygradUpdate);
|
||||
|
||||
started = s.scene.started;
|
||||
|
||||
parent->keepScreenOn = allModelsDownloading || modelDownloading || updatingTinygrad;
|
||||
parent->keepScreenOn = allModelsDownloading || modelDownloading;
|
||||
}
|
||||
|
||||
void FrogPilotModelPanel::updateModelLabels(FrogPilotListWidget *labelsList) {
|
||||
@@ -561,16 +763,16 @@ void FrogPilotModelPanel::updateToggles() {
|
||||
|
||||
if (key == "ManageBlacklistedModels" || key == "ManageScores") {
|
||||
setVisible &= params.getBool("ModelRandomizer");
|
||||
}
|
||||
|
||||
else if (key == "SelectModel") {
|
||||
} else if (key == "SelectModel") {
|
||||
setVisible &= !params.getBool("ModelRandomizer");
|
||||
} else if (key == "RecoveryPower") {
|
||||
setVisible &= (tuningLevel == 3); // Only visible in developer tuning level
|
||||
} else if (key == "StopDistance") {
|
||||
setVisible &= (tuningLevel == 3); // Only visible in developer tuning level
|
||||
}
|
||||
|
||||
toggle->setVisible(setVisible);
|
||||
}
|
||||
|
||||
openDescriptions(forceOpenDescriptions, toggles);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ private:
|
||||
void updateModelLabels(FrogPilotListWidget *labelsList);
|
||||
void updateState(const UIState &s, const FrogPilotUIState &fs);
|
||||
void updateToggles();
|
||||
bool isModelInstalled(const QString &key) const;
|
||||
QMap<QString, QString> getDeletableModelDisplayNames();
|
||||
|
||||
bool allModelsDownloaded;
|
||||
bool allModelsDownloading;
|
||||
@@ -55,8 +57,12 @@ private:
|
||||
|
||||
QMap<QString, QString> modelFileToNameMap;
|
||||
QMap<QString, QString> modelFileToNameMapProcessed;
|
||||
QMap<QString, QString> modelReleasedDates;
|
||||
QMap<QString, QString> modelSeriesMap;
|
||||
|
||||
QString currentModel;
|
||||
|
||||
|
||||
QStringList availableModelNames;
|
||||
QStringList availableModelSeries;
|
||||
};
|
||||
|
||||
@@ -168,6 +168,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
|
||||
std::vector<std::tuple<QString, QString, QString, QString>> vehicleToggles {
|
||||
{"GMToggles", tr("General Motors Settings"), tr("<b>FrogPilot features for General Motors vehicles.</b>"), ""},
|
||||
{"ExperimentalGMTune", tr("FrogsGoMoo's Experimental Tune"), tr("<b>Experimental GM tune by FrogsGoMoo</b> that attempts to smoothen stopping and takeoff control. Use at your own risk!"), ""},
|
||||
{"GMPedalLongitudinal", tr("Use Pedal for Longitudinal Control"), tr("<b>Use the pedal interceptor for longitudinal control</b> instead of camera ACC/Redneck when available."), ""},
|
||||
{"LongPitch", tr("Smooth Pedal Response on Hills"), tr("<b>Smoothen acceleration and braking</b> when driving downhill/uphill."), ""},
|
||||
{"VoltSNG", tr("Stop-and-Go Hack"), tr("<b>Force stop-and-go</b> on the 2017 Chevy Volt."), ""},
|
||||
|
||||
@@ -188,6 +189,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
|
||||
{"PedalSupport", tr("comma Pedal Support"), tr("<b>Does your vehicle support the \"comma pedal\"?</b>"), ""},
|
||||
{"OpenpilotLongitudinal", tr("openpilot Longitudinal Support"), tr("<b>Can openpilot control the vehicle's acceleration and braking?</b>"), ""},
|
||||
{"RadarSupport", tr("Radar Support"), tr("<b>Does openpilot use the vehicle's radar data</b> alongside the device's camera for tracking lead vehicles?"), ""},
|
||||
{"SASCMSupport", tr("SASCM Support"), tr("<b>Does your vehicle support \"SASCMs\"?</b>"), ""},
|
||||
{"SDSUSupport", tr("SDSU Support"), tr("<b>Does your vehicle support \"SDSUs\"?</b>"), ""},
|
||||
{"SNGSupport", tr("Stop-and-Go Support"), tr("<b>Does your vehicle support stop-and-go driving?</b>"), ""}
|
||||
};
|
||||
@@ -341,6 +343,7 @@ void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
|
||||
|
||||
QStringList detected;
|
||||
if (hasPedal) detected << "comma Pedal";
|
||||
if (parent->hasSASCM) detected << "SASCM";
|
||||
if (parent->hasSDSU) detected << "SDSU";
|
||||
if (parent->hasZSS) detected << "ZSS";
|
||||
static_cast<LabelControl*>(toggles["HardwareDetected"])->setText(detected.isEmpty() ? tr("None") : detected.join(", "));
|
||||
@@ -349,6 +352,7 @@ void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
|
||||
static_cast<LabelControl*>(toggles["OpenpilotLongitudinal"])->setText(hasOpenpilotLongitudinal ? tr("Yes") : tr("No"));
|
||||
static_cast<LabelControl*>(toggles["PedalSupport"])->setText(parent->canUsePedal ? tr("Yes") : tr("No"));
|
||||
static_cast<LabelControl*>(toggles["RadarSupport"])->setText(parent->hasRadar ? tr("Yes") : tr("No"));
|
||||
static_cast<LabelControl*>(toggles["SASCMSupport"])->setText(parent->canUseSASCM ? tr("Yes") : tr("No"));
|
||||
static_cast<LabelControl*>(toggles["SDSUSupport"])->setText(parent->canUseSDSU ? tr("Yes") : tr("No"));
|
||||
static_cast<LabelControl*>(toggles["SNGSupport"])->setText(hasSNG ? tr("Yes") : tr("No"));
|
||||
|
||||
@@ -403,6 +407,10 @@ void FrogPilotVehiclesPanel::updateToggles() {
|
||||
setVisible &= isHKGCanFd;
|
||||
}
|
||||
|
||||
else if (key == "GMPedalLongitudinal") {
|
||||
setVisible &= hasPedal;
|
||||
}
|
||||
|
||||
else if (key == "VoltSNG") {
|
||||
setVisible &= isVolt && !hasSNG;
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@ private:
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
QSet<QString> gmKeys = {"ExperimentalGMTune", "LongPitch", "VoltSNG"};
|
||||
QSet<QString> gmKeys = {"ExperimentalGMTune", "GMPedalLongitudinal", "LongPitch", "VoltSNG"};
|
||||
QSet<QString> hkgKeys = {"NewLongAPI", "TacoTuneHacks"};
|
||||
QSet<QString> longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "SNGHack", "VoltSNG"};
|
||||
QSet<QString> toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"};
|
||||
QSet<QString> vehicleInfoKeys = {"BlindSpotSupport", "HardwareDetected", "OpenpilotLongitudinal", "PedalSupport", "RadarSupport", "SDSUSupport", "SNGSupport"};
|
||||
QSet<QString> vehicleInfoKeys = {"BlindSpotSupport", "HardwareDetected", "OpenpilotLongitudinal", "PedalSupport", "RadarSupport", "SASCMSupport", "SDSUSupport", "SNGSupport"};
|
||||
|
||||
QSet<QString> parentKeys;
|
||||
|
||||
|
||||
@@ -214,6 +214,7 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) :
|
||||
{13, tr("Longitudinal MPC Jerk: Acceleration")},
|
||||
{14, tr("Longitudinal MPC Jerk: Danger Zone")},
|
||||
{15, tr("Longitudinal MPC Jerk: Speed Control")},
|
||||
{16, tr("Driving Model: Current")},
|
||||
};
|
||||
|
||||
ButtonControl *metricToggle = new ButtonControl(title, tr("SELECT"), desc);
|
||||
|
||||
@@ -8,7 +8,114 @@ source "$BASEDIR/launch_env.sh"
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
|
||||
# --- BEGIN: auto-fix persist from squashfs -> ext4 (one-time, preserves identity) ---
|
||||
function persist_convert_if_needed {
|
||||
LOG="/tmp/persist_fix.log"
|
||||
{
|
||||
echo "[persist-fix] ----- start $(date) -----"
|
||||
|
||||
# Guard: if we've already converted on this device, skip
|
||||
if [ -f /data/.persist_converted ] || [ -f /persist/.converted_to_ext4 ]; then
|
||||
echo "[persist-fix] Marker exists; skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Discover persist device
|
||||
DEV="$(blkid -t LABEL=persist -o device 2>/dev/null || true)"
|
||||
if [ -z "$DEV" ] && [ -e /dev/disk/by-partlabel/persist ]; then
|
||||
DEV="$(realpath /dev/disk/by-partlabel/persist)"
|
||||
fi
|
||||
if [ -z "$DEV" ]; then
|
||||
# Fallback commonly used path
|
||||
if lsblk -no PATH,LABEL | grep -E "persist$" >/dev/null 2>&1; then
|
||||
DEV="$(lsblk -no PATH,LABEL | awk '$2=="persist"{print $1; exit}')"
|
||||
else
|
||||
DEV="/dev/sda2"
|
||||
fi
|
||||
fi
|
||||
echo "[persist-fix] Using device: ${DEV}"
|
||||
|
||||
# Detect device filesystem type
|
||||
FSTYPE="$(blkid -o value -s TYPE "$DEV" 2>/dev/null || true)"
|
||||
echo "[persist-fix] Detected $DEV fstype='$FSTYPE'"
|
||||
UNSQS="$DIR/third_party/bin/unsquashfs"
|
||||
|
||||
# Detect current /persist mount status & fstype
|
||||
CUR_MNT_TYPE="$(mount | awk '$3==\"/persist\"{print $5}' | head -n1)"
|
||||
if mountpoint -q /persist; then
|
||||
echo "[persist-fix] /persist currently mounted as type='${CUR_MNT_TYPE}'"
|
||||
else
|
||||
echo "[persist-fix] /persist is not a mountpoint (empty dir)."
|
||||
fi
|
||||
|
||||
# Only proceed on NEW devices: either /persist is not mounted OR is squashfs,
|
||||
# AND the underlying persist partition is squashfs (factory RO image).
|
||||
if { ! mountpoint -q /persist || [ "${CUR_MNT_TYPE}" = "squashfs" ]; } && [ "${FSTYPE}" = "squashfs" ]; then
|
||||
echo "[persist-fix] NEW device detected (squashfs persist). Converting to ext4."
|
||||
|
||||
# Stream identity files using unsquashfs -cat directly
|
||||
for f in id_rsa id_rsa.pub color_cal dongle_id; do
|
||||
if sudo "$UNSQS" -cat "$DEV" "comma/$f" > "/data/$f" 2>/dev/null; then
|
||||
echo "[persist-fix] Preserved $f"
|
||||
else
|
||||
echo "[persist-fix] $f not found in squashfs (ok, may not exist)."
|
||||
fi
|
||||
done
|
||||
|
||||
# (Optional) raw backup of the original partition image (once)
|
||||
if [ ! -f /data/persist_backup.img ]; then
|
||||
echo "[persist-fix] Creating raw backup of ${DEV} to /data/persist_backup.img"
|
||||
sudo dd if="$DEV" of=/data/persist_backup.img bs=1M status=none || true
|
||||
sync
|
||||
fi
|
||||
|
||||
# Reformat persist as ext4 and label it
|
||||
echo "[persist-fix] Formatting ${DEV} as ext4..."
|
||||
if ! sudo mkfs.ext4 -F "$DEV"; then
|
||||
echo "[persist-fix][ERROR] mkfs.ext4 failed on ${DEV}. Aborting."
|
||||
return 0
|
||||
fi
|
||||
sudo e2label "$DEV" persist || true
|
||||
|
||||
# Mount the new ext4 persist RW
|
||||
if ! sudo mount -t ext4 -o rw,discard "$DEV" /persist; then
|
||||
echo "[persist-fix][ERROR] Failed to mount new ext4 persist. Aborting."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Recreate expected structure and restore identity
|
||||
sudo mkdir -p /persist/{comma,params,tracking}
|
||||
sudo chmod 755 /persist /persist/{comma,params,tracking}
|
||||
for f in id_rsa id_rsa.pub color_cal dongle_id; do
|
||||
if [ -f "/data/$f" ]; then
|
||||
sudo cp -p "/data/$f" "/persist/comma/$f"
|
||||
fi
|
||||
done
|
||||
if [ -f /persist/comma/id_rsa ]; then sudo chmod 600 /persist/comma/id_rsa; fi
|
||||
sudo chown -R comma:comma /persist/ /persist/comma /persist/params /persist/tracking || true
|
||||
sudo touch /persist/tracking/.lock
|
||||
|
||||
# Seed minimal params (only if not present)
|
||||
[ -f /persist/params/HasAcceptedTerms ] || echo -n 1 | sudo tee /persist/params/HasAcceptedTerms >/dev/null
|
||||
[ -f /persist/params/AlwaysOnDM ] || echo -n 1 | sudo tee /persist/params/AlwaysOnDM >/dev/null
|
||||
|
||||
# Mark complete so we don't run again; reboot once
|
||||
echo "ext4" | sudo tee /persist/.converted_to_ext4 >/dev/null
|
||||
sudo touch /data/.persist_converted
|
||||
sync
|
||||
echo "[persist-fix] Conversion complete. Rebooting once to finalize..."
|
||||
sudo reboot
|
||||
else
|
||||
echo "[persist-fix] Device is not NEW/squashfs (persist already ext4 or mounted). Skipping."
|
||||
fi
|
||||
|
||||
echo "[persist-fix] ----- end $(date) -----"
|
||||
} >>"$LOG" 2>&1
|
||||
}
|
||||
# --- END: auto-fix persist from squashfs -> ext4 ---
|
||||
|
||||
function agnos_init {
|
||||
persist_convert_if_needed
|
||||
# TODO: move this to agnos
|
||||
sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta
|
||||
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
|
||||
export VECLIB_MAXIMUM_THREADS=1
|
||||
|
||||
if [ -z "$AGNOS_VERSION" ]; then
|
||||
export AGNOS_VERSION="10.1"
|
||||
export AGNOS_VERSION="10.1.1"
|
||||
fi
|
||||
|
||||
export STAGING_ROOT="/data/safe_staging"
|
||||
|
||||
@@ -222,7 +222,7 @@ BO_ 715 ASCMGasRegenCmd: 8 K124_ASCM
|
||||
SG_ GasRegenCmdActive : 0|1@0+ (1,0) [0|0] "" NEO
|
||||
SG_ RollingCounter : 7|2@0+ (1,0) [0|0] "" NEO
|
||||
SG_ GasRegenAlwaysOne3 : 23|1@0+ (1,0) [0|1] "" NEO
|
||||
SG_ GasRegenCmd : 22|12@0+ (1,0) [0|0] "" NEO
|
||||
SG_ GasRegenCmd : 8|14@0+ (1,0) [0|0] "" NEO
|
||||
|
||||
BO_ 717 ASCM_2CD: 5 K124_ASCM
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+19
-15
@@ -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:
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 390 KiB After Width: | Height: | Size: 418 KiB |
@@ -166,7 +166,7 @@ class CarController(CarControllerBase):
|
||||
idx = (self.frame // 4) % 4
|
||||
|
||||
if self.CP.flags & GMFlags.CC_LONG.value:
|
||||
if CC.longActive and CS.out.vEgo > self.CP.minEnableSpeed:
|
||||
if CC.longActive and CS.out.cruiseState.enabled and CS.out.vEgo > self.CP.minEnableSpeed:
|
||||
# Using extend instead of append since the message is only sent intermittently
|
||||
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators))
|
||||
if self.CP.enableGasInterceptor:
|
||||
|
||||
@@ -8,7 +8,7 @@ from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.car import create_button_events, get_safety_config
|
||||
from openpilot.selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG
|
||||
from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR, SDGM_CAR
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LateralAccelFromTorqueCallbackType
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LateralAccelFromTorqueCallbackType, get_friction_threshold
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import get_friction
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
@@ -35,6 +35,10 @@ NON_LINEAR_TORQUE_PARAMS = {
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
def __init__(self, CP, FPCP, CarController, CarState):
|
||||
super().__init__(CP, FPCP, CarController, CarState)
|
||||
self.steer_offset = 0.0
|
||||
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
|
||||
@@ -76,20 +80,28 @@ class CarInterface(CarInterfaceBase):
|
||||
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
|
||||
|
||||
def torque_from_lateral_accel_siglin(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning):
|
||||
return np.interp(lateral_acceleration, lataccel_values, torque_values)
|
||||
return float(np.interp(lateral_acceleration, lataccel_values, torque_values) + self.steer_offset)
|
||||
return torque_from_lateral_accel_siglin
|
||||
else:
|
||||
return self.torque_from_lateral_accel_linear
|
||||
def torque_from_lateral_accel_linear(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning):
|
||||
return self.torque_from_lateral_accel_linear(lateral_acceleration, torque_params) + self.steer_offset
|
||||
return torque_from_lateral_accel_linear
|
||||
|
||||
def lateral_accel_from_torque(self) -> LateralAccelFromTorqueCallbackType:
|
||||
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
|
||||
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
|
||||
|
||||
def lateral_accel_from_torque_siglin(torque: float, torque_params: car.CarParams.LateralTorqueTuning):
|
||||
return np.interp(torque, torque_values, lataccel_values)
|
||||
return np.interp(torque - self.steer_offset, torque_values, lataccel_values)
|
||||
return lateral_accel_from_torque_siglin
|
||||
else:
|
||||
return self.lateral_accel_from_torque_linear
|
||||
def lateral_accel_from_torque_linear(torque: float, torque_params: car.CarParams.LateralTorqueTuning):
|
||||
return self.lateral_accel_from_torque_linear(torque - self.steer_offset, torque_params)
|
||||
return lateral_accel_from_torque_linear
|
||||
|
||||
def update(self, c: car.CarControl, can_strings: list[bytes], frogpilot_toggles) -> car.CarState:
|
||||
self.steer_offset = float(getattr(frogpilot_toggles, "steer_offset", 0.0))
|
||||
return super().update(c, can_strings, frogpilot_toggles)
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles):
|
||||
@@ -97,6 +109,11 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)]
|
||||
ret.autoResumeSng = False
|
||||
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN]
|
||||
|
||||
# Detect Beartech SASCM allows openpilot longitudinal control on SDGM and ASCM_INT vehicles
|
||||
if 0x2FF in fingerprint[0]:
|
||||
ret.flags |= GMFlags.SASCM.value
|
||||
|
||||
if PEDAL_MSG in fingerprint[0]:
|
||||
ret.enableGasInterceptor = True
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR
|
||||
@@ -162,6 +179,8 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.00]]
|
||||
ret.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594
|
||||
ret.steerActuatorDelay = 0.1 # Default delay, not measured yet
|
||||
ret.longitudinalTuning.deadzoneBP = [0.]
|
||||
ret.longitudinalTuning.deadzoneV = [0.]
|
||||
|
||||
ret.steerLimitTimer = 0.4
|
||||
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
|
||||
@@ -258,7 +277,7 @@ class CarInterface(CarInterfaceBase):
|
||||
elif candidate == CAR.CHEVROLET_TRAX:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
if ret.enableGasInterceptor:
|
||||
if ret.enableGasInterceptor and frogpilot_toggles.gm_pedal_longitudinal:
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
|
||||
ret.minEnableSpeed = -1
|
||||
@@ -273,7 +292,7 @@ class CarInterface(CarInterfaceBase):
|
||||
# Note: Low speed, stop and go not tested. Should be fairly smooth on highway
|
||||
ret.longitudinalTuning.kiBP = [0.0, 5., 35.]
|
||||
ret.longitudinalTuning.kiV = [0.0, 0.35, 0.5]
|
||||
ret.longitudinalTuning.kf = 0.15
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.15
|
||||
ret.stoppingDecelRate = 0.8
|
||||
else: # Pedal used for SNG, ACC for longitudinal control otherwise
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
@@ -348,8 +367,9 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.vEgo < self.CP.minSteerSpeed:
|
||||
events.add(EventName.belowSteerSpeed)
|
||||
|
||||
if (self.CP.flags & GMFlags.CC_LONG.value) and ret.vEgo < self.CP.minEnableSpeed and ret.cruiseState.enabled:
|
||||
events.add(EventName.speedTooLow)
|
||||
if (self.CP.flags & GMFlags.CC_LONG.value) and ret.vEgo < self.CP.minEnableSpeed:
|
||||
if ret.cruiseState.enabled or self.CS.out.cruiseState.enabled:
|
||||
events.add(EventName.speedTooLow)
|
||||
|
||||
if (self.CP.flags & GMFlags.PEDAL_LONG.value) and \
|
||||
self.CP.transmissionType == TransmissionType.direct and \
|
||||
|
||||
@@ -262,6 +262,8 @@ class GMFlags(IntFlag):
|
||||
CC_LONG = 2
|
||||
NO_CAMERA = 4
|
||||
NO_ACCELERATOR_POS_MSG = 8
|
||||
FORCE_BRAKE_C9 = 16
|
||||
SASCM = 32
|
||||
|
||||
|
||||
# In a Data Module, an identifier is a string used to recognize an object,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import math
|
||||
from collections import namedtuple
|
||||
|
||||
from cereal import car
|
||||
@@ -5,10 +6,12 @@ from openpilot.common.numpy_fast import clip, interp
|
||||
from openpilot.common.realtime import DT_CTRL
|
||||
from opendbc.can.packer import CANPacker
|
||||
from openpilot.selfdrive.car import create_gas_interceptor_command
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
from openpilot.selfdrive.car.honda import hondacan
|
||||
from openpilot.selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import rate_limit
|
||||
from openpilot.selfdrive.controls.lib.pid import PIDController
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
LongCtrlState = car.CarControl.Actuators.LongControlState
|
||||
@@ -125,6 +128,10 @@ class CarController(CarControllerBase):
|
||||
self.gas = 0.0
|
||||
self.brake = 0.0
|
||||
self.last_steer = 0.0
|
||||
self.pitch = 0.0
|
||||
self.gasonly_pid = PIDController(k_p=([0,], [0,]),
|
||||
k_i=([0., 5., 35.], [1.2, 0.8, 0.5]),
|
||||
rate=1 / DT_CTRL / 2)
|
||||
|
||||
def update(self, CC, CS, now_nanos, frogpilot_toggles):
|
||||
actuators = CC.actuators
|
||||
@@ -133,6 +140,9 @@ class CarController(CarControllerBase):
|
||||
hud_v_cruise = hud_control.setSpeed / conversion if hud_control.speedVisible else 255
|
||||
pcm_cancel_cmd = CC.cruiseControl.cancel
|
||||
|
||||
if len(CC.orientationNED) == 3:
|
||||
self.pitch = CC.orientationNED[1]
|
||||
|
||||
if CC.longActive:
|
||||
accel = actuators.accel
|
||||
gas, brake = compute_gas_brake(actuators.accel, CS.out.vEgo, self.CP.carFingerprint)
|
||||
@@ -173,8 +183,11 @@ class CarController(CarControllerBase):
|
||||
CS.CP.openpilotLongitudinalControl))
|
||||
|
||||
# wind brake from air resistance decel at high speed
|
||||
wind_brake = interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15])
|
||||
wind_brake_ms2 = interp(CS.out.vEgo, [0.0, 13.4, 22.4, 31.3, 40.2], [0.000, 0.049, 0.136, 0.267, 0.441]) # in m/s2 units
|
||||
hill_brake = math.sin(self.pitch) * ACCELERATION_DUE_TO_GRAVITY
|
||||
|
||||
# all of this is only relevant for HONDA NIDEC
|
||||
wind_brake = interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15]) # not in m/s2 units
|
||||
max_accel = interp(CS.out.vEgo, self.params.NIDEC_MAX_ACCEL_BP, self.params.NIDEC_MAX_ACCEL_V)
|
||||
# TODO this 1.44 is just to maintain previous behavior
|
||||
pcm_speed_BP = [-wind_brake,
|
||||
@@ -217,12 +230,25 @@ class CarController(CarControllerBase):
|
||||
|
||||
if self.CP.carFingerprint in HONDA_BOSCH:
|
||||
self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, self.params.BOSCH_ACCEL_MAX)
|
||||
self.gas = interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V)
|
||||
|
||||
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
|
||||
gas_pedal_force = self.accel # radarless does not need a pid
|
||||
elif (actuators.longControlState == LongCtrlState.pid) and not CS.out.gasPressed: # perform a gas-only pid
|
||||
gas_error = self.accel - CS.out.aEgo
|
||||
self.gasonly_pid.neg_limit = self.params.BOSCH_ACCEL_MIN
|
||||
self.gasonly_pid.pos_limit = self.params.BOSCH_ACCEL_MAX
|
||||
gas_pedal_force = self.gasonly_pid.update(gas_error, speed=CS.out.vEgo, feedforward=self.accel)
|
||||
gas_pedal_force += wind_brake_ms2 + hill_brake
|
||||
else:
|
||||
gas_pedal_force = self.accel
|
||||
self.gasonly_pid.reset()
|
||||
gas_pedal_force += wind_brake_ms2 + hill_brake
|
||||
self.gas = interp(gas_pedal_force, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V)
|
||||
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping
|
||||
self.stopping_counter = self.stopping_counter + 1 if stopping else 0
|
||||
can_sends.extend(hondacan.create_acc_commands(self.packer, self.CAN, CC.enabled, CC.longActive, self.accel, self.gas,
|
||||
self.stopping_counter, self.CP.carFingerprint))
|
||||
self.stopping_counter, self.CP.carFingerprint, accel + wind_brake_ms2 + hill_brake))
|
||||
else:
|
||||
apply_brake = clip(self.brake_last - wind_brake, 0.0, 1.0)
|
||||
apply_brake = int(clip(apply_brake * self.params.NIDEC_BRAKE_MAX, 0, self.params.NIDEC_BRAKE_MAX - 1))
|
||||
@@ -260,11 +286,11 @@ class CarController(CarControllerBase):
|
||||
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.speed = self.speed
|
||||
new_actuators.accel = self.accel
|
||||
new_actuators.gas = self.gas
|
||||
new_actuators.brake = self.brake
|
||||
new_actuators.steer = self.last_steer
|
||||
new_actuators.speed = float(self.speed)
|
||||
new_actuators.accel = float(self.accel)
|
||||
new_actuators.gas = float(self.gas)
|
||||
new_actuators.brake = float(self.brake)
|
||||
new_actuators.steer = float(self.last_steer)
|
||||
new_actuators.steerOutputCan = apply_steer
|
||||
|
||||
self.frame += 1
|
||||
|
||||
@@ -76,14 +76,14 @@ def create_brake_command(packer, CAN, apply_brake, pump_on, pcm_override, pcm_ca
|
||||
return packer.make_can_msg("BRAKE_COMMAND", CAN.pt, values)
|
||||
|
||||
|
||||
def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint):
|
||||
def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint, gas_force):
|
||||
commands = []
|
||||
min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0]
|
||||
|
||||
control_on = 5 if enabled else 0
|
||||
gas_command = gas if active and accel > min_gas_accel else -30000
|
||||
gas_command = gas if active and gas_force > min_gas_accel else -30000
|
||||
accel_command = accel if active else 0
|
||||
braking = 1 if active and accel < min_gas_accel else 0
|
||||
braking = 1 if active and gas_force < min_gas_accel else 0
|
||||
standstill = 1 if active and stopping_counter > 0 else 0
|
||||
standstill_release = 1 if active and stopping_counter == 0 else 0
|
||||
|
||||
|
||||
@@ -85,10 +85,10 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.longitudinalActuatorDelay = 0.5 # s
|
||||
if candidate in HONDA_BOSCH_RADARLESS:
|
||||
ret.stopAccel = CarControllerParams.BOSCH_ACCEL_MIN # stock uses -4.0 m/s^2 once stopped but limited by safety model
|
||||
else:
|
||||
# default longitudinal tuning for all hondas
|
||||
ret.longitudinalTuning.kiBP = [0., 5., 35.]
|
||||
ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5]
|
||||
|
||||
# default longitudinal tuning for all hondas
|
||||
ret.longitudinalTuning.kiBP = [0., 5., 35.]
|
||||
ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5]
|
||||
|
||||
eps_modified = False
|
||||
for fw in car_fw:
|
||||
@@ -117,6 +117,9 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
|
||||
if candidate == CAR.HONDA_CIVIC_BOSCH:
|
||||
CarControllerParams.BOSCH_GAS_LOOKUP_V = [0, 750]
|
||||
|
||||
elif candidate == CAR.HONDA_ACCORD:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
|
||||
|
||||
+19
-12
@@ -37,7 +37,12 @@ EventName = car.CarEvent.EventName
|
||||
MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS
|
||||
ACCEL_MAX = 2.0
|
||||
ACCEL_MIN = -3.5
|
||||
FRICTION_THRESHOLD = 0.3
|
||||
FRICTION_THRESHOLD = 0.09
|
||||
|
||||
def get_friction_threshold(v_ego):
|
||||
# Interpolate friction threshold from 0.09 at 50 mph to 0.15 at 75 mph
|
||||
from openpilot.common.numpy_fast import interp
|
||||
return interp(v_ego, [1 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.12, 0.25])
|
||||
|
||||
TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.toml')
|
||||
TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.toml')
|
||||
@@ -52,6 +57,7 @@ GEAR_SHIFTER_MAP: dict[str, car.CarState.GearShifter] = {
|
||||
'D': GearShifter.drive, 'DRIVE': GearShifter.drive,
|
||||
'S': GearShifter.sport, 'SPORT': GearShifter.sport,
|
||||
'L': GearShifter.low, 'LOW': GearShifter.low,
|
||||
'L2': GearShifter.low, 'L3': GearShifter.low,
|
||||
'B': GearShifter.brake, 'BRAKE': GearShifter.brake,
|
||||
}
|
||||
|
||||
@@ -146,14 +152,22 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles)
|
||||
|
||||
trailer_load_kg = getattr(frogpilot_toggles, "trailer_load_kg", 0)
|
||||
|
||||
# Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
|
||||
if not ret.notCar:
|
||||
ret.mass = ret.mass + trailer_load_kg
|
||||
ret.mass = ret.mass + STD_CARGO_KG
|
||||
|
||||
# Set params dependent on values set by the car interface
|
||||
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
|
||||
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront, ret.tireStiffnessFactor)
|
||||
|
||||
# FrogPilot variables
|
||||
toggles_to_check = ("force_torque_controller", "nnff", "nnff_lite")
|
||||
if ret.steerControlType != car.CarParams.SteerControlType.angle and any(getattr(frogpilot_toggles, toggle, False) for toggle in toggles_to_check):
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
return ret
|
||||
|
||||
@classmethod
|
||||
@@ -173,6 +187,7 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
elif platform in GMCAR:
|
||||
fp_ret.canUsePedal = True
|
||||
fp_ret.canUseSASCM = True
|
||||
|
||||
elif platform in HondaCAR:
|
||||
if candidate == HondaCAR.HONDA_CLARITY:
|
||||
@@ -216,14 +231,6 @@ class CarInterfaceBase(ABC):
|
||||
fp_ret.canUsePedal = not CP.autoResumeSng
|
||||
fp_ret.canUseSDSU = not CP.enableDsu and candidate not in UNSUPPORTED_DSU_CAR and candidate not in TSS2_CAR
|
||||
|
||||
if CP.steerControlType != car.CarParams.SteerControlType.angle:
|
||||
if CP.lateralTuning.which() == "pid" and (frogpilot_toggles.force_torque_controller or frogpilot_toggles.nnff or frogpilot_toggles.nnff_lite):
|
||||
CarInterfaceBase.configure_torque_tune(candidate, fp_ret.lateralTuning)
|
||||
elif CP.lateralTuning.which() == "torque":
|
||||
CarInterfaceBase.configure_torque_tune(candidate, fp_ret.lateralTuning)
|
||||
else:
|
||||
fp_ret.lateralTuning.init("pid")
|
||||
|
||||
fp_ret.openpilotLongitudinalControlDisabled = frogpilot_toggles.disable_openpilot_long
|
||||
|
||||
return fp_ret
|
||||
@@ -285,7 +292,7 @@ class CarInterfaceBase(ABC):
|
||||
ret.vEgoStopping = 0.5
|
||||
ret.vEgoStarting = 0.5
|
||||
ret.stoppingControl = True
|
||||
ret.longitudinalTuning.kf = 1.
|
||||
ret.longitudinalTuning.kfDEPRECATED = 1.
|
||||
ret.longitudinalTuning.kpBP = [0.]
|
||||
ret.longitudinalTuning.kpV = [0.]
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
@@ -301,9 +308,9 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
tune.init('torque')
|
||||
tune.torque.useSteeringAngle = use_steering_angle
|
||||
tune.torque.kf = 1.0
|
||||
tune.torque.kp = 1.0
|
||||
tune.torque.kp = 0.6
|
||||
tune.torque.ki = 0.3
|
||||
tune.torque.kd = 0.0
|
||||
tune.torque.friction = params['FRICTION']
|
||||
tune.torque.latAccelFactor = params['LAT_ACCEL_FACTOR']
|
||||
tune.torque.latAccelOffset = 0.0
|
||||
|
||||
@@ -53,7 +53,7 @@ def get_long_tune(CP, params):
|
||||
kiBP = [2., 5.]
|
||||
kiV = [0.5, 0.25]
|
||||
|
||||
return PIDController(0.0, (kiBP, kiV), k_f=1.0,
|
||||
return PIDController(0.0, (kiBP, kiV),
|
||||
pos_limit=params.ACCEL_MAX, neg_limit=params.ACCEL_MIN,
|
||||
rate=1 / (DT_CTRL * 3))
|
||||
|
||||
|
||||
@@ -29,10 +29,12 @@ from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, S
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
||||
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel
|
||||
from openpilot.frogpilot.tinygrad_modeld.tinygrad_modeld import LAT_SMOOTH_SECONDS
|
||||
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles, params_memory
|
||||
from openpilot.frogpilot.controls.lib.neural_network_feedforward import LatControlNNFF
|
||||
|
||||
SOFT_DISABLE_TIME = 3 # seconds
|
||||
LDW_MIN_SPEED = 31 * CV.MPH_TO_MS
|
||||
@@ -135,11 +137,11 @@ class Controls:
|
||||
|
||||
self.LaC: LatControl
|
||||
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
|
||||
self.LaC = LatControlAngle(self.CP, self.CI)
|
||||
elif self.FPCP.lateralTuning.which() == 'pid':
|
||||
self.LaC = LatControlPID(self.CP, self.CI)
|
||||
elif self.FPCP.lateralTuning.which() == 'torque':
|
||||
self.LaC = LatControlTorque(self.CP, self.FPCP, self.CI)
|
||||
self.LaC = LatControlAngle(self.CP, self.CI, DT_CTRL)
|
||||
elif self.CP.lateralTuning.which() == 'pid':
|
||||
self.LaC = LatControlPID(self.CP, self.CI, DT_CTRL)
|
||||
elif self.CP.lateralTuning.which() == 'torque':
|
||||
self.LaC = LatControlTorque(self.CP, self.CI, DT_CTRL)
|
||||
|
||||
self.initialized = False
|
||||
self.state = State.disabled
|
||||
@@ -204,6 +206,12 @@ class Controls:
|
||||
|
||||
self.frogpilot_toggles = get_frogpilot_toggles()
|
||||
|
||||
if self.CP.lateralTuning.which() == "torque" and (self.frogpilot_toggles.nnff or self.frogpilot_toggles.nnff_lite):
|
||||
self.LaC = LatControlNNFF(self.CP, self.CI, DT_CTRL)
|
||||
|
||||
|
||||
self.frogpilot_toggles.is_metric = self.is_metric
|
||||
|
||||
def set_initial_state(self):
|
||||
if REPLAY:
|
||||
controls_state = self.params.get("ReplayControlsState")
|
||||
@@ -613,14 +621,36 @@ class Controls:
|
||||
self.curvature = -self.VM.calc_curvature(steer_angle_without_offset, CS.vEgo, lp.roll)
|
||||
|
||||
# Update Torque Params
|
||||
if self.FPCP.lateralTuning.which() == 'torque':
|
||||
if self.CP.lateralTuning.which() == 'torque':
|
||||
torque_params = self.sm['liveTorqueParameters']
|
||||
if self.sm.all_checks(['liveTorqueParameters']) and (torque_params.useParams or self.frogpilot_toggles.force_auto_tune):
|
||||
self.LaC.update_live_torque_params(torque_params.latAccelFactorFiltered, torque_params.latAccelOffsetFiltered,
|
||||
torque_params.frictionCoefficientFiltered)
|
||||
|
||||
if self.sm.updated['liveDelay'] and (self.frogpilot_toggles.nnff or self.frogpilot_toggles.nnff_lite):
|
||||
self.LaC.nnff.update_live_delay(self.sm['liveDelay'].lateralDelay)
|
||||
allow_lat_accel_learning = self.CP.carName in ['toyota', 'hyundai']
|
||||
allow_friction_learning = (allow_lat_accel_learning or self.CP.carName in ['gm'])
|
||||
use_live_params = self.sm.all_checks(['liveTorqueParameters']) and (torque_params.useParams or self.frogpilot_toggles.force_auto_tune)
|
||||
|
||||
# Defaults pulled from manual tuning values
|
||||
lat_accel_factor = self.params.get_float("SteerLatAccel")
|
||||
friction = self.params.get_float("SteerFriction")
|
||||
lat_accel_offset = self.CP.lateralTuning.torque.latAccelOffset
|
||||
|
||||
# Apply user overrides first
|
||||
if self.frogpilot_toggles.use_custom_latAccelFactor:
|
||||
lat_accel_factor = self.frogpilot_toggles.latAccelFactor
|
||||
if self.frogpilot_toggles.use_custom_friction:
|
||||
friction = self.frogpilot_toggles.friction
|
||||
|
||||
# Layer in live values only for parameters the platform allows to learn and only when not overridden
|
||||
if use_live_params:
|
||||
if allow_lat_accel_learning and not self.frogpilot_toggles.use_custom_latAccelFactor:
|
||||
lat_accel_factor = torque_params.latAccelFactorFiltered
|
||||
lat_accel_offset = torque_params.latAccelOffsetFiltered
|
||||
if allow_friction_learning and not self.frogpilot_toggles.use_custom_friction:
|
||||
friction = torque_params.frictionCoefficientFiltered
|
||||
|
||||
self.LaC.update_live_torque_params(lat_accel_factor, lat_accel_offset, friction)
|
||||
|
||||
if self.sm.updated['liveDelay'] and hasattr(self.LaC, "update_live_delay"):
|
||||
self.LaC.update_live_delay(self.sm['liveDelay'].lateralDelay)
|
||||
|
||||
long_plan = self.sm['longitudinalPlan']
|
||||
model_v2 = self.sm['modelV2']
|
||||
@@ -671,11 +701,12 @@ class Controls:
|
||||
# Reset desired curvature to current to avoid violating the limits on engage
|
||||
new_desired_curvature = model_v2.action.desiredCurvature if CC.latActive else self.curvature
|
||||
self.desired_curvature, curvature_limited = clip_curvature(CS.vEgo, self.desired_curvature, new_desired_curvature, lp.roll)
|
||||
lat_delay = self.sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS
|
||||
|
||||
actuators.curvature = self.desired_curvature
|
||||
steer, steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp,
|
||||
self.steer_limited_by_safety, self.desired_curvature,
|
||||
curvature_limited,
|
||||
curvature_limited, lat_delay,
|
||||
self.sm['liveLocationKalman'],
|
||||
self.sm['modelV2'],
|
||||
self.frogpilot_toggles)
|
||||
@@ -715,8 +746,8 @@ class Controls:
|
||||
desired_lateral_accel = model_v2.action.desiredCurvature * (clipped_speed**2)
|
||||
undershooting = abs(desired_lateral_accel) / abs(1e-3 + actual_lateral_accel) > 1.2
|
||||
turning = abs(desired_lateral_accel) > 1.0
|
||||
# TODO: lac.saturated includes speed and other checks, should be pulled out
|
||||
if undershooting and turning and lac_log.saturated:
|
||||
commanded_torque_at_max = abs(lac_log.output) > 0.99
|
||||
if undershooting and turning and (lac_log.saturated or commanded_torque_at_max):
|
||||
if self.frogpilot_toggles.goat_scream_alert:
|
||||
self.frogpilot_events.add(FrogPilotEventName.goatSteerSaturated)
|
||||
else:
|
||||
@@ -764,7 +795,7 @@ class Controls:
|
||||
if self.frogpilot_toggles.conditional_experimental_mode or self.frogpilot_toggles.slc_fallback_experimental_mode:
|
||||
self.experimental_mode = self.sm['frogpilotPlan'].experimentalMode
|
||||
|
||||
if hasattr(self.LaC, "pid"):
|
||||
if hasattr(self.LaC, "pid") and self.CP.lateralTuning.which() != "pid":
|
||||
self.LaC.pid._k_p = self.frogpilot_toggles.steerKp
|
||||
|
||||
# Update FrogPilot variables
|
||||
@@ -888,7 +919,7 @@ class Controls:
|
||||
controlsState.experimentalMode = self.experimental_mode
|
||||
controlsState.personality = self.personality
|
||||
|
||||
lat_tuning = self.FPCP.lateralTuning.which()
|
||||
lat_tuning = self.CP.lateralTuning.which()
|
||||
if self.joystick_mode:
|
||||
controlsState.lateralControlState.debugState = lac_log
|
||||
elif self.CP.steerControlType == car.CarParams.SteerControlType.angle:
|
||||
|
||||
@@ -189,7 +189,7 @@ def smooth_value(val, prev_val, tau, dt=DT_MDL):
|
||||
alpha = 1 - np.exp(-dt/tau) if tau > 0 else 1
|
||||
return alpha * val + (1 - alpha) * prev_val
|
||||
|
||||
def clip_curvature(v_ego, prev_curvature, new_curvature, roll):
|
||||
def clip_curvature(v_ego, prev_curvature, new_curvature, roll) -> tuple[float, bool]:
|
||||
# This function respects ISO lateral jerk and acceleration limits + a max curvature
|
||||
v_ego = max(v_ego, MIN_SPEED)
|
||||
max_curvature_rate = MAX_LATERAL_JERK / (v_ego ** 2) # inexact calculation, check https://github.com/commaai/openpilot/pull/24755
|
||||
@@ -225,7 +225,7 @@ def get_speed_error(modelV2: log.ModelDataV2, v_ego: float) -> float:
|
||||
return 0.0
|
||||
|
||||
|
||||
def get_accel_from_plan(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0.05):
|
||||
def get_accel_from_plan_tomb_raider(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0.05):
|
||||
if len(speeds) == len(t_idxs):
|
||||
v_now = speeds[0]
|
||||
a_now = accels[0]
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
import numpy as np
|
||||
from abc import abstractmethod, ABC
|
||||
|
||||
from openpilot.common.realtime import DT_CTRL
|
||||
|
||||
MIN_LATERAL_CONTROL_SPEED = 0.3 # m/s
|
||||
|
||||
|
||||
class LatControl(ABC):
|
||||
def __init__(self, CP, CI):
|
||||
self.sat_count_rate = 1.0 * DT_CTRL
|
||||
def __init__(self, CP, CI, dt):
|
||||
self.dt = dt
|
||||
self.sat_limit = CP.steerLimitTimer
|
||||
self.sat_count = 0.
|
||||
self.sat_time = 0.
|
||||
self.sat_check_min_speed = 10.
|
||||
|
||||
# we define the steer torque scale as [-1.0...1.0]
|
||||
self.steer_max = 1.0
|
||||
|
||||
@abstractmethod
|
||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, llk, model_data, frogpilot_toggles):
|
||||
def update(self, active: bool, CS, VM, params, steer_limited_by_safety: bool, desired_curvature: float, curvature_limited: bool, lat_delay: float, llk, model_data, frogpilot_toggles):
|
||||
pass
|
||||
|
||||
def reset(self):
|
||||
self.sat_count = 0.
|
||||
self.sat_time = 0.
|
||||
|
||||
def _check_saturation(self, saturated, CS, steer_limited_by_safety, curvature_limited):
|
||||
# Saturated only if control output is not being limited by car torque/angle rate limits
|
||||
if (saturated or curvature_limited) and CS.vEgo > self.sat_check_min_speed and not steer_limited_by_safety and not CS.steeringPressed:
|
||||
self.sat_count += self.sat_count_rate
|
||||
self.sat_time += self.dt
|
||||
else:
|
||||
self.sat_count -= self.sat_count_rate
|
||||
self.sat_count = np.clip(self.sat_count, 0.0, self.sat_limit)
|
||||
return self.sat_count > (self.sat_limit - 1e-3)
|
||||
self.sat_time -= self.dt
|
||||
self.sat_time = np.clip(self.sat_time, 0.0, self.sat_limit)
|
||||
return self.sat_time > (self.sat_limit - 1e-3)
|
||||
|
||||
@@ -8,12 +8,12 @@ STEER_ANGLE_SATURATION_THRESHOLD = 2.5 # Degrees
|
||||
|
||||
|
||||
class LatControlAngle(LatControl):
|
||||
def __init__(self, CP, CI):
|
||||
super().__init__(CP, CI)
|
||||
def __init__(self, CP, CI, dt):
|
||||
super().__init__(CP, CI, dt)
|
||||
self.sat_check_min_speed = 5.
|
||||
self.use_steer_limited_by_safety = CP.carName == "tesla"
|
||||
|
||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, llk, model_data, frogpilot_toggles):
|
||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay, llk, model_data, frogpilot_toggles):
|
||||
angle_log = log.ControlsState.LateralAngleState.new_message()
|
||||
|
||||
if not active:
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import math
|
||||
|
||||
from cereal import log
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
|
||||
from openpilot.selfdrive.controls.lib.pid import PIDController
|
||||
|
||||
|
||||
class LatControlPID(LatControl):
|
||||
def __init__(self, CP, CI):
|
||||
super().__init__(CP, CI)
|
||||
def __init__(self, CP, CI, dt):
|
||||
super().__init__(CP, CI, dt)
|
||||
self.steer_release_i_decay = 0.8
|
||||
self.prev_steering_pressed = False
|
||||
self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
|
||||
(CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
|
||||
k_f=CP.lateralTuning.pid.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max)
|
||||
pos_limit=self.steer_max, neg_limit=-self.steer_max)
|
||||
self.ff_factor = CP.lateralTuning.pid.kf
|
||||
self.get_steer_feedforward = CI.get_steer_feedforward_function()
|
||||
self.low_speed_reset_threshold = max(CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED)
|
||||
|
||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, llk, model_data, frogpilot_toggles):
|
||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay, llk, model_data, frogpilot_toggles):
|
||||
pid_log = log.ControlsState.LateralPIDState.new_message()
|
||||
pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
|
||||
pid_log.steeringRateDeg = float(CS.steeringRateDeg)
|
||||
@@ -27,11 +31,17 @@ class LatControlPID(LatControl):
|
||||
if not active:
|
||||
output_torque = 0.0
|
||||
pid_log.active = False
|
||||
self.pid.reset()
|
||||
|
||||
else:
|
||||
if self.prev_steering_pressed and not CS.steeringPressed:
|
||||
self.pid.i *= self.steer_release_i_decay
|
||||
|
||||
# offset does not contribute to resistive torque
|
||||
ff = self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo)
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
||||
ff = self.ff_factor * self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo)
|
||||
if CS.vEgo < self.low_speed_reset_threshold:
|
||||
self.pid.reset()
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < self.low_speed_reset_threshold
|
||||
|
||||
output_torque = self.pid.update(error,
|
||||
feedforward=ff,
|
||||
@@ -45,4 +55,6 @@ class LatControlPID(LatControl):
|
||||
pid_log.output = float(output_torque)
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
|
||||
|
||||
self.prev_steering_pressed = CS.steeringPressed
|
||||
|
||||
return output_torque, angle_steers_des, pid_log
|
||||
|
||||
@@ -1,45 +1,60 @@
|
||||
import math
|
||||
import numpy as np
|
||||
from collections import deque
|
||||
|
||||
from cereal import log
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import get_friction
|
||||
from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
||||
from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD, get_friction_threshold
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED, get_friction
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
|
||||
from openpilot.selfdrive.controls.lib.pid import PIDController
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
|
||||
from openpilot.frogpilot.controls.lib.neural_network_feedforward import LOW_SPEED_Y_NN, NeuralNetworkFeedforward
|
||||
|
||||
# At higher speeds (25+mph) we can assume:
|
||||
# Lateral acceleration achieved by a specific car correlates to
|
||||
# torque applied to the steering rack. It does not correlate to
|
||||
# wheel slip, or to speed.
|
||||
|
||||
# This controller applies torque to achieve desired lateral
|
||||
# accelerations. To compensate for the low speed effects we
|
||||
# use a LOW_SPEED_FACTOR in the error. Additionally, there is
|
||||
# friction in the steering wheel that needs to be overcome to
|
||||
# move it at all, this is compensated for too.
|
||||
# accelerations. To compensate for the low speed effects the
|
||||
# proportional gain is increased at low speeds by the PID controller.
|
||||
# Additionally, there is friction in the steering wheel that needs
|
||||
# to be overcome to move it at all, this is compensated for too.
|
||||
|
||||
KP = 0.6
|
||||
KI = 0.3
|
||||
|
||||
INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30]
|
||||
KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP]
|
||||
|
||||
LOW_SPEED_X = [0, 10, 20, 30]
|
||||
LOW_SPEED_Y = [15, 13, 10, 5]
|
||||
LOW_SPEED_Y = [12, 10.5, 8, 5]
|
||||
MAX_LAT_JERK_UP = 2.5 # m/s^3
|
||||
|
||||
LP_FILTER_CUTOFF_HZ = 1.2
|
||||
JERK_LOOKAHEAD_SECONDS = 0.19
|
||||
JERK_GAIN = 0.22
|
||||
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
|
||||
VERSION = 2
|
||||
|
||||
class LatControlTorque(LatControl):
|
||||
def __init__(self, CP, FPCP, CI):
|
||||
super().__init__(CP, CI)
|
||||
self.torque_params = FPCP.lateralTuning.torque
|
||||
def __init__(self, CP, CI, dt):
|
||||
super().__init__(CP, CI, dt)
|
||||
self.steer_release_i_decay = 0.8
|
||||
self.prev_steering_pressed = False
|
||||
self.torque_params = CP.lateralTuning.torque
|
||||
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
|
||||
self.lateral_accel_from_torque = CI.lateral_accel_from_torque()
|
||||
self.pid = PIDController(self.torque_params.kp, self.torque_params.ki,
|
||||
k_f=self.torque_params.kf)
|
||||
self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, rate=1/self.dt)
|
||||
self.update_limits()
|
||||
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
|
||||
|
||||
# FrogPilot variables
|
||||
self.nnff = NeuralNetworkFeedforward(CP, self)
|
||||
|
||||
self.nnff_loaded = self.nnff.lat_torque_nn_model != None
|
||||
self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt)
|
||||
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
|
||||
self.lookahead_frames = int(JERK_LOOKAHEAD_SECONDS / self.dt)
|
||||
self.jerk_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt)
|
||||
self.previous_measurement = 0.0
|
||||
self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * (MAX_LAT_JERK_UP - 0.5)), self.dt)
|
||||
self.low_speed_reset_threshold = max(CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED)
|
||||
|
||||
def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction):
|
||||
self.torque_params.latAccelFactor = latAccelFactor
|
||||
@@ -51,50 +66,57 @@ class LatControlTorque(LatControl):
|
||||
self.pid.set_limits(self.lateral_accel_from_torque(self.steer_max, self.torque_params),
|
||||
self.lateral_accel_from_torque(-self.steer_max, self.torque_params))
|
||||
|
||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, llk, model_data, frogpilot_toggles):
|
||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay, llk, model_data, frogpilot_toggles):
|
||||
pid_log = log.ControlsState.LateralTorqueState.new_message()
|
||||
pid_log.version = VERSION
|
||||
if not active:
|
||||
output_torque = 0.0
|
||||
pid_log.active = False
|
||||
self.pid.reset()
|
||||
self.previous_measurement = 0.0
|
||||
self.measurement_rate_filter.x = 0.0
|
||||
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
|
||||
else:
|
||||
actual_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
|
||||
if self.prev_steering_pressed and not CS.steeringPressed:
|
||||
self.pid.i *= self.steer_release_i_decay
|
||||
|
||||
measured_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
|
||||
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
|
||||
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
|
||||
|
||||
desired_lateral_accel = desired_curvature * CS.vEgo ** 2
|
||||
actual_lateral_accel = actual_curvature * CS.vEgo ** 2
|
||||
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2
|
||||
|
||||
low_speed_factor = np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y_NN if frogpilot_toggles.nnff else LOW_SPEED_Y)**2
|
||||
setpoint = desired_lateral_accel + low_speed_factor * desired_curvature
|
||||
measurement = actual_lateral_accel + low_speed_factor * actual_curvature
|
||||
gravity_adjusted_lateral_accel = desired_lateral_accel - roll_compensation
|
||||
delay_frames = int(np.clip(lat_delay / self.dt, 1, self.lat_accel_request_buffer_len))
|
||||
expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames]
|
||||
future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2
|
||||
self.lat_accel_request_buffer.append(future_desired_lateral_accel)
|
||||
raw_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / max(lat_delay, self.dt)
|
||||
raw_lateral_jerk = np.clip(raw_lateral_jerk, -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
|
||||
desired_lateral_jerk = np.clip(self.jerk_filter.update(raw_lateral_jerk), -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
|
||||
gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
|
||||
setpoint = expected_lateral_accel + desired_lateral_jerk * lat_delay
|
||||
|
||||
if self.nnff_loaded and frogpilot_toggles.nnff or frogpilot_toggles.nnff_lite:
|
||||
pid_log, ff = self.nnff.compute_nnff(
|
||||
CS, VM, actual_lateral_accel, desired_lateral_accel, gravity_adjusted_lateral_accel, lateral_accel_deadzone,
|
||||
llk, measurement, model_data, params, pid_log, roll_compensation, setpoint, frogpilot_toggles
|
||||
)
|
||||
measurement = measured_curvature * CS.vEgo ** 2
|
||||
measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt)
|
||||
measurement_rate = np.clip(measurement_rate, -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
|
||||
self.previous_measurement = measurement
|
||||
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
||||
output_torque = self.pid.update(pid_log.error,
|
||||
feedforward=ff,
|
||||
speed=CS.vEgo,
|
||||
freeze_integrator=freeze_integrator)
|
||||
else:
|
||||
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
|
||||
pid_log.error = float(setpoint - measurement)
|
||||
ff = gravity_adjusted_lateral_accel
|
||||
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
|
||||
ff -= self.torque_params.latAccelOffset
|
||||
ff += get_friction(desired_lateral_accel - actual_lateral_accel, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)
|
||||
low_speed_factor = (np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y) / max(CS.vEgo, MIN_SPEED)) ** 2
|
||||
current_kp = np.interp(CS.vEgo, self.pid._k_p[0], self.pid._k_p[1])
|
||||
error = setpoint - measurement
|
||||
error_with_lsf = error * (1 + low_speed_factor / max(current_kp, 1e-3))
|
||||
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
||||
output_lataccel = self.pid.update(pid_log.error,
|
||||
feedforward=ff,
|
||||
speed=CS.vEgo,
|
||||
freeze_integrator=freeze_integrator)
|
||||
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
|
||||
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
|
||||
pid_log.error = float(error_with_lsf)
|
||||
ff = gravity_adjusted_future_lateral_accel
|
||||
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
|
||||
ff -= self.torque_params.latAccelOffset
|
||||
ff += get_friction(error_with_lsf + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, get_friction_threshold(CS.vEgo), self.torque_params)
|
||||
|
||||
if CS.vEgo < self.low_speed_reset_threshold:
|
||||
self.pid.reset()
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < self.low_speed_reset_threshold
|
||||
output_lataccel = self.pid.update(pid_log.error, error_rate=-measurement_rate, speed=CS.vEgo, feedforward=ff, freeze_integrator=freeze_integrator)
|
||||
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
|
||||
|
||||
pid_log.active = True
|
||||
pid_log.p = float(self.pid.p)
|
||||
@@ -102,9 +124,12 @@ class LatControlTorque(LatControl):
|
||||
pid_log.d = float(self.pid.d)
|
||||
pid_log.f = float(self.pid.f)
|
||||
pid_log.output = float(-output_torque) # TODO: log lat accel?
|
||||
pid_log.actualLateralAccel = float(actual_lateral_accel)
|
||||
pid_log.desiredLateralAccel = float(desired_lateral_accel)
|
||||
pid_log.actualLateralAccel = float(measurement)
|
||||
pid_log.desiredLateralAccel = float(setpoint)
|
||||
pid_log.desiredLateralJerk = float(desired_lateral_jerk)
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
|
||||
|
||||
self.prev_steering_pressed = CS.steeringPressed
|
||||
|
||||
# TODO left is positive in this convention
|
||||
return -output_torque, 0.0, pid_log
|
||||
|
||||
@@ -4,6 +4,8 @@ from openpilot.common.realtime import DT_CTRL
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, apply_deadzone
|
||||
from openpilot.selfdrive.controls.lib.pid import PIDController
|
||||
from openpilot.selfdrive.modeld.constants import ModelConstants
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.selfdrive.car.gm.values import CarControllerParams
|
||||
|
||||
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
|
||||
|
||||
@@ -85,17 +87,51 @@ def long_control_state_trans_old_long(CP, active, long_control_state, v_ego, v_t
|
||||
|
||||
return long_control_state
|
||||
|
||||
|
||||
class LongControl:
|
||||
def __init__(self, CP):
|
||||
self.CP = CP
|
||||
self.long_control_state = LongCtrlState.off
|
||||
self.experimental_mode = False
|
||||
pos_p_limit = 0.0 # if params("NoPositivePResponse") else None # put parameter-based control here
|
||||
self.pid = PIDController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
|
||||
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
|
||||
k_f=CP.longitudinalTuning.kf, rate=1 / DT_CTRL)
|
||||
rate=1 / DT_CTRL, pos_p_limit=pos_p_limit)
|
||||
# Preserve legacy behaviour when no feedforward gain is provided (default of 0.0)
|
||||
kf = getattr(CP.longitudinalTuning, 'kfDEPRECATED', 0.0)
|
||||
self.feedforward_gain = kf if kf != 0.0 else 1.0
|
||||
self.v_pid = 0.0
|
||||
self._mode_setup()
|
||||
self.last_output_accel = 0.0
|
||||
|
||||
|
||||
|
||||
def update_mpc_mode(self, experimental_mode):
|
||||
new_mode = 'blended' if experimental_mode else 'acc'
|
||||
|
||||
if self.transitioning and self.prev_mode == 'blended' and self.current_mode == 'acc':
|
||||
self.mode_transition_timer = 0.0
|
||||
|
||||
if new_mode != self.current_mode:
|
||||
self.prev_mode = self.current_mode
|
||||
self.transitioning = True
|
||||
self.mode_transition_timer = 0.0
|
||||
self.mode_transition_filter.x = self.last_output_accel
|
||||
|
||||
self.current_mode = new_mode
|
||||
|
||||
if self.transitioning:
|
||||
self.mode_transition_timer += DT_CTRL
|
||||
if self.mode_transition_timer >= self.mode_transition_duration:
|
||||
self.transitioning = False
|
||||
|
||||
def _mode_setup(self):
|
||||
self.prev_mode = 'acc'
|
||||
self.current_mode = 'acc'
|
||||
self.mode_transition_filter = FirstOrderFilter(0.0, 0.5, DT_CTRL)
|
||||
self.mode_transition_timer = 0.0
|
||||
self.mode_transition_duration = 1.0
|
||||
self.transitioning = False
|
||||
|
||||
def reset(self):
|
||||
self.pid.reset()
|
||||
|
||||
@@ -124,8 +160,44 @@ class LongControl:
|
||||
|
||||
else: # LongCtrlState.pid
|
||||
error = a_target - CS.aEgo
|
||||
output_accel = self.pid.update(error, speed=CS.vEgo,
|
||||
feedforward=a_target)
|
||||
self.update_mpc_mode(self.experimental_mode)
|
||||
feedforward = a_target * self.feedforward_gain
|
||||
raw_output_accel = self.pid.update(error, speed=CS.vEgo, feedforward=feedforward)
|
||||
|
||||
|
||||
if self.transitioning and self.prev_mode == 'acc' and self.current_mode == 'blended':
|
||||
if raw_output_accel < 0 and raw_output_accel < self.last_output_accel:
|
||||
progress = min(1.0, self.mode_transition_timer / self.mode_transition_duration)
|
||||
# Soften transition at low urgency, but keep sharp for high decel
|
||||
# 20% smoother for chill decel (lower exponent)
|
||||
urgency = abs(raw_output_accel / CarControllerParams.ACCEL_MIN)
|
||||
urgency_smooth = min(1.0, urgency ** 0.4) # 20% smoother for chill decel
|
||||
blend_factor = 1.0 - (1.0 - progress) * (1.0 - urgency_smooth)
|
||||
output_accel = self.last_output_accel + (raw_output_accel - self.last_output_accel) * blend_factor
|
||||
else:
|
||||
output_accel = raw_output_accel
|
||||
else:
|
||||
output_accel = raw_output_accel
|
||||
|
||||
if self.long_control_state == LongCtrlState.pid:
|
||||
# Smooth acceleration and deceleration with urgency-based rate limiting
|
||||
base_rate = 1.0
|
||||
|
||||
if output_accel < self.last_output_accel: # Deceleration requested
|
||||
decel_needed = self.last_output_accel - output_accel
|
||||
# Use a safe default for ACCEL_MIN if not available, to prevent division by zero
|
||||
max_decel = abs(CarControllerParams.ACCEL_MIN) if CarControllerParams.ACCEL_MIN != 0 else 4.0
|
||||
urgency = min(1.0, decel_needed / max_decel)
|
||||
|
||||
# Adjust rate based on urgency (1.0 m/s^3 for low urgency, up to 4.0 m/s^3 for high urgency)
|
||||
max_rate = 1.0 + 3.0 * urgency
|
||||
else:
|
||||
max_rate = base_rate # Acceleration is always smooth
|
||||
|
||||
max_accel_change = max_rate * DT_CTRL
|
||||
output_accel = clip(output_accel,
|
||||
self.last_output_accel - max_accel_change,
|
||||
self.last_output_accel + max_accel_change)
|
||||
|
||||
self.last_output_accel = clip(output_accel, accel_limits[0], accel_limits[1])
|
||||
return self.last_output_accel
|
||||
@@ -187,8 +259,9 @@ class LongControl:
|
||||
|
||||
error = self.v_pid - CS.vEgo
|
||||
error_deadzone = apply_deadzone(error, deadzone)
|
||||
feedforward = a_target * self.feedforward_gain
|
||||
output_accel = self.pid.update(error_deadzone, speed=CS.vEgo,
|
||||
feedforward=a_target,
|
||||
feedforward=feedforward,
|
||||
freeze_integrator=freeze_integrator)
|
||||
|
||||
self.last_output_accel = clip(output_accel, accel_limits[0], accel_limits[1])
|
||||
|
||||
@@ -3,11 +3,13 @@ import os
|
||||
import time
|
||||
import numpy as np
|
||||
from cereal import log
|
||||
from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX
|
||||
from openpilot.common.numpy_fast import clip, interp
|
||||
from openpilot.common.realtime import DT_MDL
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
# WARNING: imports outside of constants will not trigger a rebuild
|
||||
from openpilot.selfdrive.modeld.constants import index_function
|
||||
from openpilot.selfdrive.car.interfaces import ACCEL_MIN
|
||||
|
||||
if __name__ == '__main__': # generating code
|
||||
from openpilot.third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver
|
||||
@@ -30,12 +32,54 @@ COST_E_DIM = 5
|
||||
COST_DIM = COST_E_DIM + 1
|
||||
CONSTR_DIM = 4
|
||||
|
||||
X_EGO_OBSTACLE_COST = 3.
|
||||
# ===== VOACC SPEED-BASED TUNING PARAMETERS =====
|
||||
# City: Emergency-responsive | Highway: Rubber-banding prevention
|
||||
# Speed ranges: [0-35, 35-55, 55-70, 70+ mph]
|
||||
|
||||
# SPEED BREAKPOINTS (mph)
|
||||
SPEED_BREAKPOINTS = [0, 35, 55, 70] # 4 ranges: 0-35, 35-55, 55-70, 70+
|
||||
|
||||
# ===== CHANGE THESE VALUES FOR DIFFERENT SPEEDS =====
|
||||
|
||||
# RESPONSIVENESS TO LEAD CARS (Lower = More responsive, Higher = More stable)
|
||||
# [City Emergency, Urban Hwy, Rural Hwy, High Speed]
|
||||
X_EGO_OBSTACLE_COSTS = [3.0, 3.0, 2.5, 2.0] # Less aggressive at low speeds, closer to original
|
||||
|
||||
# JERK CONTROL (Lower = More jerky/responsive, Higher = Smoother/conservative)
|
||||
# [City Emergency, Urban Hwy, Rural Hwy, High Speed]
|
||||
J_EGO_COSTS = [5.0, 4.75, 4.5, 4.0] # Reverted to original 5.0 at low speeds
|
||||
|
||||
# ACCELERATION CHANGE PENALTIES (Lower = More responsive, Higher = Smoother)
|
||||
# [City Emergency, Urban Hwy, Rural Hwy, High Speed]
|
||||
A_CHANGE_COSTS = [200, 195, 180, 170] # Reverted to original 200 at low speeds
|
||||
|
||||
# SMOOTHING FILTERS - Speed-adaptive for optimal responsiveness
|
||||
# Lower = More responsive, Higher = Smoother
|
||||
LEAD_FILTER_TIME_LOW = 0.8 # Under 40 mph: Fast response for city emergency braking
|
||||
LEAD_FILTER_TIME_HIGH = 1.2 # Over 40 mph: Faster response to prevent highway gaps
|
||||
SPEED_FILTER_THRESHOLD = 40 * CV.MPH_TO_MS # 40 mph threshold
|
||||
|
||||
# DISTANCE ADAPTATION STRENGTH (How much penalties increase when close to lead)
|
||||
# [City, Urban Hwy, Rural Hwy, High Speed]
|
||||
DIST_ADAPTS = [0.04, 0.06, 0.06, 0.05] # Balanced across speeds
|
||||
|
||||
# ===== END TUNING PARAMETERS =====
|
||||
|
||||
# Function to get parameter value based on current speed
|
||||
def get_speed_based_param(speed_mph, param_array):
|
||||
"""Get parameter value based on current speed using smooth interpolation"""
|
||||
return np.interp(speed_mph, SPEED_BREAKPOINTS, param_array)
|
||||
|
||||
# Current active values (set based on speed)
|
||||
X_EGO_OBSTACLE_COST = 2.75
|
||||
J_EGO_COST = 5.5
|
||||
A_CHANGE_COST = 250.0
|
||||
LEAD_FILTER_TIME = 2.0
|
||||
DIST_ADAPT = 0.06
|
||||
|
||||
X_EGO_COST = 0.
|
||||
V_EGO_COST = 0.
|
||||
A_EGO_COST = 0.
|
||||
J_EGO_COST = 5.0
|
||||
A_CHANGE_COST = 200.
|
||||
DANGER_ZONE_COST = 100.
|
||||
CRASH_DISTANCE = .25
|
||||
LEAD_DANGER_FACTOR = 0.75
|
||||
@@ -55,9 +99,6 @@ T_IDXS = np.array(T_IDXS_LST)
|
||||
FCW_IDXS = T_IDXS < 5.0
|
||||
T_DIFFS = np.diff(T_IDXS, prepend=[0.])
|
||||
COMFORT_BRAKE = 2.5
|
||||
STOP_DISTANCE = 6.0
|
||||
CRUISE_MIN_ACCEL = -1.2
|
||||
CRUISE_MAX_ACCEL = 1.6
|
||||
|
||||
def get_jerk_factor(aggressive_jerk_acceleration=0.5, aggressive_jerk_danger=0.5, aggressive_jerk_speed=0.5,
|
||||
standard_jerk_acceleration=1.0, standard_jerk_danger=1.0, standard_jerk_speed=1.0,
|
||||
@@ -107,7 +148,11 @@ def get_stopped_equivalence_factor(v_lead):
|
||||
return (v_lead**2) / (2 * COMFORT_BRAKE)
|
||||
|
||||
def get_safe_obstacle_distance(v_ego, t_follow):
|
||||
return (v_ego**2) / (2 * COMFORT_BRAKE) + t_follow * v_ego + STOP_DISTANCE
|
||||
from openpilot.common.params import Params
|
||||
params = Params()
|
||||
stop_str = params.get("StopDistance", encoding="utf8")
|
||||
stop_distance = float(stop_str) if stop_str else 6.0
|
||||
return (v_ego**2) / (2 * COMFORT_BRAKE) + t_follow * v_ego + stop_distance
|
||||
|
||||
def desired_follow_distance(v_ego, v_lead, t_follow=None):
|
||||
if t_follow is None:
|
||||
@@ -188,11 +233,12 @@ def gen_long_ocp():
|
||||
# from an obstacle at every timestep. This obstacle can be a lead car
|
||||
# or other object. In e2e mode we can use x_position targets as a cost
|
||||
# instead.
|
||||
accel_change = a_ego - prev_a
|
||||
costs = [((x_obstacle - x_ego) - (desired_dist_comfort)) / (v_ego + 10.),
|
||||
x_ego,
|
||||
v_ego,
|
||||
a_ego,
|
||||
a_ego - prev_a,
|
||||
accel_change,
|
||||
j_ego]
|
||||
ocp.model.cost_y_expr = vertcat(*costs)
|
||||
ocp.model.cost_y_expr_e = vertcat(*costs[:-1])
|
||||
@@ -250,8 +296,23 @@ class LongitudinalMpc:
|
||||
self.mode = mode
|
||||
self.dt = dt
|
||||
self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N)
|
||||
self.reset()
|
||||
self.source = SOURCES[2]
|
||||
# Initialize smoothing filters with default time constants
|
||||
self.current_filter_time = LEAD_FILTER_TIME_LOW
|
||||
self.lead_a_filter = FirstOrderFilter(0.0, self.current_filter_time, self.dt)
|
||||
self.lead_v_filter = FirstOrderFilter(0.0, self.current_filter_time, self.dt)
|
||||
# Slew-limited filter factor to avoid abrupt 0.50↔1.00 jumps
|
||||
self.filter_time_factor = 1.0
|
||||
self.slew_per_sec = 1.0
|
||||
# Instance variables to avoid global modifications
|
||||
self.current_x_ego_cost = X_EGO_OBSTACLE_COSTS[0]
|
||||
self.current_j_ego_cost = J_EGO_COSTS[0]
|
||||
self.current_a_change_cost = A_CHANGE_COSTS[0]
|
||||
self.current_dist_adapt = DIST_ADAPTS[0]
|
||||
# Initialize acceleration limits to prevent AttributeError
|
||||
self.cruise_min_a = ACCEL_MIN
|
||||
self.max_a = 1.2 # Default max acceleration
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
# self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N)
|
||||
@@ -298,10 +359,71 @@ class LongitudinalMpc:
|
||||
for i in range(N):
|
||||
self.solver.cost_set(i, 'Zl', Zl)
|
||||
|
||||
def set_weights(self, acceleration_jerk=1.0, danger_jerk=1.0, speed_jerk=1.0, prev_accel_constraint=True, personality=log.LongitudinalPersonality.standard):
|
||||
def set_weights(self, acceleration_jerk=1.0, danger_jerk=1.0, speed_jerk=1.0, prev_accel_constraint=True,
|
||||
personality=log.LongitudinalPersonality.standard, v_ego=0.0, lead_dist=50.0,
|
||||
uncertainty=0.0, accel_reengage=False, panic_bypass=False):
|
||||
# Update parameters based on current speed with interpolation for smooth scaling
|
||||
speed_mph = v_ego * CV.MS_TO_MPH # Convert m/s to mph
|
||||
|
||||
# Use speed-based parameters for smooth scaling across all breakpoints
|
||||
self.current_x_ego_cost = get_speed_based_param(speed_mph, X_EGO_OBSTACLE_COSTS)
|
||||
self.current_j_ego_cost = get_speed_based_param(speed_mph, J_EGO_COSTS)
|
||||
self.current_a_change_cost = get_speed_based_param(speed_mph, A_CHANGE_COSTS)
|
||||
|
||||
# For dist_adapt, start from 0.0 under low speeds while enabling full smooth transitions
|
||||
dist_adapt_array = [0.0, DIST_ADAPTS[1], DIST_ADAPTS[2], DIST_ADAPTS[3]]
|
||||
self.current_dist_adapt = get_speed_based_param(speed_mph, dist_adapt_array)
|
||||
|
||||
# Update filter time constants with interp and recreate filters if needed
|
||||
if speed_mph < 47:
|
||||
self.current_filter_time = 0.0
|
||||
else:
|
||||
self.current_filter_time = interp(speed_mph, [47, 65], [0.0, LEAD_FILTER_TIME_HIGH])
|
||||
if abs(self.current_filter_time - getattr(self, 'prev_filter_time', 0)) > 0.1: # Only update if significant change
|
||||
# Recreate filters with new time constant while preserving current values
|
||||
current_a = self.lead_a_filter.x if hasattr(self.lead_a_filter, 'x') else 0.0
|
||||
current_v = self.lead_v_filter.x if hasattr(self.lead_v_filter, 'x') else 0.0
|
||||
self.lead_a_filter = FirstOrderFilter(current_a, self.current_filter_time, self.dt)
|
||||
self.lead_v_filter = FirstOrderFilter(current_v, self.current_filter_time, self.dt)
|
||||
self.prev_filter_time = self.current_filter_time
|
||||
|
||||
# Adaptive jerk factors for distance with interp scaling
|
||||
dist_factor = 1.0 + self.current_dist_adapt * (20.0 / max(lead_dist, 5.0))
|
||||
acceleration_jerk *= dist_factor
|
||||
danger_jerk *= dist_factor
|
||||
speed_jerk *= dist_factor
|
||||
|
||||
# Scene complexity adjustment based on model uncertainty
|
||||
prev_filter_time_factor = getattr(self, 'prev_filter_time_factor', 1.0)
|
||||
# Target factor from uncertainty
|
||||
if uncertainty <= 0.45:
|
||||
tgt_factor = 1.0
|
||||
elif uncertainty >= 0.70:
|
||||
tgt_factor = 0.0
|
||||
else:
|
||||
tgt_factor = float(np.interp(uncertainty, [0.45, 0.70], [1.0, 0.30]))
|
||||
|
||||
if accel_reengage:
|
||||
tgt_factor = min(tgt_factor, 0.5)
|
||||
|
||||
# Hard bypass of smoothing when approaching fast or magnitude trips
|
||||
if panic_bypass:
|
||||
tgt_factor = 0.0
|
||||
|
||||
# Slew-limit changes to avoid step-wise filter jumps
|
||||
max_step = self.slew_per_sec * self.dt
|
||||
delta = np.clip(tgt_factor - self.filter_time_factor, -max_step, max_step)
|
||||
self.filter_time_factor += float(delta)
|
||||
filter_time_factor = float(self.filter_time_factor)
|
||||
|
||||
# When uncertainty is moderately elevated, allow accel but cap jerk by increasing jerk cost
|
||||
if 0.45 <= uncertainty < 0.60:
|
||||
scale = float(np.interp(uncertainty, [0.45, 0.60], [1.2, 1.5]))
|
||||
speed_jerk *= scale
|
||||
|
||||
if self.mode == 'acc':
|
||||
a_change_cost = acceleration_jerk if prev_accel_constraint else 0
|
||||
cost_weights = [X_EGO_OBSTACLE_COST, X_EGO_COST, V_EGO_COST, A_EGO_COST, a_change_cost, speed_jerk]
|
||||
cost_weights = [self.current_x_ego_cost, X_EGO_COST, V_EGO_COST, A_EGO_COST, a_change_cost, speed_jerk]
|
||||
constraint_cost_weights = [LIMIT_COST, LIMIT_COST, LIMIT_COST, danger_jerk]
|
||||
elif self.mode == 'blended':
|
||||
a_change_cost = 40.0 if prev_accel_constraint else 0
|
||||
@@ -311,6 +433,15 @@ class LongitudinalMpc:
|
||||
raise NotImplementedError(f'Planner mode {self.mode} not recognized in planner cost set')
|
||||
self.set_cost_weights(cost_weights, constraint_cost_weights)
|
||||
|
||||
# Adjust filter time constants for complex scenes
|
||||
if abs(filter_time_factor - getattr(self, 'prev_filter_time_factor', 1.0)) > 0.05:
|
||||
current_a = self.lead_a_filter.x if hasattr(self.lead_a_filter, 'x') else 0.0
|
||||
current_v = self.lead_v_filter.x if hasattr(self.lead_v_filter, 'x') else 0.0
|
||||
new_filter_time = self.current_filter_time * filter_time_factor
|
||||
self.lead_a_filter = FirstOrderFilter(current_a, new_filter_time, self.dt)
|
||||
self.lead_v_filter = FirstOrderFilter(current_v, new_filter_time, self.dt)
|
||||
self.prev_filter_time_factor = filter_time_factor
|
||||
|
||||
def set_cur_state(self, v, a):
|
||||
v_prev = self.x0[1]
|
||||
self.x0[1] = v
|
||||
@@ -320,16 +451,34 @@ class LongitudinalMpc:
|
||||
self.solver.set(i, 'x', self.x0)
|
||||
|
||||
@staticmethod
|
||||
def extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau):
|
||||
a_lead_traj = a_lead * np.exp(-a_lead_tau * (T_IDXS**2)/2.)
|
||||
v_lead_traj = np.clip(v_lead + np.cumsum(T_DIFFS * a_lead_traj), 0.0, 1e8)
|
||||
x_lead_traj = x_lead + np.cumsum(T_DIFFS * v_lead_traj)
|
||||
def extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau, v_ego=0.0):
|
||||
speed_mph = v_ego * CV.MS_TO_MPH
|
||||
bp = [0, 20, 35]
|
||||
exp_weight = interp(speed_mph, bp, [1.0, 1.0, 0.0]) # Full exp at <20, blend to constant at 35
|
||||
|
||||
if exp_weight > 0:
|
||||
# Exponential decay component
|
||||
a_lead_traj_exp = a_lead * np.exp(-a_lead_tau * (T_IDXS**2)/2.)
|
||||
v_lead_traj_exp = np.clip(v_lead + np.cumsum(T_DIFFS * a_lead_traj_exp), 0.0, 1e8)
|
||||
x_lead_traj_exp = x_lead + np.cumsum(T_DIFFS * v_lead_traj_exp)
|
||||
else:
|
||||
x_lead_traj_exp = np.zeros_like(T_IDXS)
|
||||
v_lead_traj_exp = np.zeros_like(T_IDXS)
|
||||
|
||||
# Constant acceleration component
|
||||
v_lead_traj_const = np.clip(v_lead + a_lead * T_IDXS, 0.0, 1e8)
|
||||
x_lead_traj_const = x_lead + v_lead * T_IDXS + 0.5 * a_lead * T_IDXS**2
|
||||
|
||||
# Blend based on weight
|
||||
v_lead_traj = exp_weight * v_lead_traj_exp + (1 - exp_weight) * v_lead_traj_const
|
||||
x_lead_traj = exp_weight * x_lead_traj_exp + (1 - exp_weight) * x_lead_traj_const
|
||||
|
||||
lead_xv = np.column_stack((x_lead_traj, v_lead_traj))
|
||||
return lead_xv
|
||||
|
||||
def process_lead(self, lead):
|
||||
def process_lead(self, lead, tracking_lead=True):
|
||||
v_ego = self.x0[1]
|
||||
if lead is not None and lead.status:
|
||||
if lead is not None and lead.status and tracking_lead:
|
||||
x_lead = lead.dRel
|
||||
v_lead = lead.vLead
|
||||
a_lead = lead.aLeadK
|
||||
@@ -344,18 +493,29 @@ class LongitudinalMpc:
|
||||
# MPC will not converge if immediate crash is expected
|
||||
# Clip lead distance to what is still possible to brake for
|
||||
min_x_lead = ((v_ego + v_lead)/2) * (v_ego - v_lead) / (-ACCEL_MIN * 2)
|
||||
x_lead = np.clip(x_lead, min_x_lead, 1e8)
|
||||
v_lead = np.clip(v_lead, 0.0, 1e8)
|
||||
a_lead = np.clip(a_lead, -10., 5.)
|
||||
lead_xv = self.extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau)
|
||||
x_lead = clip(x_lead, min_x_lead, 1e8)
|
||||
v_lead = clip(v_lead, 0.0, 1e8)
|
||||
a_lead = clip(a_lead, -10., 5.)
|
||||
# Apply smoothing filters with interp scaling
|
||||
self.lead_a_filter.update(a_lead)
|
||||
self.lead_v_filter.update(v_lead)
|
||||
a_lead = self.lead_a_filter.x
|
||||
v_lead = self.lead_v_filter.x
|
||||
lead_xv = self.extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau, v_ego)
|
||||
return lead_xv
|
||||
|
||||
def update(self, radarstate, v_cruise, x, v, a, j, t_follow, frogpilot_toggles, personality=log.LongitudinalPersonality.standard):
|
||||
v_ego = self.x0[1]
|
||||
self.status = radarstate.leadOne.status or radarstate.leadTwo.status
|
||||
def set_accel_limits(self, min_a, max_a):
|
||||
# TODO this sets a max accel limit, but the minimum limit is only for cruise decel
|
||||
# needs refactor
|
||||
self.cruise_min_a = min_a
|
||||
self.max_a = max_a
|
||||
|
||||
lead_xv_0 = self.process_lead(radarstate.leadOne)
|
||||
lead_xv_1 = self.process_lead(radarstate.leadTwo)
|
||||
def update(self, lead_one, lead_two, v_cruise, x, v, a, j, t_follow, tracking_lead, personality=log.LongitudinalPersonality.standard):
|
||||
v_ego = self.x0[1]
|
||||
self.status = lead_one.status and tracking_lead or lead_two.status
|
||||
|
||||
lead_xv_0 = self.process_lead(lead_one, tracking_lead)
|
||||
lead_xv_1 = self.process_lead(lead_two, v_ego)
|
||||
|
||||
# To estimate a safe distance from a moving lead, we calculate how much stopping
|
||||
# distance that lead needs as a minimum. We can add that to the current distance
|
||||
@@ -364,7 +524,8 @@ class LongitudinalMpc:
|
||||
lead_1_obstacle = lead_xv_1[:,0] + get_stopped_equivalence_factor(lead_xv_1[:,1])
|
||||
|
||||
self.params[:,0] = ACCEL_MIN
|
||||
self.params[:,1] = ACCEL_MAX
|
||||
# negative accel constraint causes problems because negative speed is not allowed
|
||||
self.params[:,1] = max(0.0, self.max_a)
|
||||
|
||||
# Update in ACC mode or ACC/e2e blend
|
||||
if self.mode == 'acc':
|
||||
@@ -372,9 +533,9 @@ class LongitudinalMpc:
|
||||
|
||||
# Fake an obstacle for cruise, this ensures smooth acceleration to set speed
|
||||
# when the leads are no factor.
|
||||
v_lower = v_ego + (T_IDXS * CRUISE_MIN_ACCEL * 1.05)
|
||||
v_lower = v_ego + (T_IDXS * self.cruise_min_a * 1.05)
|
||||
# TODO does this make sense when max_a is negative?
|
||||
v_upper = v_ego + (T_IDXS * CRUISE_MAX_ACCEL * 1.05)
|
||||
v_upper = v_ego + (T_IDXS * self.max_a * 1.05)
|
||||
v_cruise_clipped = np.clip(v_cruise * np.ones(N+1),
|
||||
v_lower,
|
||||
v_upper)
|
||||
@@ -415,8 +576,8 @@ class LongitudinalMpc:
|
||||
self.params[:,4] = t_follow
|
||||
|
||||
self.run()
|
||||
if (np.any(lead_xv_0[FCW_IDXS,0] - self.x_sol[FCW_IDXS,0] < CRASH_DISTANCE) and
|
||||
radarstate.leadOne.modelProb > 0.9):
|
||||
lead_probability = lead_one.modelProb
|
||||
if (np.any(lead_xv_0[FCW_IDXS,0] - self.x_sol[FCW_IDXS,0] < CRASH_DISTANCE) and lead_probability > 0.9):
|
||||
self.crash_cnt += 1
|
||||
else:
|
||||
self.crash_cnt = 0
|
||||
@@ -462,11 +623,7 @@ class LongitudinalMpc:
|
||||
|
||||
self.prev_a = np.interp(T_IDXS + self.dt, T_IDXS, self.a_solution)
|
||||
|
||||
t = time.monotonic()
|
||||
if self.solution_status != 0:
|
||||
if t > self.last_cloudlog_t + 5.0:
|
||||
self.last_cloudlog_t = t
|
||||
cloudlog.warning(f"Long mpc reset, solution_status: {self.solution_status}")
|
||||
self.reset()
|
||||
# reset = 1
|
||||
# print(f"long_mpc timings: total internal {self.solve_time:.2e}, external: {(time.monotonic() - t0):.2e} qp {self.time_qp_solution:.2e}, \
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
import math
|
||||
import numpy as np
|
||||
import time
|
||||
from openpilot.common.numpy_fast import clip, interp
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
@@ -11,25 +13,28 @@ from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX
|
||||
from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, V_CRUISE_UNSET, CONTROL_N, get_accel_from_plan
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_UNSET, CONTROL_N, get_speed_error, get_accel_from_plan_tomb_raider
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import MINIMUM_LATERAL_ACCELERATION
|
||||
|
||||
LON_MPC_STEP = 0.2 # first step is 0.2s
|
||||
A_CRUISE_MIN = -1.2
|
||||
A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6]
|
||||
A_CRUISE_MAX_BP = [0., 10.0, 25., 40.]
|
||||
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
|
||||
ALLOW_THROTTLE_THRESHOLD = 0.4
|
||||
ALLOW_THROTTLE_THRESHOLD = 0.5
|
||||
MIN_ALLOW_THROTTLE_SPEED = 2.5
|
||||
|
||||
# Uncertainty-based filter disable thresholds
|
||||
UNCERT_SLOPE_TRIG = 0.12 # per second
|
||||
UNCERT_MAG_TRIG = 0.50
|
||||
|
||||
# Lookup table for turns
|
||||
_A_TOTAL_MAX_V = [1.7, 3.2]
|
||||
_A_TOTAL_MAX_BP = [20., 40.]
|
||||
|
||||
|
||||
def get_max_accel(v_ego):
|
||||
return float(np.interp(v_ego, A_CRUISE_MAX_BP, A_CRUISE_MAX_VALS))
|
||||
return interp(v_ego, A_CRUISE_MAX_BP, A_CRUISE_MAX_VALS)
|
||||
|
||||
def get_coast_accel(pitch):
|
||||
return np.sin(pitch) * -5.65 - 0.3 # fitted from data using xx/projects/allow_throttle/compute_coast_accel.py
|
||||
@@ -42,45 +47,114 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP):
|
||||
"""
|
||||
# FIXME: This function to calculate lateral accel is incorrect and should use the VehicleModel
|
||||
# The lookup table for turns should also be updated if we do this
|
||||
a_total_max = np.interp(v_ego, _A_TOTAL_MAX_BP, _A_TOTAL_MAX_V)
|
||||
a_total_max = interp(v_ego, _A_TOTAL_MAX_BP, _A_TOTAL_MAX_V)
|
||||
a_y = v_ego ** 2 * angle_steers * CV.DEG_TO_RAD / (CP.steerRatio * CP.wheelbase)
|
||||
|
||||
if abs(a_y) > MINIMUM_LATERAL_ACCELERATION:
|
||||
a_x_allowed = math.sqrt(max(a_total_max ** 2 - a_y ** 2, 0.))
|
||||
else:
|
||||
a_x_allowed = a_target[1]
|
||||
a_x_allowed = math.sqrt(max(a_total_max ** 2 - a_y ** 2, 0.))
|
||||
|
||||
return [a_target[0], min(a_target[1], a_x_allowed)]
|
||||
|
||||
|
||||
def get_accel_from_plan_classic(CP, speeds, accels, vEgoStopping):
|
||||
if len(speeds) == CONTROL_N:
|
||||
v_target_now = interp(DT_MDL, CONTROL_N_T_IDX, speeds)
|
||||
a_target_now = interp(DT_MDL, CONTROL_N_T_IDX, accels)
|
||||
|
||||
v_target = interp(CP.longitudinalActuatorDelay + DT_MDL, CONTROL_N_T_IDX, speeds)
|
||||
if v_target != v_target_now:
|
||||
a_target = 2 * (v_target - v_target_now) / CP.longitudinalActuatorDelay - a_target_now
|
||||
else:
|
||||
a_target = a_target_now
|
||||
|
||||
v_target_1sec = interp(CP.longitudinalActuatorDelay + DT_MDL + 1.0, CONTROL_N_T_IDX, speeds)
|
||||
else:
|
||||
v_target = 0.0
|
||||
v_target_1sec = 0.0
|
||||
a_target = 0.0
|
||||
should_stop = (v_target < vEgoStopping and
|
||||
v_target_1sec < vEgoStopping)
|
||||
return a_target, should_stop
|
||||
|
||||
|
||||
def get_accel_from_plan(speeds, accels, action_t=DT_MDL, vEgoStopping=0.05):
|
||||
if len(speeds) == CONTROL_N:
|
||||
v_now = speeds[0]
|
||||
a_now = accels[0]
|
||||
|
||||
v_target = interp(action_t, CONTROL_N_T_IDX, speeds)
|
||||
a_target = 2 * (v_target - v_now) / (action_t) - a_now
|
||||
v_target_1sec = interp(action_t + 1.0, CONTROL_N_T_IDX, speeds)
|
||||
else:
|
||||
v_target = 0.0
|
||||
v_target_1sec = 0.0
|
||||
a_target = 0.0
|
||||
should_stop = (v_target < vEgoStopping and
|
||||
v_target_1sec < vEgoStopping)
|
||||
return a_target, should_stop
|
||||
|
||||
|
||||
class LongitudinalPlanner:
|
||||
def __init__(self, CP, init_v=0.0, init_a=0.0, dt=DT_MDL):
|
||||
self.CP = CP
|
||||
self.mpc = LongitudinalMpc(dt=dt)
|
||||
# TODO remove mpc modes when TR released
|
||||
self.mpc.mode = 'acc'
|
||||
self.fcw = False
|
||||
self.dt = dt
|
||||
self.allow_throttle = True
|
||||
self.mode = 'acc'
|
||||
|
||||
self.generation = None
|
||||
|
||||
self.a_desired = init_a
|
||||
self.v_desired_filter = FirstOrderFilter(init_v, 2.0, self.dt)
|
||||
self.prev_accel_clip = [ACCEL_MIN, ACCEL_MAX]
|
||||
self.output_a_target = 0.0
|
||||
self.output_should_stop = False
|
||||
self.v_model_error = 0.0
|
||||
|
||||
self.v_desired_trajectory = np.zeros(CONTROL_N)
|
||||
self.a_desired_trajectory = np.zeros(CONTROL_N)
|
||||
self.j_desired_trajectory = np.zeros(CONTROL_N)
|
||||
self.solverExecutionTime = 0.0
|
||||
|
||||
# ---- Rubberband mitigation state ----
|
||||
# Two uncertainty tracks (slow/fast) for asymmetric gating
|
||||
self.uncert_slow = FirstOrderFilter(0.0, 1.6, self.dt) # ~lam=0.6
|
||||
self.uncert_fast = FirstOrderFilter(0.0, 0.9, self.dt) # faster cool-down for accel decisions
|
||||
# Lead stability tracking
|
||||
self.prev_lead_dist = None
|
||||
self.last_big_brake_t = 0.0
|
||||
self.stable_lead = False
|
||||
# Temporary accel nudge window
|
||||
self.accel_nudge_until = 0.0
|
||||
|
||||
# Hysteresis gate + dwell for accel re-engage and smoothed lead distance
|
||||
self.accel_gate = False
|
||||
self._t_arm = 0.0
|
||||
self._t_disarm = 0.0
|
||||
self.lead_dist_f = None
|
||||
|
||||
# Uncertainty slope tracking
|
||||
self._uncert_last = 0.0
|
||||
self._uncert_last_t = None
|
||||
|
||||
@property
|
||||
def mlsim(self):
|
||||
return self.generation in ("v8", "v10", "v11", "v12")
|
||||
|
||||
def get_mpc_mode(self) -> str:
|
||||
"""
|
||||
Determine the desired MPC mode: if not ML-SIM, MPC should follow self.mode;
|
||||
otherwise leave MPC.mode unchanged.
|
||||
"""
|
||||
# For non-ML-SIM generations, MPC mode tracks self.mode
|
||||
if not self.mlsim:
|
||||
return self.mode
|
||||
# For ML-SIM (v8), preserve the existing MPC mode
|
||||
return getattr(self.mpc, 'mode', 'acc')
|
||||
|
||||
@staticmethod
|
||||
def parse_model(model_msg, v_ego, taco_tune):
|
||||
def parse_model(model_msg, model_error, v_ego, taco_tune):
|
||||
if (len(model_msg.position.x) == ModelConstants.IDX_N and
|
||||
len(model_msg.velocity.x) == ModelConstants.IDX_N and
|
||||
len(model_msg.acceleration.x) == ModelConstants.IDX_N):
|
||||
x = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.position.x)
|
||||
v = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.velocity.x)
|
||||
x = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.position.x) - model_error * T_IDXS_MPC
|
||||
v = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.velocity.x) - model_error
|
||||
a = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.acceleration.x)
|
||||
j = np.zeros(len(T_IDXS_MPC))
|
||||
else:
|
||||
@@ -90,7 +164,7 @@ class LongitudinalPlanner:
|
||||
j = np.zeros(len(T_IDXS_MPC))
|
||||
|
||||
if taco_tune:
|
||||
max_lat_accel = np.interp(v_ego, [5, 10, 20], [1.5, 2.0, 3.0])
|
||||
max_lat_accel = interp(v_ego, [5, 10, 20], [1.5, 2.0, 3.0])
|
||||
curvatures = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.orientationRate.z) / np.clip(v, 0.3, 100.0)
|
||||
max_v = np.sqrt(max_lat_accel / (np.abs(curvatures) + 1e-3)) - 2.0
|
||||
v = np.minimum(max_v, v)
|
||||
@@ -101,17 +175,22 @@ class LongitudinalPlanner:
|
||||
throttle_prob = 1.0
|
||||
return x, v, a, j, throttle_prob
|
||||
|
||||
def update(self, sm, classic_longitudinal, frogpilot_toggles):
|
||||
mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
|
||||
if classic_longitudinal:
|
||||
self.mpc.mode = mode
|
||||
def update(self, tinygrad_model, sm, frogpilot_toggles):
|
||||
self.generation = frogpilot_toggles.model_version
|
||||
if tinygrad_model:
|
||||
self.mpc.mode = 'acc'
|
||||
self.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
|
||||
else:
|
||||
self.mpc.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
|
||||
if not self.mlsim:
|
||||
self.mpc.mode = self.mode
|
||||
|
||||
if len(sm['carControl'].orientationNED) == 3:
|
||||
accel_coast = get_coast_accel(sm['carControl'].orientationNED[1])
|
||||
else:
|
||||
accel_coast = ACCEL_MAX
|
||||
|
||||
v_ego = sm['carState'].vEgo
|
||||
v_ego = max(sm['carState'].vEgo, sm['carState'].vEgoCluster)
|
||||
v_cruise = sm['frogpilotPlan'].vCruise
|
||||
v_cruise_initialized = sm['controlsState'].vCruise != V_CRUISE_UNSET
|
||||
|
||||
@@ -126,36 +205,190 @@ class LongitudinalPlanner:
|
||||
# No change cost when user is controlling the speed, or when standstill
|
||||
prev_accel_constraint = not (reset_state or sm['carState'].standstill)
|
||||
|
||||
if mode == 'acc':
|
||||
accel_clip = [sm['frogpilotPlan'].minAcceleration, sm['frogpilotPlan'].maxAcceleration]
|
||||
if self.mpc.mode == 'acc':
|
||||
accel_limits = [sm['frogpilotPlan'].minAcceleration, sm['frogpilotPlan'].maxAcceleration]
|
||||
steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg
|
||||
if not sm['frogpilotPlan'].cscControllingSpeed:
|
||||
accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP)
|
||||
accel_limits_turns = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_limits, self.CP)
|
||||
else:
|
||||
accel_clip = [ACCEL_MIN, ACCEL_MAX]
|
||||
accel_limits = [ACCEL_MIN, ACCEL_MAX]
|
||||
accel_limits_turns = [ACCEL_MIN, ACCEL_MAX]
|
||||
|
||||
if reset_state:
|
||||
self.v_desired_filter.x = v_ego
|
||||
# Clip aEgo to cruise limits to prevent large accelerations when becoming active
|
||||
self.a_desired = np.clip(sm['carState'].aEgo, accel_clip[0], accel_clip[1])
|
||||
self.a_desired = clip(sm['carState'].aEgo, accel_limits[0], accel_limits[1])
|
||||
|
||||
# Prevent divergence, smooth in current v_ego
|
||||
self.v_desired_filter.x = max(0.0, self.v_desired_filter.update(v_ego))
|
||||
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], v_ego, frogpilot_toggles.taco_tune)
|
||||
# Compute model v_ego error
|
||||
self.v_model_error = get_speed_error(sm['modelV2'], v_ego)
|
||||
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], self.v_model_error, v_ego, frogpilot_toggles.taco_tune)
|
||||
# Don't clip at low speeds since throttle_prob doesn't account for creep
|
||||
self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED
|
||||
self.allow_throttle &= not sm['frogpilotPlan'].disableThrottle
|
||||
|
||||
if not self.allow_throttle:
|
||||
clipped_accel_coast = max(accel_coast, accel_clip[0])
|
||||
clipped_accel_coast_interp = np.interp(v_ego, [MIN_ALLOW_THROTTLE_SPEED, MIN_ALLOW_THROTTLE_SPEED*2], [accel_clip[1], clipped_accel_coast])
|
||||
accel_clip[1] = min(accel_clip[1], clipped_accel_coast_interp)
|
||||
clipped_accel_coast = max(accel_coast, accel_limits_turns[0])
|
||||
clipped_accel_coast_interp = interp(v_ego, [MIN_ALLOW_THROTTLE_SPEED, MIN_ALLOW_THROTTLE_SPEED*2], [accel_limits_turns[1], clipped_accel_coast])
|
||||
accel_limits_turns[1] = min(accel_limits_turns[1], clipped_accel_coast_interp)
|
||||
|
||||
if force_slow_decel:
|
||||
v_cruise = 0.0
|
||||
# clip limits, cannot init MPC outside of bounds
|
||||
accel_limits_turns[0] = min(accel_limits_turns[0], self.a_desired + 0.05)
|
||||
accel_limits_turns[1] = max(accel_limits_turns[1], self.a_desired - 0.05)
|
||||
|
||||
self.mpc.set_weights(sm['frogpilotPlan'].accelerationJerk, sm['frogpilotPlan'].dangerJerk, sm['frogpilotPlan'].speedJerk, prev_accel_constraint, personality=sm['controlsState'].personality)
|
||||
self.lead_one = sm['radarState'].leadOne
|
||||
self.lead_two = sm['radarState'].leadTwo
|
||||
|
||||
lead_dist = self.lead_one.dRel if self.lead_one.status else 50.0
|
||||
|
||||
# Smooth lead distance (EMA) to avoid chatter in thresholds
|
||||
alpha = max(0.02, min(0.15, 0.05 + 0.002 * v_ego))
|
||||
if self.lead_dist_f is None:
|
||||
self.lead_dist_f = float(lead_dist)
|
||||
else:
|
||||
self.lead_dist_f += alpha * (float(lead_dist) - self.lead_dist_f)
|
||||
|
||||
# Lead stability estimation and recent-brake timer
|
||||
now_t = time.monotonic()
|
||||
# relative speed (ego - lead) positive when closing
|
||||
v_rel = (v_ego - self.lead_one.vLead) if self.lead_one.status else 0.0
|
||||
if self.prev_lead_dist is None:
|
||||
d_rel_dot = 0.0
|
||||
else:
|
||||
d_rel_dot = (lead_dist - self.prev_lead_dist) / max(self.dt, 1e-3)
|
||||
self.prev_lead_dist = lead_dist
|
||||
|
||||
# Remember time of last non-trivial model brake risk
|
||||
if 'raw_brake_max' in locals() and raw_brake_max is not None and raw_brake_max > 0.02:
|
||||
self.last_big_brake_t = now_t
|
||||
|
||||
# Stable lead heuristic (short window, cheap to compute)
|
||||
recently_braked = (now_t - self.last_big_brake_t) < 0.7
|
||||
self.stable_lead = (
|
||||
self.lead_one.status and
|
||||
abs(v_rel) < 0.5 and
|
||||
abs(d_rel_dot) < 0.5 and
|
||||
not recently_braked
|
||||
)
|
||||
|
||||
# Calculate scene uncertainty from model desire prediction entropy and disengage predictions
|
||||
uncertainty = 0.0
|
||||
if hasattr(sm['modelV2'], 'meta'):
|
||||
# Desire prediction entropy (maneuver uncertainty), normalized to [0, 1]
|
||||
desire_entropy = 0.0
|
||||
if hasattr(sm['modelV2'].meta, 'desirePrediction'):
|
||||
desire_probs = sm['modelV2'].meta.desirePrediction
|
||||
if len(desire_probs) > 1:
|
||||
probs = np.asarray(desire_probs, dtype=float)
|
||||
total = float(np.sum(probs))
|
||||
if total > 1e-6:
|
||||
p = probs / total
|
||||
entropy = -np.sum(p * np.log(p + 1e-10))
|
||||
max_entropy = np.log(len(p))
|
||||
desire_entropy = float(entropy / max(max_entropy, 1e-6)) # normalized entropy in [0,1]
|
||||
else:
|
||||
desire_entropy = 0.0 # guard against all-zero vector
|
||||
|
||||
# Disengage prediction risk (intervention likelihood)
|
||||
disengage_risk = 0.0
|
||||
raw_brake_max = -1.0
|
||||
lam = -1.0
|
||||
if hasattr(sm['modelV2'].meta, 'disengagePredictions'):
|
||||
# Use brake press probabilities as primary risk indicator
|
||||
brake_probs = sm['modelV2'].meta.disengagePredictions.brakePressProbs
|
||||
if len(brake_probs) > 0:
|
||||
# Exponentially decayed max over the full horizon
|
||||
probs = np.asarray(brake_probs, dtype=float)
|
||||
# Clip tiny brake blips so they don't inflate uncertainty
|
||||
if float(np.max(probs)) < 0.015:
|
||||
probs = probs * 0.5
|
||||
raw_brake_max = float(np.max(probs))
|
||||
# Time vector assuming model horizon step = DT_MDL
|
||||
t = np.arange(len(probs), dtype=float) * DT_MDL
|
||||
lam = 0.6 # decay rate per second (tunable: 0.5–0.9 typical)
|
||||
weights = np.exp(-lam * t)
|
||||
disengage_risk = float(np.max(probs * weights))
|
||||
|
||||
# Combined uncertainty metric (range roughly 0..2), with dual-track filtering
|
||||
raw_uncertainty = desire_entropy + disengage_risk
|
||||
# Update filters
|
||||
self.uncert_slow.update(raw_uncertainty)
|
||||
self.uncert_fast.update(raw_uncertainty)
|
||||
# Use a more permissive track for accel decisions
|
||||
uncertainty = self.uncert_slow.x
|
||||
uncertainty_accel = min(self.uncert_slow.x, self.uncert_fast.x)
|
||||
|
||||
# --- Slope-based panic bypass ---
|
||||
if self._uncert_last_t is None:
|
||||
uncert_slope = 0.0
|
||||
else:
|
||||
dt_u = max(1e-3, now_t - self._uncert_last_t)
|
||||
uncert_slope = (uncertainty - self._uncert_last) / dt_u
|
||||
self._uncert_last = uncertainty
|
||||
self._uncert_last_t = now_t
|
||||
|
||||
closing_fast = (self.lead_one.status and (v_ego - self.lead_one.vLead) > 0.5)
|
||||
# Trigger if either slope is high or magnitude is high; require a valid lead and closing
|
||||
panic_bypass = closing_fast and (uncert_slope > UNCERT_SLOPE_TRIG or uncertainty >= UNCERT_MAG_TRIG)
|
||||
|
||||
if panic_bypass:
|
||||
try:
|
||||
cloudlog.error(f"LON_SLOPE; slope={uncert_slope:.3f}/s; uncertainty={uncertainty:.3f}; v_ego={v_ego:.2f}; v_rel={(v_ego - self.lead_one.vLead) if self.lead_one.status else 0.0:.2f}; lead_dist={self.lead_dist_f if self.lead_dist_f is not None else -1:.2f}; trigger=True")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# now_t defined earlier
|
||||
over = uncertainty > 1.0
|
||||
|
||||
# Asymmetric accel release with hysteresis + dwell to prevent on/off pulsing
|
||||
rise_dwell_s, fall_dwell_s = 0.6, 0.4
|
||||
good = (
|
||||
(self.a_desired > 0.0) and
|
||||
self.stable_lead and
|
||||
(uncertainty <= 0.425) and
|
||||
(desire_entropy < 0.41)
|
||||
)
|
||||
|
||||
# dwell timers for robust gating
|
||||
if good and not self.accel_gate:
|
||||
if now_t - self._t_arm >= rise_dwell_s:
|
||||
self.accel_gate = True
|
||||
else:
|
||||
self._t_arm = now_t
|
||||
|
||||
if (not good) and self.accel_gate:
|
||||
if now_t - self._t_disarm >= fall_dwell_s:
|
||||
self.accel_gate = False
|
||||
else:
|
||||
self._t_disarm = now_t
|
||||
|
||||
if self.accel_gate:
|
||||
# Ensure some positive headroom for MPC to exit coasting
|
||||
accel_limits_turns[1] = max(accel_limits_turns[1], 0.2)
|
||||
# Short self-canceling nudge to unstick (applied post-MPC)
|
||||
if now_t > self.accel_nudge_until:
|
||||
self.accel_nudge_until = now_t + 0.45
|
||||
|
||||
self.mpc.set_weights(sm['frogpilotPlan'].accelerationJerk,
|
||||
sm['frogpilotPlan'].dangerJerk,
|
||||
sm['frogpilotPlan'].speedJerk,
|
||||
prev_accel_constraint,
|
||||
personality=sm['controlsState'].personality,
|
||||
v_ego=v_ego,
|
||||
lead_dist=self.lead_dist_f if self.lead_dist_f is not None else lead_dist,
|
||||
uncertainty=uncertainty,
|
||||
accel_reengage=self.accel_gate,
|
||||
panic_bypass=panic_bypass)
|
||||
self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1])
|
||||
self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
|
||||
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, sm['frogpilotPlan'].tFollow, frogpilot_toggles, personality=sm['controlsState'].personality)
|
||||
# After deciding the MPC mode via get_mpc_mode(), ensure MPC uses that mode when not mlsim
|
||||
dec_mpc_mode = self.get_mpc_mode()
|
||||
if not self.mlsim:
|
||||
self.mpc.mode = dec_mpc_mode
|
||||
self.mpc.update(self.lead_one, self.lead_two, v_cruise, x, v, a, j, sm['frogpilotPlan'].tFollow,
|
||||
sm['frogpilotPlan'].trackingLead, personality=sm['controlsState'].personality)
|
||||
|
||||
self.a_desired_trajectory_full = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution)
|
||||
self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution)
|
||||
@@ -167,30 +400,41 @@ class LongitudinalPlanner:
|
||||
if self.fcw:
|
||||
cloudlog.info("FCW triggered")
|
||||
|
||||
# Safety checks for rubber-banding mitigation
|
||||
max_jerk = np.max(np.abs(self.mpc.j_solution))
|
||||
max_accel_change = np.max(np.abs(np.diff(self.mpc.a_solution)))
|
||||
if max_jerk > 5.0: # m/s^3
|
||||
cloudlog.warning(f"High jerk detected: {max_jerk:.2f} m/s^3")
|
||||
if max_accel_change > 2.0: # m/s^2
|
||||
cloudlog.warning(f"High acceleration change: {max_accel_change:.2f} m/s^2")
|
||||
|
||||
# Interpolate 0.05 seconds and save as starting point for next iteration
|
||||
a_prev = self.a_desired
|
||||
self.a_desired = float(np.interp(self.dt, CONTROL_N_T_IDX, self.a_desired_trajectory))
|
||||
self.a_desired = float(interp(self.dt, CONTROL_N_T_IDX, self.a_desired_trajectory))
|
||||
self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0
|
||||
|
||||
action_t = frogpilot_toggles.longitudinalActuatorDelay + DT_MDL
|
||||
output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX,
|
||||
action_t=action_t, vEgoStopping=frogpilot_toggles.vEgoStopping)
|
||||
output_a_target_e2e = sm['modelV2'].action.desiredAcceleration
|
||||
output_should_stop_e2e = sm['modelV2'].action.shouldStop
|
||||
# Anticipatory pre-brake to avoid "coming in hot" when closing on a lead
|
||||
if self.lead_one.status:
|
||||
rel_v = max(0.0, v_ego - self.lead_one.vLead)
|
||||
# dynamic time headway adds a small buffer when uncertainty is elevated
|
||||
base_th = 1.6
|
||||
th = base_th + 0.6 * max(0.0, uncertainty - 0.42)
|
||||
desired_gap = th * v_ego
|
||||
if (self.lead_dist_f is not None and self.lead_dist_f < desired_gap and rel_v > 0.5):
|
||||
k_rel, k_unc = 0.04, 0.20
|
||||
pre_brake = k_rel * rel_v + k_unc * max(0.0, uncertainty - 0.42)
|
||||
pre_brake = min(pre_brake, 0.06)
|
||||
self.a_desired = float(self.a_desired - pre_brake)
|
||||
|
||||
if mode == 'acc':
|
||||
output_a_target = output_a_target_mpc
|
||||
self.output_should_stop = output_should_stop_mpc
|
||||
else:
|
||||
output_a_target = min(output_a_target_mpc, output_a_target_e2e)
|
||||
self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc
|
||||
# Apply tiny feed-forward nudge when released and safe
|
||||
if now_t < self.accel_nudge_until and self.a_desired > -0.1:
|
||||
self.a_desired = float(min(self.a_desired + 0.12, get_max_accel(v_ego)))
|
||||
|
||||
for idx in range(2):
|
||||
accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05)
|
||||
self.output_a_target = np.clip(output_a_target, accel_clip[0], accel_clip[1])
|
||||
self.prev_accel_clip = accel_clip
|
||||
# Small deadzone around zero accel to kill micro-dithers
|
||||
if -0.05 < self.a_desired < 0.05:
|
||||
self.a_desired = 0.0
|
||||
|
||||
def publish(self, sm, pm):
|
||||
def publish(self, classic_model, tinygrad_model, sm, pm, frogpilot_toggles):
|
||||
plan_send = messaging.new_message('longitudinalPlan')
|
||||
|
||||
plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState'])
|
||||
@@ -204,13 +448,34 @@ class LongitudinalPlanner:
|
||||
longitudinalPlan.accels = self.a_desired_trajectory.tolist()
|
||||
longitudinalPlan.jerks = self.j_desired_trajectory.tolist()
|
||||
|
||||
longitudinalPlan.hasLead = sm['radarState'].leadOne.status
|
||||
longitudinalPlan.hasLead = self.lead_one.status
|
||||
longitudinalPlan.longitudinalPlanSource = self.mpc.source
|
||||
longitudinalPlan.fcw = self.fcw
|
||||
|
||||
longitudinalPlan.aTarget = float(self.output_a_target)
|
||||
longitudinalPlan.shouldStop = bool(self.output_should_stop)
|
||||
if classic_model:
|
||||
a_target, should_stop = get_accel_from_plan_classic(self.CP, longitudinalPlan.speeds,
|
||||
longitudinalPlan.accels, vEgoStopping=frogpilot_toggles.vEgoStopping)
|
||||
elif tinygrad_model:
|
||||
action_t = self.CP.longitudinalActuatorDelay + DT_MDL
|
||||
output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan_tomb_raider(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX,
|
||||
action_t=action_t, vEgoStopping=frogpilot_toggles.vEgoStopping)
|
||||
output_a_target_e2e = sm['modelV2'].action.desiredAcceleration
|
||||
output_should_stop_e2e = sm['modelV2'].action.shouldStop
|
||||
|
||||
# v9 uses a different longitudinal interface; keep MPC-only behavior even in blended mode
|
||||
if self.mode == 'acc' or self.generation == 'v9':
|
||||
a_target = output_a_target_mpc
|
||||
should_stop = output_should_stop_mpc
|
||||
else:
|
||||
a_target = min(output_a_target_mpc, output_a_target_e2e)
|
||||
should_stop = output_should_stop_e2e or output_should_stop_mpc
|
||||
else:
|
||||
action_t = self.CP.longitudinalActuatorDelay + DT_MDL
|
||||
a_target, should_stop = get_accel_from_plan(longitudinalPlan.speeds, longitudinalPlan.accels,
|
||||
action_t=action_t, vEgoStopping=frogpilot_toggles.vEgoStopping)
|
||||
longitudinalPlan.aTarget = float(a_target)
|
||||
longitudinalPlan.shouldStop = bool(should_stop) or sm['frogpilotPlan'].forcingStopLength < 1
|
||||
longitudinalPlan.allowBrake = True
|
||||
longitudinalPlan.allowThrottle = bool(self.allow_throttle)
|
||||
longitudinalPlan.allowThrottle = self.allow_throttle
|
||||
|
||||
pm.send('longitudinalPlan', plan_send)
|
||||
|
||||
@@ -2,11 +2,10 @@ import numpy as np
|
||||
from numbers import Number
|
||||
|
||||
class PIDController:
|
||||
def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
|
||||
def __init__(self, k_p, k_i, k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100, pos_p_limit=None, neg_p_limit=None):
|
||||
self._k_p = k_p
|
||||
self._k_i = k_i
|
||||
self._k_d = k_d
|
||||
self.k_f = k_f # feedforward gain
|
||||
if isinstance(self._k_p, Number):
|
||||
self._k_p = [[0], [self._k_p]]
|
||||
if isinstance(self._k_i, Number):
|
||||
@@ -16,7 +15,7 @@ class PIDController:
|
||||
|
||||
self.set_limits(pos_limit, neg_limit)
|
||||
|
||||
self.i_rate = 1.0 / rate
|
||||
self.i_dt = 1.0 / rate
|
||||
self.speed = 0.0
|
||||
|
||||
self.reset()
|
||||
@@ -46,12 +45,12 @@ class PIDController:
|
||||
|
||||
def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False):
|
||||
self.speed = speed
|
||||
self.p = float(error) * self.k_p
|
||||
self.f = feedforward * self.k_f
|
||||
self.d = error_rate * self.k_d
|
||||
self.p = self.k_p * float(error)
|
||||
self.d = self.k_d * error_rate
|
||||
self.f = feedforward
|
||||
|
||||
if not freeze_integrator:
|
||||
i = self.i + error * self.k_i * self.i_rate
|
||||
i = self.i + self.k_i * self.i_dt * error
|
||||
|
||||
# Don't allow windup if already clipping
|
||||
test_control = self.p + i + self.d + self.f
|
||||
|
||||
@@ -37,13 +37,11 @@ def plannerd_thread():
|
||||
# FrogPilot variables
|
||||
frogpilot_toggles = get_frogpilot_toggles()
|
||||
|
||||
classic_longitudinal = frogpilot_toggles.classic_longitudinal
|
||||
|
||||
while True:
|
||||
sm.update()
|
||||
if sm.updated['modelV2']:
|
||||
longitudinal_planner.update(sm, classic_longitudinal, frogpilot_toggles)
|
||||
longitudinal_planner.publish(sm, pm)
|
||||
longitudinal_planner.update(False, sm, frogpilot_toggles)
|
||||
longitudinal_planner.publish(False, False, sm, pm, frogpilot_toggles)
|
||||
publish_ui_plan(sm, pm, longitudinal_planner)
|
||||
|
||||
# Update FrogPilot variables
|
||||
|
||||
@@ -16,9 +16,10 @@ from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.simple_kalman import KF1D
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import THRESHOLD, get_frogpilot_toggles
|
||||
from openpilot.selfdrive.controls.controlsd import LaneChangeDirection, LaneChangeState
|
||||
|
||||
# Default lead acceleration decay set to 50% at 1s
|
||||
_LEAD_ACCEL_TAU = 1.5
|
||||
_LEAD_ACCEL_TAU = 0.6
|
||||
|
||||
# radar tracks
|
||||
SPEED, ACCEL = 0, 1 # Kalman filter states enum
|
||||
@@ -84,7 +85,7 @@ class Track:
|
||||
|
||||
# Learn if constant acceleration
|
||||
if abs(self.aLeadK) < 0.5:
|
||||
self.aLeadTau.x = _LEAD_ACCEL_TAU
|
||||
self.aLeadTau.x = min(max(self.aLeadTau.x, 1e-2) * 1.1, _LEAD_ACCEL_TAU)
|
||||
else:
|
||||
self.aLeadTau.update(0.0)
|
||||
|
||||
@@ -149,7 +150,15 @@ def laplacian_pdf(x: float, mu: float, b: float):
|
||||
return math.exp(-abs(x-mu)/b)
|
||||
|
||||
|
||||
def match_vision_to_track(v_ego: float, lead: capnp._DynamicStructReader, tracks: dict[int, Track]):
|
||||
def match_vision_to_track(v_ego: float, lead: capnp._DynamicStructReader, model_data: capnp._DynamicStructReader, tracks: dict[int, Track], frogpilot_toggles: SimpleNamespace):
|
||||
if model_data.meta.laneChangeState == LaneChangeState.laneChangeStarting and getattr(frogpilot_toggles, "human_lane_changes", False):
|
||||
direction = model_data.meta.laneChangeDirection
|
||||
|
||||
if direction == LaneChangeDirection.left:
|
||||
tracks = {k: v for k, v in tracks.items() if v.yRel > 0}
|
||||
elif direction == LaneChangeDirection.right:
|
||||
tracks = {k: v for k, v in tracks.items() if v.yRel < 0}
|
||||
|
||||
offset_vision_dist = lead.x[0] - RADAR_TO_CAMERA
|
||||
|
||||
def prob(c):
|
||||
@@ -173,14 +182,16 @@ def match_vision_to_track(v_ego: float, lead: capnp._DynamicStructReader, tracks
|
||||
|
||||
|
||||
def get_RadarState_from_vision(lead_msg: capnp._DynamicStructReader, v_ego: float, model_v_ego: float):
|
||||
lead_v_rel_pred = lead_msg.v[0] - model_v_ego
|
||||
prev_aLeadK = getattr(get_RadarState_from_vision, "prev_aLeadK", 0.0)
|
||||
blended_aLeadK = 0.8 * float(lead_msg.a[0]) + 0.2 * prev_aLeadK
|
||||
get_RadarState_from_vision.prev_aLeadK = blended_aLeadK
|
||||
return {
|
||||
"dRel": float(lead_msg.x[0] - RADAR_TO_CAMERA),
|
||||
"yRel": float(-lead_msg.y[0]),
|
||||
"vRel": float(lead_v_rel_pred),
|
||||
"vLead": float(v_ego + lead_v_rel_pred),
|
||||
"vLeadK": float(v_ego + lead_v_rel_pred),
|
||||
"aLeadK": float(lead_msg.a[0]),
|
||||
"vRel": float(lead_msg.v[0] - model_v_ego),
|
||||
"vLead": float(v_ego + (lead_msg.v[0] - model_v_ego)),
|
||||
"vLeadK": float(v_ego + (lead_msg.v[0] - model_v_ego)),
|
||||
"aLeadK": blended_aLeadK,
|
||||
"aLeadTau": 0.3,
|
||||
"fcw": False,
|
||||
"modelProb": float(lead_msg.prob),
|
||||
@@ -192,11 +203,11 @@ def get_RadarState_from_vision(lead_msg: capnp._DynamicStructReader, v_ego: floa
|
||||
|
||||
def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capnp._DynamicStructReader,
|
||||
model_v_ego: float, model_data: capnp._DynamicStructReader, standstill: bool,
|
||||
frogpilot_toggles: SimpleNamespace, frogpilotPlan: capnp._DynamicStructReader,
|
||||
frogpilotPlan: capnp._DynamicStructReader, frogpilot_toggles: SimpleNamespace,
|
||||
low_speed_override: bool = True) -> dict[str, Any]:
|
||||
# Determine leads, this is where the essential logic happens
|
||||
if len(tracks) > 0 and ready and lead_msg.prob > frogpilot_toggles.lead_detection_probability:
|
||||
track = match_vision_to_track(v_ego, lead_msg, tracks)
|
||||
track = match_vision_to_track(v_ego, lead_msg, model_data, tracks, frogpilot_toggles)
|
||||
else:
|
||||
track = None
|
||||
|
||||
@@ -313,8 +324,8 @@ class RadarD:
|
||||
model_v_ego = self.v_ego
|
||||
leads_v3 = sm['modelV2'].leadsV3
|
||||
if len(leads_v3) > 1:
|
||||
self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, sm['modelV2'], sm['carState'].standstill, self.frogpilot_toggles, sm['frogpilotPlan'], low_speed_override=True)
|
||||
self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, sm['modelV2'], sm['carState'].standstill, self.frogpilot_toggles, sm['frogpilotPlan'], low_speed_override=False)
|
||||
self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, sm['modelV2'], sm['carState'].standstill, sm['frogpilotPlan'], self.frogpilot_toggles, low_speed_override=True)
|
||||
self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, sm['modelV2'], sm['carState'].standstill, sm['frogpilotPlan'], self.frogpilot_toggles, low_speed_override=False)
|
||||
|
||||
if self.frogpilot_toggles.adjacent_lead_tracking and self.ready:
|
||||
self.frogpilot_radar_state.leadLeft = get_adjacent_lead(self.tracks, sm['carState'].standstill, sm['modelV2'], left=True)
|
||||
|
||||
@@ -187,8 +187,6 @@ def main():
|
||||
# FrogPilot variables
|
||||
frogpilot_toggles = get_frogpilot_toggles()
|
||||
|
||||
with custom.FrogPilotCarParams.from_bytes(params_reader.get("FrogPilotCarParams", block=True)) as msg:
|
||||
FPCP = msg
|
||||
|
||||
while True:
|
||||
sm.update()
|
||||
@@ -240,7 +238,7 @@ def main():
|
||||
0.2 <= liveParameters.stiffnessFactor <= 5.0,
|
||||
min_sr <= liveParameters.steerRatio <= max_sr,
|
||||
))
|
||||
if CP.carFingerprint == "RAM_HD" or CP.carName == "subaru" and FPCP.lateralTuning.which() == "torque":
|
||||
if CP.carFingerprint == "RAM_HD" or CP.carName == "subaru" and CP.lateralTuning.which() == "torque":
|
||||
liveParameters.valid = True
|
||||
liveParameters.steerRatioStd = float(P[States.STEER_RATIO].item())
|
||||
liveParameters.stiffnessFactorStd = float(P[States.STIFFNESS].item())
|
||||
|
||||
@@ -34,7 +34,8 @@ MIN_BUCKET_POINTS = np.array([100, 300, 500, 500, 500, 500, 300, 100])
|
||||
MIN_ENGAGE_BUFFER = 2 # secs
|
||||
|
||||
VERSION = 1 # bump this to invalidate old parameter caches
|
||||
ALLOWED_CARS = ['toyota', 'hyundai']
|
||||
FULL_AUTO_CARS = ['toyota', 'hyundai']
|
||||
FRICTION_ONLY_CARS = ['gm']
|
||||
|
||||
|
||||
def slope2rot(slope):
|
||||
@@ -52,7 +53,7 @@ class TorqueBuckets(PointBuckets):
|
||||
|
||||
|
||||
class TorqueEstimator(ParameterEstimator):
|
||||
def __init__(self, CP, FPCP, decimated=False):
|
||||
def __init__(self, CP, decimated=False):
|
||||
self.hist_len = int(HISTORY / DT_MDL)
|
||||
self.lag = 0.0
|
||||
if decimated:
|
||||
@@ -72,11 +73,14 @@ class TorqueEstimator(ParameterEstimator):
|
||||
self.offline_friction = 0.0
|
||||
self.offline_latAccelFactor = 0.0
|
||||
self.resets = 0.0
|
||||
self.use_params = CP.carName in ALLOWED_CARS and FPCP.lateralTuning.which() == 'torque'
|
||||
self.allow_lat_accel_learning = CP.carName in FULL_AUTO_CARS and CP.lateralTuning.which() == 'torque'
|
||||
self.allow_friction_learning = (self.allow_lat_accel_learning or CP.carName in FRICTION_ONLY_CARS) \
|
||||
and CP.lateralTuning.which() == 'torque'
|
||||
self.use_params = self.allow_friction_learning
|
||||
|
||||
if FPCP.lateralTuning.which() == 'torque':
|
||||
self.offline_friction = FPCP.lateralTuning.torque.friction
|
||||
self.offline_latAccelFactor = FPCP.lateralTuning.torque.latAccelFactor
|
||||
if CP.lateralTuning.which() == 'torque':
|
||||
self.offline_friction = CP.lateralTuning.torque.friction
|
||||
self.offline_latAccelFactor = CP.lateralTuning.torque.latAccelFactor
|
||||
|
||||
self.reset()
|
||||
|
||||
@@ -102,13 +106,13 @@ class TorqueEstimator(ParameterEstimator):
|
||||
cache_ltp = log_evt.liveTorqueParameters
|
||||
with car.CarParams.from_bytes(params_cache) as msg:
|
||||
cache_CP = msg
|
||||
if self.get_restore_key(cache_CP, FPCP, cache_ltp.version) == self.get_restore_key(CP, FPCP, VERSION):
|
||||
if self.get_restore_key(cache_CP, cache_ltp.version) == self.get_restore_key(CP, VERSION):
|
||||
if cache_ltp.liveValid:
|
||||
initial_params = {
|
||||
'latAccelFactor': cache_ltp.latAccelFactorFiltered,
|
||||
'latAccelOffset': cache_ltp.latAccelOffsetFiltered,
|
||||
'frictionCoefficient': cache_ltp.frictionCoefficientFiltered
|
||||
}
|
||||
if self.allow_lat_accel_learning:
|
||||
initial_params['latAccelFactor'] = cache_ltp.latAccelFactorFiltered
|
||||
initial_params['latAccelOffset'] = cache_ltp.latAccelOffsetFiltered
|
||||
if self.allow_friction_learning:
|
||||
initial_params['frictionCoefficient'] = cache_ltp.frictionCoefficientFiltered
|
||||
initial_params['points'] = cache_ltp.points
|
||||
self.decay = cache_ltp.decay
|
||||
self.filtered_points.load_points(initial_params['points'])
|
||||
@@ -121,12 +125,12 @@ class TorqueEstimator(ParameterEstimator):
|
||||
for param in initial_params:
|
||||
self.filtered_params[param] = FirstOrderFilter(initial_params[param], self.decay, DT_MDL)
|
||||
|
||||
def get_restore_key(self, CP, FPCP, version):
|
||||
def get_restore_key(self, CP, version):
|
||||
a, b = None, None
|
||||
if FPCP.lateralTuning.which() == 'torque':
|
||||
a = FPCP.lateralTuning.torque.friction
|
||||
b = FPCP.lateralTuning.torque.latAccelFactor
|
||||
return (CP.carFingerprint, FPCP.lateralTuning.which(), a, b, version)
|
||||
if CP.lateralTuning.which() == 'torque':
|
||||
a = CP.lateralTuning.torque.friction
|
||||
b = CP.lateralTuning.torque.latAccelFactor
|
||||
return (CP.carFingerprint, CP.lateralTuning.which(), a, b, version)
|
||||
|
||||
def reset(self):
|
||||
self.resets += 1.0
|
||||
@@ -155,6 +159,10 @@ class TorqueEstimator(ParameterEstimator):
|
||||
def update_params(self, params):
|
||||
self.decay = min(self.decay + DT_MDL, MAX_FILTER_DECAY)
|
||||
for param, value in params.items():
|
||||
if param.startswith('latAccel') and not self.allow_lat_accel_learning:
|
||||
continue
|
||||
if param == 'frictionCoefficient' and not self.allow_friction_learning:
|
||||
continue
|
||||
self.filtered_params[param].update(value)
|
||||
self.filtered_params[param].update_alpha(self.decay)
|
||||
|
||||
@@ -227,14 +235,14 @@ def main(demo=False):
|
||||
sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveLocationKalman', 'liveDelay', 'frogpilotPlan'], poll='liveLocationKalman')
|
||||
|
||||
params = Params()
|
||||
with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP, custom.FrogPilotCarParams.from_bytes(params.get("FrogPilotCarParams", block=True)) as FPCP:
|
||||
estimator = TorqueEstimator(CP, FPCP)
|
||||
with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP:
|
||||
estimator = TorqueEstimator(CP)
|
||||
|
||||
# FrogPilot variables
|
||||
frogpilot_toggles = get_frogpilot_toggles()
|
||||
|
||||
if not frogpilot_toggles.liveValid:
|
||||
estimator = TorqueEstimator(CP, FPCP, decimated=True)
|
||||
estimator = TorqueEstimator(CP, decimated=True)
|
||||
|
||||
while True:
|
||||
sm.update()
|
||||
|
||||
@@ -33,12 +33,10 @@ class DRIVER_MONITOR_SETTINGS:
|
||||
self._SG_THRESHOLD = 0.9
|
||||
self._BLINK_THRESHOLD = 0.865
|
||||
|
||||
self._EE_THRESH11 = 0.25
|
||||
self._EE_THRESH12 = 7.5
|
||||
self._EE_THRESH11 = 0.6
|
||||
self._EE_THRESH12 = 15.0
|
||||
self._EE_MAX_OFFSET1 = 0.06
|
||||
self._EE_MIN_OFFSET1 = 0.025
|
||||
self._EE_THRESH21 = 0.01
|
||||
self._EE_THRESH22 = 0.35
|
||||
|
||||
self._POSE_PITCH_THRESHOLD = 0.3133
|
||||
self._POSE_PITCH_THRESHOLD_SLACK = 0.3237
|
||||
@@ -46,9 +44,9 @@ class DRIVER_MONITOR_SETTINGS:
|
||||
self._POSE_YAW_THRESHOLD = 0.4020
|
||||
self._POSE_YAW_THRESHOLD_SLACK = 0.5042
|
||||
self._POSE_YAW_THRESHOLD_STRICT = self._POSE_YAW_THRESHOLD
|
||||
self._PITCH_NATURAL_OFFSET = 0.029 # initial value before offset is learned
|
||||
self._PITCH_NATURAL_OFFSET = 0.011 # initial value before offset is learned
|
||||
self._PITCH_NATURAL_THRESHOLD = 0.449
|
||||
self._YAW_NATURAL_OFFSET = 0.097 # initial value before offset is learned
|
||||
self._YAW_NATURAL_OFFSET = 0.075 # initial value before offset is learned
|
||||
self._PITCH_MAX_OFFSET = 0.124
|
||||
self._PITCH_MIN_OFFSET = -0.0881
|
||||
self._YAW_MAX_OFFSET = 0.289
|
||||
@@ -57,7 +55,7 @@ class DRIVER_MONITOR_SETTINGS:
|
||||
self._POSESTD_THRESHOLD = 0.3
|
||||
self._HI_STD_FALLBACK_TIME = int(10 / self._DT_DMON) # fall back to wheel touch if model is uncertain for 10s
|
||||
self._DISTRACTED_FILTER_TS = 0.25 # 0.6Hz
|
||||
self._ALWAYS_ON_ALERT_MIN_SPEED = 7
|
||||
self._ALWAYS_ON_ALERT_MIN_SPEED = 11
|
||||
|
||||
self._POSE_CALIB_MIN_SPEED = 13 # 30 mph
|
||||
self._POSE_OFFSET_MIN_COUNT = int(60 / self._DT_DMON) # valid data counts before calibration completes, 1min cumulative
|
||||
@@ -136,11 +134,8 @@ class DriverMonitoring:
|
||||
self.pose = DriverPose(self.settings._POSE_OFFSET_MAX_COUNT)
|
||||
self.blink = DriverBlink()
|
||||
self.eev1 = 0.
|
||||
self.eev2 = 1.
|
||||
self.ee1_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT)
|
||||
self.ee2_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT)
|
||||
self.ee1_calibrated = False
|
||||
self.ee2_calibrated = False
|
||||
|
||||
self.always_on = always_on
|
||||
self.distracted_types = []
|
||||
@@ -258,7 +253,7 @@ class DriverMonitoring:
|
||||
driver_data = driver_state.rightDriverData if self.wheel_on_right else driver_state.leftDriverData
|
||||
if not all(len(x) > 0 for x in (driver_data.faceOrientation, driver_data.facePosition,
|
||||
driver_data.faceOrientationStd, driver_data.facePositionStd,
|
||||
driver_data.readyProb, driver_data.notReadyProb)):
|
||||
driver_data.notReadyProb)):
|
||||
return
|
||||
|
||||
self.face_detected = driver_data.faceProb > self.settings._FACE_THRESHOLD
|
||||
@@ -275,7 +270,6 @@ class DriverMonitoring:
|
||||
self.blink.right = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) \
|
||||
* (driver_data.sunglassesProb < self.settings._SG_THRESHOLD)
|
||||
self.eev1 = driver_data.notReadyProb[0]
|
||||
self.eev2 = driver_data.readyProb[0]
|
||||
|
||||
self.distracted_types = self._get_distracted_types()
|
||||
self.driver_distracted = (DistractedType.DISTRACTED_E2E in self.distracted_types or DistractedType.DISTRACTED_POSE in self.distracted_types
|
||||
@@ -289,12 +283,10 @@ class DriverMonitoring:
|
||||
self.pose.pitch_offseter.push_and_update(self.pose.pitch)
|
||||
self.pose.yaw_offseter.push_and_update(self.pose.yaw)
|
||||
self.ee1_offseter.push_and_update(self.eev1)
|
||||
self.ee2_offseter.push_and_update(self.eev2)
|
||||
|
||||
self.pose.calibrated = self.pose.pitch_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT and \
|
||||
self.pose.yaw_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
|
||||
self.ee1_calibrated = self.ee1_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
|
||||
self.ee2_calibrated = self.ee2_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
|
||||
|
||||
self.is_model_uncertain = self.hi_stds > self.settings._HI_STD_FALLBACK_TIME
|
||||
self._set_timers(self.face_detected and not self.is_model_uncertain)
|
||||
|
||||
@@ -24,8 +24,6 @@ def make_msg(face_detected, distracted=False, model_uncertain=False):
|
||||
ds.leftDriverData.rightBlinkProb = 1. * distracted
|
||||
ds.leftDriverData.faceOrientationStd = [1.*model_uncertain, 1.*model_uncertain, 1.*model_uncertain]
|
||||
ds.leftDriverData.facePositionStd = [1.*model_uncertain, 1.*model_uncertain]
|
||||
# TODO: test both separately when e2e is used
|
||||
ds.leftDriverData.readyProb = [0., 0., 0., 0.]
|
||||
ds.leftDriverData.notReadyProb = [0., 0.]
|
||||
return ds
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -32,7 +32,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
// automatic updates toggle
|
||||
ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"),
|
||||
tr("FrogPilot will automatically update itself and it's assets when you're offroad and have an active internet connection."), "");
|
||||
automaticUpdatesToggle->setVisible(params.getBool("IsReleaseBranch"));
|
||||
addItem(automaticUpdatesToggle);
|
||||
|
||||
// download update btn
|
||||
|
||||
Binary file not shown.
@@ -431,6 +431,41 @@
|
||||
<translation>بدء تشغيل الكاميرا</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExpandableMultiOptionDialog</name>
|
||||
<message>
|
||||
<source>Sort by:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Alphabetical</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Date (Newest)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Date (Oldest)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Favorites First</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select</source>
|
||||
<translation type="unfinished">اختيار</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished">إلغاء</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 (Community Fav)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExperimentalModeButton</name>
|
||||
<message>
|
||||
@@ -1164,6 +1199,22 @@
|
||||
<source><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>
|
||||
@@ -2075,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>
|
||||
@@ -2476,6 +2563,94 @@
|
||||
<source>Select a Model — 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC</source>
|
||||
<translation type="gpt-5-generated">اختر نموذجًا — 🗺️ = الملاحة | 📡 = الرادار | 👀 = VOACC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically download new driving models as they become available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete driving models from the device.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download driving models to the device.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Recovery Power</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stop Distance</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add or remove models from the <b>Model Randomizer</b>'s blacklist list.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset or view the saved ratings for the driving models.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select the active driving model.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom Series</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no more models to blacklist! The only available model is "%1"!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model to add to the blacklist</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model to remove from the blacklist</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to remove all of your blacklisted models?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset all of your model drives and scores?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The "Model Randomizer" only works with downloaded models. Do you want to download all the driving models?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset your <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>
|
||||
@@ -3631,6 +3806,22 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
|
||||
<source>comma Pedal Support</source>
|
||||
<translation type="gpt-5-generated">دعم comma Pedal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use Pedal for Longitudinal Control</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><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>
|
||||
@@ -4322,6 +4513,10 @@ Developer - Highly customizable settings for seasoned enthusiasts</source>
|
||||
<source><b>Set the road-edge thickness.</b><br><br>Default matches half of the MUTCD lane-line width standard of 10 centimeters.</source>
|
||||
<translation type="gpt-5-generated"><b>اضبط سماكة حافة الطريق.</b><br><br>القيمة الافتراضية تطابق نصف معيار MUTCD لعرض خط المسار وهو 10 سنتيمترات.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Driving Model: Current</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotWheelPanel</name>
|
||||
|
||||
Binary file not shown.
@@ -432,6 +432,41 @@
|
||||
<translation type="gpt-5-generated">camera start now</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExpandableMultiOptionDialog</name>
|
||||
<message>
|
||||
<source>Sort by:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Alphabetical</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Date (Newest)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Date (Oldest)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Favorites First</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select</source>
|
||||
<translation type="unfinished">Pick</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished">Stop</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 (Community Fav)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExperimentalModeButton</name>
|
||||
<message>
|
||||
@@ -1165,6 +1200,22 @@
|
||||
<source><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>
|
||||
@@ -2077,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>
|
||||
@@ -2478,6 +2565,94 @@
|
||||
<source>Select a Model — 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC</source>
|
||||
<translation type="gpt-5-generated">Pick Model — 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically download new driving models as they become available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete driving models from the device.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download driving models to the device.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Recovery Power</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stop Distance</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add or remove models from the <b>Model Randomizer</b>'s blacklist list.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset or view the saved ratings for the driving models.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select the active driving model.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom Series</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no more models to blacklist! The only available model is "%1"!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model to add to the blacklist</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model to remove from the blacklist</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to remove all of your blacklisted models?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset all of your model drives and scores?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The "Model Randomizer" only works with downloaded models. Do you want to download all the driving models?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset your <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>
|
||||
@@ -3635,6 +3810,22 @@ Developer - Many custom setting for seasoned enthusiast</translation>
|
||||
<source>comma Pedal Support</source>
|
||||
<translation type="gpt-5-generated">comma Pedal Help</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use Pedal for Longitudinal Control</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><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>
|
||||
@@ -4328,6 +4519,10 @@ Developer - Many custom setting for seasoned enthusiast</translation>
|
||||
<source><b>Set the road-edge thickness.</b><br><br>Default matches half of the MUTCD lane-line width standard of 10 centimeters.</source>
|
||||
<translation type="gpt-5-generated"><b>Set road-edge thickness.</b><br><br>Default same as half MUTCD lane-line width standard, 10 centimeters.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Driving Model: Current</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotWheelPanel</name>
|
||||
|
||||
Binary file not shown.
@@ -431,6 +431,41 @@
|
||||
<translation>Kamera startet</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExpandableMultiOptionDialog</name>
|
||||
<message>
|
||||
<source>Sort by:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Alphabetical</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Date (Newest)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Date (Oldest)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Favorites First</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select</source>
|
||||
<translation type="unfinished">Auswählen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished">Abbrechen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 (Community Fav)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExperimentalModeButton</name>
|
||||
<message>
|
||||
@@ -1164,6 +1199,22 @@
|
||||
<source><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>
|
||||
@@ -2075,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>
|
||||
@@ -2476,6 +2563,94 @@
|
||||
<source>Select a Model — 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC</source>
|
||||
<translation type="gpt-5-generated">Modell auswählen — 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically download new driving models as they become available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete driving models from the device.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download driving models to the device.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Recovery Power</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stop Distance</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add or remove models from the <b>Model Randomizer</b>'s blacklist list.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset or view the saved ratings for the driving models.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select the active driving model.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom Series</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no more models to blacklist! The only available model is "%1"!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model to add to the blacklist</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model to remove from the blacklist</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to remove all of your blacklisted models?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset all of your model drives and scores?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The "Model Randomizer" only works with downloaded models. Do you want to download all the driving models?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset your <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>
|
||||
@@ -3631,6 +3806,22 @@ Entwickler – Hochgradig anpassbare Einstellungen für versierte Enthusiasten</
|
||||
<source>comma Pedal Support</source>
|
||||
<translation type="gpt-5-generated">Unterstützung für comma Pedal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use Pedal for Longitudinal Control</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><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>
|
||||
@@ -4322,6 +4513,10 @@ Entwickler – Hochgradig anpassbare Einstellungen für versierte Enthusiasten</
|
||||
<source><b>Set the road-edge thickness.</b><br><br>Default matches half of the MUTCD lane-line width standard of 10 centimeters.</source>
|
||||
<translation type="gpt-5-generated"><b>Stellen Sie die Randstreifendicke ein.</b><br><br>Standard entspricht der Hälfte des MUTCD-Standards für Fahrbahnmarkierungsbreite von 10 Zentimetern.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Driving Model: Current</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotWheelPanel</name>
|
||||
|
||||
Binary file not shown.
@@ -431,6 +431,41 @@
|
||||
<translation type="gpt-5-generated">Quack! Camera starting—waddle on!</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExpandableMultiOptionDialog</name>
|
||||
<message>
|
||||
<source>Sort by:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Alphabetical</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Date (Newest)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Date (Oldest)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Favorites First</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select</source>
|
||||
<translation type="unfinished">Quack-select!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 (Community Fav)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExperimentalModeButton</name>
|
||||
<message>
|
||||
@@ -1166,6 +1201,22 @@
|
||||
<source><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>
|
||||
@@ -2077,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>
|
||||
@@ -2478,6 +2565,94 @@
|
||||
<source>Select a Model — 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC</source>
|
||||
<translation type="gpt-5-generated">Quack a Model — 🗺️ = Navigation, 📡 = Radar, 👀 = VOACC, waddle!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically download new driving models as they become available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete driving models from the device.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download driving models to the device.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Recovery Power</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adjust the strength of planplus lane recovery corrections (0.5 to 2.0).</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stop Distance</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add or remove models from the <b>Model Randomizer</b>'s blacklist list.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset or view the saved ratings for the driving models.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select the active driving model.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom Series</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There are no more models to blacklist! The only available model is "%1"!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model to add to the blacklist</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model to remove from the blacklist</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to remove all of your blacklisted models?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset all of your model drives and scores?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The "Model Randomizer" only works with downloaded models. Do you want to download all the driving models?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset your <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>
|
||||
@@ -3633,6 +3808,22 @@ Developer - Ultra-custom settings for seasoned duckthusiasts</translation>
|
||||
<source>comma Pedal Support</source>
|
||||
<translation type="gpt-5-generated">Quack! comma Pedal Support, waddle on!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use Pedal for Longitudinal Control</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><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>
|
||||
@@ -4324,6 +4515,10 @@ Developer - Ultra-custom settings for seasoned duckthusiasts</translation>
|
||||
<source><b>Set the road-edge thickness.</b><br><br>Default matches half of the MUTCD lane-line width standard of 10 centimeters.</source>
|
||||
<translation type="gpt-5-generated"><b>Quack! Set the road-edge thickness, waddlers.</b><br><br>Default quacks to half the MUTCD lane-line width standard of 10 centimeters.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Driving Model: Current</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FrogPilotWheelPanel</name>
|
||||
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user