mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-08-05 15:35:40 +08:00
Merge branch 'master' into fp-KIA_FORTE_2019_NON_SCC
This commit is contained in:
@@ -153,6 +153,7 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
|
||||
navigation @1;
|
||||
vision @2;
|
||||
policy @3;
|
||||
offPolicy @4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"SshEnabled", {PERSISTENT | BACKUP, BOOL}},
|
||||
{"TermsVersion", {PERSISTENT, STRING}},
|
||||
{"TorqueBar", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"TrainingVersion", {PERSISTENT, STRING}},
|
||||
{"UbloxAvailable", {PERSISTENT, BOOL}},
|
||||
{"UpdateAvailable", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
@@ -248,7 +249,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"OsmStateTitle", {PERSISTENT, STRING}},
|
||||
{"OsmWayTest", {PERSISTENT, STRING}},
|
||||
{"RoadName", {CLEAR_ON_ONROAD_TRANSITION, STRING}},
|
||||
{"RoadNameToggle", {PERSISTENT, STRING}},
|
||||
{"RoadNameToggle", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
|
||||
// Speed Limit
|
||||
{"SpeedLimitMode", {PERSISTENT | BACKUP, INT, "1"}},
|
||||
|
||||
+1
-10
@@ -4,7 +4,7 @@
|
||||
|
||||
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
||||
|
||||
# 344 Supported Cars
|
||||
# 335 Supported Cars
|
||||
|
||||
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br> |Video|Setup Video|
|
||||
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
@@ -23,10 +23,7 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi S3 2015-17">Buy Here</a></sub></details>|||
|
||||
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim, without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EUV 2022-23">Buy Here</a></sub></details>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EV 2022-23">Buy Here</a></sub></details>|||
|
||||
|Chevrolet|Bolt EV Non-ACC 2017|Adaptive Cruise Control (ACC)|Stock|24 mph|7 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EV Non-ACC 2017">Buy Here</a></sub></details>|||
|
||||
|Chevrolet|Bolt EV Non-ACC 2018-21|Adaptive Cruise Control (ACC)|Stock|24 mph|7 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EV Non-ACC 2018-21">Buy Here</a></sub></details>|||
|
||||
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Equinox 2019-22">Buy Here</a></sub></details>|||
|
||||
|Chevrolet|Malibu Non-ACC 2016-23|Adaptive Cruise Control (ACC)|Stock|24 mph|7 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Malibu Non-ACC 2016-23">Buy Here</a></sub></details>|||
|
||||
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Silverado 1500 2020-21">Buy Here</a></sub></details>|||
|
||||
|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Trailblazer 2021-22">Buy Here</a></sub></details>|||
|
||||
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica 2017-18">Buy Here</a></sub></details>|||
|
||||
@@ -91,7 +88,6 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Honda|Civic Hatchback Hybrid 2025-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback Hybrid 2025-26">Buy Here</a></sub></details>|||
|
||||
|Honda|Civic Hatchback Hybrid (Europe only) 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback Hybrid (Europe only) 2023">Buy Here</a></sub></details>|||
|
||||
|Honda|Civic Hybrid 2025-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hybrid 2025-26">Buy Here</a></sub></details>|||
|
||||
|Honda|Clarity 2018-21|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector + Honda Clarity Proxy Board<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://shop.retropilot.org/product/honda-clarity-proxy-board-kit">Buy Here</a></sub></details>|||
|
||||
|Honda|CR-V 2015-16|Touring Trim|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V 2015-16">Buy Here</a></sub></details>|||
|
||||
|Honda|CR-V 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|15 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V 2017-22">Buy Here</a></sub></details>|||
|
||||
|Honda|CR-V 2023-26|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda CR-V 2023-26">Buy Here</a></sub></details>|||
|
||||
@@ -122,7 +118,6 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||
|Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra GT 2017-20">Buy Here</a></sub></details>|||
|
||||
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra Hybrid 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||
|Hyundai|Elantra Non-SCC 2022|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra Non-SCC 2022">Buy Here</a></sub></details>|||
|
||||
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai J connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Genesis 2015-16">Buy Here</a></sub></details>|||
|
||||
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai i30 2017-19">Buy Here</a></sub></details>|||
|
||||
|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Ioniq 5 (Southeast Asia and Europe only) 2022-24">Buy Here</a></sub></details>|||
|
||||
@@ -140,9 +135,7 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric 2018-21">Buy Here</a></sub></details>|||
|
||||
|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai O connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric 2022-23">Buy Here</a></sub></details>|||
|
||||
|Hyundai|Kona Electric (with HDA II, Korea only) 2023|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric (with HDA II, Korea only) 2023">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=U2fOCmcQ8hw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||
|Hyundai|Kona Electric Non-SCC 2019|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric Non-SCC 2019">Buy Here</a></sub></details>|||
|
||||
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Hybrid 2020">Buy Here</a></sub></details>|||
|
||||
|Hyundai|Kona Non-SCC 2019|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Non-SCC 2019">Buy Here</a></sub></details>|||
|
||||
|Hyundai|Nexo 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Nexo 2021">Buy Here</a></sub></details>|||
|
||||
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Palisade 2020-22">Buy Here</a></sub></details>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||
|Hyundai|Santa Cruz 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Cruz 2022-24">Buy Here</a></sub></details>|||
|
||||
@@ -166,13 +159,11 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Kia|Carnival 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival 2022-24">Buy Here</a></sub></details>|||
|
||||
|Kia|Carnival (China only) 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival (China only) 2023">Buy Here</a></sub></details>|||
|
||||
|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Ceed 2019-21">Buy Here</a></sub></details>|||
|
||||
|Kia|Ceed Plug-in Hybrid Non-SCC 2022|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Ceed Plug-in Hybrid Non-SCC 2022">Buy Here</a></sub></details>|||
|
||||
|Kia|EV6 (Southeast Asia only) 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia EV6 (Southeast Asia only) 2022-24">Buy Here</a></sub></details>|||
|
||||
|Kia|EV6 (with HDA II) 2022-24|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia EV6 (with HDA II) 2022-24">Buy Here</a></sub></details>|||
|
||||
|Kia|EV6 (without HDA II) 2022-24|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia EV6 (without HDA II) 2022-24">Buy Here</a></sub></details>|||
|
||||
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|6 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Forte 2019-21">Buy Here</a></sub></details>|||
|
||||
|Kia|Forte 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Forte 2022-23">Buy Here</a></sub></details>|||
|
||||
|Kia|Forte Non-SCC 2019|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Forte Non-SCC 2019">Buy Here</a></sub></details>|||
|
||||
|Kia|K5 2021-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 2021-24">Buy Here</a></sub></details>|||
|
||||
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 Hybrid 2020-22">Buy Here</a></sub></details>|||
|
||||
|Kia|K8 Hybrid (with HDA II) 2023|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>|||
|
||||
|
||||
@@ -68,8 +68,10 @@ def generate_metadata(model_path: Path, output_dir: Path, short_name: str):
|
||||
metadata_file = metadata_file.rename(output_path / f"{base}_{short_name.lower()}_metadata.pkl")
|
||||
|
||||
# Build the metadata structure
|
||||
model_type = "offPolicy" if "off_policy" in base else base.split("_")[-1]
|
||||
|
||||
model_metadata = {
|
||||
"type": base.split("_")[-1] if "dmonitoring" not in base else "dmonitoring",
|
||||
"type": model_type,
|
||||
"artifact": {
|
||||
"file_name": tinygrad_file.name,
|
||||
"download_uri": {
|
||||
|
||||
@@ -146,9 +146,11 @@ def arc_bar_pts(cx: float, cy: float,
|
||||
|
||||
|
||||
class TorqueBar(Widget):
|
||||
def __init__(self, demo: bool = False):
|
||||
def __init__(self, demo: bool = False, scale: float = 1.0, always: bool = False):
|
||||
super().__init__()
|
||||
self._demo = demo
|
||||
self._scale = scale
|
||||
self._always = always
|
||||
self._torque_filter = FirstOrderFilter(0, 0.1, 1 / gui_app.target_fps)
|
||||
self._torque_line_alpha_filter = FirstOrderFilter(0.0, 0.1, 1 / gui_app.target_fps)
|
||||
|
||||
@@ -180,8 +182,8 @@ class TorqueBar(Widget):
|
||||
|
||||
def _render(self, rect: rl.Rectangle) -> None:
|
||||
# adjust y pos with torque
|
||||
torque_line_offset = np.interp(abs(self._torque_filter.x), [0.5, 1], [22, 26])
|
||||
torque_line_height = np.interp(abs(self._torque_filter.x), [0.5, 1], [14, 56])
|
||||
torque_line_offset = np.interp(abs(self._torque_filter.x), [0.5, 1], [22 * self._scale, 26 * self._scale])
|
||||
torque_line_height = np.interp(abs(self._torque_filter.x), [0.5, 1], [14 * self._scale, 56 * self._scale])
|
||||
|
||||
# animate alpha and angle span
|
||||
if not self._demo:
|
||||
@@ -195,7 +197,7 @@ class TorqueBar(Widget):
|
||||
torque_line_bg_color = rl.Color(255, 255, 255, int(255 * 0.15 * self._torque_line_alpha_filter.x))
|
||||
|
||||
# draw curved line polygon torque bar
|
||||
torque_line_radius = 1200
|
||||
torque_line_radius = 1200 * self._scale
|
||||
top_angle = -90
|
||||
torque_bg_angle_span = self._torque_line_alpha_filter.x * TORQUE_ANGLE_SPAN
|
||||
torque_start_angle = top_angle - torque_bg_angle_span / 2
|
||||
@@ -207,13 +209,13 @@ class TorqueBar(Widget):
|
||||
cy = rect.y + rect.height + torque_line_radius - torque_line_offset
|
||||
|
||||
# draw bg torque indicator line
|
||||
bg_pts = arc_bar_pts(cx, cy, mid_r, torque_line_height, torque_start_angle, torque_end_angle)
|
||||
bg_pts = arc_bar_pts(cx, cy, mid_r, torque_line_height, torque_start_angle, torque_end_angle, cap_radius=7 * self._scale)
|
||||
draw_polygon(rect, bg_pts, color=torque_line_bg_color)
|
||||
|
||||
# draw torque indicator line
|
||||
a0s = top_angle
|
||||
a1s = a0s + torque_bg_angle_span / 2 * self._torque_filter.x
|
||||
sl_pts = arc_bar_pts(cx, cy, mid_r, torque_line_height, a0s, a1s)
|
||||
sl_pts = arc_bar_pts(cx, cy, mid_r, torque_line_height, a0s, a1s, cap_radius=7 * self._scale)
|
||||
|
||||
# draw beautiful gradient from center to 65% of the bg torque bar width
|
||||
start_grad_pt = cx / rect.width
|
||||
@@ -252,5 +254,5 @@ class TorqueBar(Widget):
|
||||
# draw center torque bar dot
|
||||
if abs(self._torque_filter.x) < 0.5:
|
||||
dot_y = self._rect.y + self._rect.height - torque_line_offset - torque_line_height / 2
|
||||
rl.draw_circle(int(cx), int(dot_y), 10 // 2,
|
||||
rl.draw_circle(int(cx), int(dot_y), (10 // 2 * self._scale),
|
||||
rl.Color(182, 182, 182, int(255 * 0.9 * self._torque_line_alpha_filter.x)))
|
||||
|
||||
@@ -15,7 +15,7 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS, DeviceCamera
|
||||
from openpilot.common.transformations.orientation import rot_from_euler
|
||||
|
||||
if gui_app.sunnypilot_ui():
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.augmented_road_view import BORDER_COLORS_SP
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.augmented_road_view import BORDER_COLORS_SP, AugmentedRoadViewSP
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.driver_state import DriverStateRendererSP as DriverStateRenderer
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.hud_renderer import HudRendererSP as HudRenderer
|
||||
from openpilot.selfdrive.ui.sunnypilot.ui_state import OnroadTimerStatus
|
||||
@@ -38,9 +38,10 @@ ROAD_CAM_MIN_SPEED = 15.0 # m/s (34 mph)
|
||||
INF_POINT = np.array([1000.0, 0.0, 0.0])
|
||||
|
||||
|
||||
class AugmentedRoadView(CameraView):
|
||||
class AugmentedRoadView(CameraView, AugmentedRoadViewSP):
|
||||
def __init__(self, stream_type: VisionStreamType = VisionStreamType.VISION_STREAM_ROAD):
|
||||
super().__init__("camerad", stream_type)
|
||||
CameraView.__init__(self, "camerad", stream_type)
|
||||
AugmentedRoadViewSP.__init__(self)
|
||||
self._set_placeholder_color(BORDER_COLORS[UIStatus.DISENGAGED])
|
||||
|
||||
self.device_camera: DeviceCameraConfig | None = None
|
||||
@@ -92,6 +93,7 @@ class AugmentedRoadView(CameraView):
|
||||
|
||||
# Draw all UI overlays
|
||||
self.model_renderer.render(self._content_rect)
|
||||
AugmentedRoadViewSP.update_fade_out_bottom_overlay(self, self._content_rect)
|
||||
self._hud_renderer.render(self._content_rect)
|
||||
self.alert_renderer.render(self._content_rect)
|
||||
self.driver_state_renderer.render(self._content_rect)
|
||||
|
||||
@@ -5,9 +5,18 @@ This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.sunnypilot.widgets.list_view import toggle_item_sp, multiple_button_item_sp
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
|
||||
CHEVRON_INFO_DESCRIPTION = {
|
||||
"enabled": tr_noop("Display useful metrics below the chevron that tracks the lead car " +
|
||||
"only applicable to cars with sunnypilot longitudinal control."),
|
||||
"disabled": tr_noop("This feature requires sunnypilot longitudinal control to be available.")
|
||||
}
|
||||
|
||||
|
||||
class VisualsLayout(Widget):
|
||||
def __init__(self):
|
||||
@@ -18,13 +27,128 @@ class VisualsLayout(Widget):
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
def _initialize_items(self):
|
||||
items = [
|
||||
self._toggle_defs = {
|
||||
"BlindSpot": (
|
||||
lambda: tr("Show Blind Spot Warnings"),
|
||||
tr("Enabling this will display warnings when a vehicle is detected in your " +
|
||||
"blind spot as long as your car has BSM supported."),
|
||||
None,
|
||||
),
|
||||
"TorqueBar": (
|
||||
lambda: tr("Steering Arc"),
|
||||
tr("Display steering arc on the driving screen when lateral control is enabled."),
|
||||
None,
|
||||
),
|
||||
"RainbowMode": (
|
||||
lambda: tr("Enable Tesla Rainbow Mode"),
|
||||
tr("A beautiful rainbow effect on the path the model wants to take. " +
|
||||
"It does not affect driving in any way."),
|
||||
None,
|
||||
),
|
||||
"StandstillTimer": (
|
||||
lambda: tr("Enable Standstill Timer"),
|
||||
tr("Show a timer on the HUD when the car is at a standstill."),
|
||||
None,
|
||||
),
|
||||
"RoadNameToggle": (
|
||||
lambda: tr("Display Road Name"),
|
||||
tr("Displays the name of the road the car is traveling on." +
|
||||
"<br>The OpenStreetMap database of the location must be downloaded from " +
|
||||
"the OSM panel to fetch the road name."),
|
||||
None,
|
||||
),
|
||||
"GreenLightAlert": (
|
||||
lambda: tr("Green Traffic Light Alert (Beta)"),
|
||||
tr("A chime and on-screen alert will play when the traffic light you are waiting for " +
|
||||
"turns green and you have no vehicle in front of you." +
|
||||
"<br>Note: This chime is only designed as a notification. " +
|
||||
"It is the driver's responsibility to observe their environment and make decisions accordingly."),
|
||||
None,
|
||||
),
|
||||
"LeadDepartAlert": (
|
||||
lambda: tr("Lead Departure Alert (Beta)"),
|
||||
tr("A chime and on-screen alert will play when you are stopped, and the vehicle in front of you start moving." +
|
||||
"<br>Note: This chime is only designed as a notification. " +
|
||||
"It is the driver's responsibility to observe their environment and make decisions accordingly."),
|
||||
None,
|
||||
),
|
||||
"TrueVEgoUI": (
|
||||
lambda: tr("Speedometer: Always Display True Speed"),
|
||||
tr("For applicable vehicles, always display the true vehicle current speed from wheel speed sensors."),
|
||||
None,
|
||||
),
|
||||
"HideVEgoUI": (
|
||||
lambda: tr("Speedometer: Hide from Onroad Screen"),
|
||||
tr("When enabled, the speedometer on the onroad screen is not displayed."),
|
||||
None,
|
||||
),
|
||||
"ShowTurnSignals": (
|
||||
lambda: tr("Display Turn Signals"),
|
||||
tr("When enabled, visual turn indicators are drawn on the HUD."),
|
||||
None,
|
||||
),
|
||||
"RocketFuel": (
|
||||
lambda: tr("Real-time Acceleration Bar"),
|
||||
tr("Show an indicator on the left side of the screen to display real-time vehicle acceleration and deceleration. " +
|
||||
"This displays what the car is currently doing, not what the planner is requesting."),
|
||||
None,
|
||||
),
|
||||
}
|
||||
self._toggles = {}
|
||||
for param, (title, desc, callback) in self._toggle_defs.items():
|
||||
toggle = toggle_item_sp(
|
||||
title=title,
|
||||
description=desc,
|
||||
param=param,
|
||||
initial_state=ui_state.params.get_bool(param),
|
||||
callback=callback,
|
||||
)
|
||||
self._toggles[param] = toggle
|
||||
|
||||
self._chevron_info = multiple_button_item_sp(
|
||||
title=lambda: tr("Display Metrics Below Chevron"),
|
||||
description="",
|
||||
buttons=[lambda: tr("Off"), lambda: tr("Distance"), lambda: tr("Speed"), lambda: tr("Time"), lambda: tr("All")],
|
||||
param="ChevronInfo",
|
||||
inline=False
|
||||
)
|
||||
self._dev_ui_info = multiple_button_item_sp(
|
||||
title=lambda: tr("Developer UI"),
|
||||
description=lambda: tr("Display real-time parameters and metrics from various sources."),
|
||||
buttons=[lambda: tr("Off"), lambda: tr("Bottom"), lambda: tr("Right"), lambda: tr("Right & Bottom")],
|
||||
param="DevUIInfo",
|
||||
button_width=350,
|
||||
inline=False
|
||||
)
|
||||
|
||||
items = list(self._toggles.values()) + [
|
||||
self._chevron_info,
|
||||
self._dev_ui_info,
|
||||
]
|
||||
return items
|
||||
|
||||
def _update_state(self):
|
||||
super()._update_state()
|
||||
|
||||
for param in self._toggle_defs:
|
||||
self._toggles[param].action_item.set_state(self._params.get_bool(param))
|
||||
|
||||
self._dev_ui_info.action_item.set_selected_button(ui_state.params.get("DevUIInfo", return_default=True))
|
||||
|
||||
if ui_state.has_longitudinal_control:
|
||||
self._chevron_info.set_description(tr(CHEVRON_INFO_DESCRIPTION["enabled"]))
|
||||
self._chevron_info.action_item.set_selected_button(ui_state.params.get("ChevronInfo", return_default=True))
|
||||
self._chevron_info.action_item.set_enabled(True)
|
||||
else:
|
||||
self._chevron_info.set_description(tr(CHEVRON_INFO_DESCRIPTION["disabled"]))
|
||||
self._chevron_info.action_item.set_enabled(False)
|
||||
ui_state.params.put("ChevronInfo", 0)
|
||||
|
||||
def _render(self, rect):
|
||||
self._scroller.render(rect)
|
||||
|
||||
def show_event(self):
|
||||
self._scroller.show_event()
|
||||
if not ui_state.has_longitudinal_control:
|
||||
self._chevron_info.set_description(tr(CHEVRON_INFO_DESCRIPTION["disabled"]))
|
||||
self._chevron_info.show_description(True)
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
from collections.abc import Callable
|
||||
|
||||
from cereal import custom
|
||||
from openpilot.selfdrive.ui.mici.widgets.button import BigButton
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets import NavWidget, Widget
|
||||
from openpilot.system.ui.widgets.scroller import Scroller
|
||||
|
||||
|
||||
class ModelsLayoutMici(NavWidget):
|
||||
def __init__(self, back_callback: Callable):
|
||||
super().__init__()
|
||||
self.set_back_callback(back_callback)
|
||||
self.original_back_callback = back_callback
|
||||
self.focused_widget = None
|
||||
|
||||
self.current_model_btn = BigButton(tr("current model"), "", "")
|
||||
self.current_model_btn.set_click_callback(self._show_folders)
|
||||
|
||||
self.cancel_download_btn = BigButton(tr("cancel download"), "", "")
|
||||
self.cancel_download_btn.set_click_callback(lambda: ui_state.params.remove("ModelManager_DownloadIndex"))
|
||||
|
||||
self.main_items: list[Widget] = [self.current_model_btn, self.cancel_download_btn]
|
||||
self._scroller = Scroller(self.main_items, snap_items=False)
|
||||
|
||||
@property
|
||||
def model_manager(self):
|
||||
return ui_state.sm["modelManagerSP"]
|
||||
|
||||
def _get_grouped_bundles(self):
|
||||
bundles = self.model_manager.availableBundles
|
||||
folders = {}
|
||||
for bundle in bundles:
|
||||
folder = next((override.value for override in bundle.overrides if override.key == "folder"), "")
|
||||
folders.setdefault(folder, []).append(bundle)
|
||||
return folders
|
||||
|
||||
def _show_selection_view(self, items: list[Widget], back_callback: Callable):
|
||||
self._scroller._items = items
|
||||
for item in items:
|
||||
item.set_touch_valid_callback(lambda: self._scroller.scroll_panel.is_touch_valid() and self._scroller.enabled)
|
||||
self._scroller.scroll_panel.set_offset(0)
|
||||
self.set_back_callback(back_callback)
|
||||
|
||||
def _show_folders(self):
|
||||
self.focused_widget = self.current_model_btn
|
||||
folders = self._get_grouped_bundles()
|
||||
folder_buttons = []
|
||||
default_btn = BigButton(tr("default model"), "", "")
|
||||
default_btn.set_click_callback(self._select_default)
|
||||
folder_buttons.append(default_btn)
|
||||
|
||||
for folder in sorted(folders.keys(), key=lambda f: max((bundle.index for bundle in folders[f]), default=-1), reverse=True):
|
||||
if folder.lower() in ["release models", "master models"]:
|
||||
btn = BigButton(folder.lower(), "", "")
|
||||
btn.set_click_callback(lambda f=folder: self._select_folder(f))
|
||||
folder_buttons.append(btn)
|
||||
self._show_selection_view(folder_buttons, self._reset_main_view)
|
||||
|
||||
def _select_model(self, bundle):
|
||||
ui_state.params.put("ModelManager_DownloadIndex", bundle.index)
|
||||
self._reset_main_view()
|
||||
|
||||
def _select_default(self):
|
||||
ui_state.params.remove("ModelManager_ActiveBundle")
|
||||
self._reset_main_view()
|
||||
|
||||
def _select_folder(self, folder_name):
|
||||
folders = self._get_grouped_bundles()
|
||||
bundles = sorted(folders.get(folder_name, []), key=lambda b: b.index, reverse=True)
|
||||
|
||||
btns = []
|
||||
for bundle in bundles:
|
||||
txt = bundle.displayName.lower()
|
||||
btn = BigButton(txt, "", "")
|
||||
btn.set_click_callback(lambda b=bundle: self._select_model(b))
|
||||
btns.append(btn)
|
||||
self._show_selection_view(btns, self._show_folders)
|
||||
|
||||
def _reset_main_view(self):
|
||||
self._scroller._items = self.main_items
|
||||
self.set_back_callback(self.original_back_callback)
|
||||
if self.focused_widget and self.focused_widget in self.main_items:
|
||||
x = self._scroller._pad_start
|
||||
for item in self.main_items:
|
||||
if not item.is_visible:
|
||||
continue
|
||||
if item == self.focused_widget:
|
||||
break
|
||||
x += item.rect.width + self._scroller._spacing
|
||||
self._scroller.scroll_panel.set_offset(0)
|
||||
self._scroller.scroll_to(x)
|
||||
self.focused_widget = None
|
||||
else:
|
||||
self._scroller.scroll_panel.set_offset(0)
|
||||
|
||||
def _update_state(self):
|
||||
super()._update_state()
|
||||
|
||||
manager = self.model_manager
|
||||
if manager.selectedBundle and manager.selectedBundle.status == custom.ModelManagerSP.DownloadStatus.downloading:
|
||||
self.current_model_btn.set_value("downloading...")
|
||||
self.cancel_download_btn.set_visible(True)
|
||||
else:
|
||||
self.current_model_btn.set_value(manager.activeBundle.internalName.lower() if manager.activeBundle else tr("default model"))
|
||||
self.cancel_download_btn.set_visible(False)
|
||||
self.current_model_btn.set_enabled(ui_state.is_offroad())
|
||||
self.current_model_btn.set_text(tr("current model"))
|
||||
|
||||
def _render(self, rect):
|
||||
self._scroller.render(rect)
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
self._scroller.show_event()
|
||||
@@ -9,6 +9,7 @@ from enum import IntEnum
|
||||
from openpilot.selfdrive.ui.mici.layouts.settings import settings as OP
|
||||
from openpilot.selfdrive.ui.mici.widgets.button import BigButton
|
||||
from openpilot.selfdrive.ui.sunnypilot.mici.layouts.sunnylink import SunnylinkLayoutMici
|
||||
from openpilot.selfdrive.ui.sunnypilot.mici.layouts.models import ModelsLayoutMici
|
||||
|
||||
ICON_SIZE = 70
|
||||
|
||||
@@ -16,6 +17,7 @@ OP.PanelType = IntEnum(
|
||||
"PanelType",
|
||||
[es.name for es in OP.PanelType] + [
|
||||
"SUNNYLINK",
|
||||
"MODELS",
|
||||
],
|
||||
start=0,
|
||||
)
|
||||
@@ -27,13 +29,19 @@ class SettingsLayoutSP(OP.SettingsLayout):
|
||||
|
||||
sunnylink_btn = BigButton("sunnylink", "", "icons_mici/settings/developer/ssh.png")
|
||||
sunnylink_btn.set_click_callback(lambda: self._set_current_panel(OP.PanelType.SUNNYLINK))
|
||||
|
||||
models_btn = BigButton("models", "", "../../sunnypilot/selfdrive/assets/offroad/icon_models.png")
|
||||
models_btn.set_click_callback(lambda: self._set_current_panel(OP.PanelType.MODELS))
|
||||
|
||||
self._panels.update({
|
||||
OP.PanelType.SUNNYLINK: OP.PanelInfo("sunnylink", SunnylinkLayoutMici(back_callback=lambda: self._set_current_panel(None))),
|
||||
OP.PanelType.MODELS: OP.PanelInfo("models", ModelsLayoutMici(back_callback=lambda: self._set_current_panel(None))),
|
||||
})
|
||||
|
||||
items = self._scroller._items.copy()
|
||||
|
||||
items.insert(1, sunnylink_btn)
|
||||
items.insert(2, models_btn)
|
||||
self._scroller._items.clear()
|
||||
for item in items:
|
||||
self._scroller.add_widget(item)
|
||||
|
||||
@@ -5,9 +5,27 @@ This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
import pyray as rl
|
||||
from openpilot.selfdrive.ui.ui_state import UIStatus
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.selfdrive.ui.ui_state import UIStatus, ui_state
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
|
||||
BORDER_COLORS_SP = {
|
||||
UIStatus.LAT_ONLY: rl.Color(0x00, 0xC8, 0xC8, 0xFF), # Cyan for lateral-only state
|
||||
UIStatus.LONG_ONLY: rl.Color(0x96, 0x1C, 0xA8, 0xFF), # Purple for longitudinal-only state
|
||||
}
|
||||
|
||||
|
||||
class AugmentedRoadViewSP:
|
||||
def __init__(self):
|
||||
self._fade_texture = gui_app.texture("icons_mici/onroad/onroad_fade.png")
|
||||
self._fade_alpha_filter = FirstOrderFilter(0, 0.1, 1 / gui_app.target_fps)
|
||||
|
||||
def update_fade_out_bottom_overlay(self, _content_rect):
|
||||
# Fade out bottom of overlays for looks (only when engaged)
|
||||
fade_alpha = self._fade_alpha_filter.update(ui_state.status != UIStatus.DISENGAGED)
|
||||
if ui_state.torque_bar and fade_alpha > 1e-2:
|
||||
# Scale the fade texture to the content rect
|
||||
rl.draw_texture_pro(self._fade_texture,
|
||||
rl.Rectangle(0, 0, self._fade_texture.width, self._fade_texture.height),
|
||||
_content_rect, rl.Vector2(0, 0), 0.0,
|
||||
rl.Color(255, 255, 255, int(255 * fade_alpha)))
|
||||
|
||||
@@ -31,6 +31,9 @@ class BlindSpotIndicators:
|
||||
return self._blind_spot_left_alpha_filter.x > 0.01 or self._blind_spot_right_alpha_filter.x > 0.01
|
||||
|
||||
def render(self, rect: rl.Rectangle) -> None:
|
||||
if not ui_state.blindspot:
|
||||
return
|
||||
|
||||
BLIND_SPOT_MARGIN_X = 20 # Distance from edge of screen
|
||||
BLIND_SPOT_Y_OFFSET = 100 # Distance from top of screen
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
import pyray as rl
|
||||
|
||||
from cereal import log
|
||||
from openpilot.selfdrive.ui import UI_BORDER_SIZE
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.developer_ui import DeveloperUiRenderer
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
|
||||
|
||||
class CircularAlertsRenderer:
|
||||
def __init__(self):
|
||||
self._green_light_alert_img = gui_app.texture("../../sunnypilot/selfdrive/assets/images/green_light.png", 250, 250)
|
||||
self._lead_depart_alert_img = gui_app.texture("../../sunnypilot/selfdrive/assets/images/lead_depart.png", 250, 250)
|
||||
|
||||
self._e2e_alert_display_timer = 0
|
||||
self._e2e_alert_frame = 0
|
||||
self._green_light_alert = False
|
||||
self._lead_depart_alert = False
|
||||
self._standstill_elapsed_time = 0.0
|
||||
self._is_standstill = False
|
||||
self._alert_text = ""
|
||||
self._alert_img = None
|
||||
self._allow_e2e_alerts = False
|
||||
|
||||
def update(self) -> None:
|
||||
sm = ui_state.sm
|
||||
lp_sp = sm['longitudinalPlanSP']
|
||||
car_state = sm['carState']
|
||||
self._green_light_alert = lp_sp.e2eAlerts.greenLightAlert
|
||||
self._lead_depart_alert = lp_sp.e2eAlerts.leadDepartAlert
|
||||
self._is_standstill = car_state.standstill
|
||||
|
||||
if not ui_state.started:
|
||||
self._standstill_elapsed_time = 0.0
|
||||
|
||||
self._allow_e2e_alerts = sm['selfdriveState'].alertSize == log.SelfdriveState.AlertSize.none and \
|
||||
sm.recv_frame['driverStateV2'] > ui_state.started_frame
|
||||
|
||||
if self._green_light_alert or self._lead_depart_alert:
|
||||
self._e2e_alert_display_timer = 3 * gui_app.target_fps
|
||||
# reset onroad sleep timer for e2e alerts
|
||||
ui_state.reset_onroad_sleep_timer()
|
||||
|
||||
if self._e2e_alert_display_timer > 0:
|
||||
self._e2e_alert_frame += 1
|
||||
self._e2e_alert_display_timer -= 1
|
||||
|
||||
if self._green_light_alert:
|
||||
self._alert_text = "GREEN\nLIGHT"
|
||||
self._alert_img = self._green_light_alert_img
|
||||
elif self._lead_depart_alert:
|
||||
self._alert_text = "LEAD VEHICLE\nDEPARTING"
|
||||
self._alert_img = self._lead_depart_alert_img
|
||||
|
||||
elif ui_state.standstill_timer and self._is_standstill:
|
||||
self._alert_img = None
|
||||
self._standstill_elapsed_time += 1.0 / gui_app.target_fps
|
||||
minute = int(self._standstill_elapsed_time / 60)
|
||||
second = int(self._standstill_elapsed_time - (minute * 60))
|
||||
self._alert_text = f"{minute:01d}:{second:02d}"
|
||||
self._e2e_alert_frame += 1
|
||||
|
||||
else:
|
||||
self._e2e_alert_frame = 0
|
||||
if not self._is_standstill:
|
||||
self._standstill_elapsed_time = 0.0
|
||||
|
||||
def render(self, rect: rl.Rectangle) -> None:
|
||||
if not self._allow_e2e_alerts or (self._e2e_alert_display_timer <= 0 and not (ui_state.standstill_timer and self._is_standstill)):
|
||||
return
|
||||
|
||||
e2e_alert_size = 250
|
||||
dev_ui_width_adjustment = 180 if ui_state.developer_ui in (DeveloperUiRenderer.DEV_UI_RIGHT, DeveloperUiRenderer.DEV_UI_BOTH) else 100
|
||||
|
||||
x = rect.x + rect.width - e2e_alert_size - dev_ui_width_adjustment - (UI_BORDER_SIZE * 3)
|
||||
y = rect.y + rect.height / 2 + 20
|
||||
|
||||
alert_rect = rl.Rectangle(x - e2e_alert_size, y - e2e_alert_size, e2e_alert_size * 2, e2e_alert_size * 2)
|
||||
center = rl.Vector2(alert_rect.x + alert_rect.width / 2, alert_rect.y + alert_rect.height / 2)
|
||||
|
||||
# Pulse logic
|
||||
is_pulsing = (self._e2e_alert_frame % gui_app.target_fps) < (gui_app.target_fps / 2.5)
|
||||
|
||||
# Standstill Timer (STOPPED) should be static white
|
||||
if self._e2e_alert_display_timer == 0 and ui_state.standstill_timer and self._is_standstill:
|
||||
frame_color = rl.Color(255, 255, 255, 75)
|
||||
else:
|
||||
frame_color = rl.Color(255, 255, 255, 75) if is_pulsing else rl.Color(0, 255, 0, 75)
|
||||
|
||||
# Draw Circle
|
||||
rl.draw_circle_v(center, e2e_alert_size, rl.Color(0, 0, 0, 190))
|
||||
# Draw Ring (Border)
|
||||
rl.draw_ring(center, e2e_alert_size - 7.5, e2e_alert_size + 7.5, 0, 360, 0, frame_color)
|
||||
|
||||
# Draw Image
|
||||
if self._alert_img and self._e2e_alert_display_timer > 0:
|
||||
img_x = int(center.x - self._alert_img.width / 2)
|
||||
img_y = int(center.y - self._alert_img.height / 2)
|
||||
rl.draw_texture(self._alert_img, img_x, img_y, rl.WHITE)
|
||||
|
||||
# Draw Text
|
||||
txt_color = rl.Color(255, 255, 255, 255) if is_pulsing else rl.Color(0, 255, 0, 190)
|
||||
font = gui_app.font(FontWeight.BOLD)
|
||||
text_size = 48
|
||||
spacing = 0
|
||||
|
||||
lines = self._alert_text.split('\n')
|
||||
|
||||
# Position text at bottom of alert circle
|
||||
bottom_y = (alert_rect.y + alert_rect.height) - (alert_rect.height / 7)
|
||||
|
||||
# Draw lines upwards from bottom
|
||||
current_y = bottom_y - (len(lines) * text_size * FONT_SCALE)
|
||||
|
||||
if self._e2e_alert_display_timer == 0 and ui_state.standstill_timer and self._is_standstill:
|
||||
# Standstill Timer Text
|
||||
alert_alt_text = "STOPPED"
|
||||
top_text_size = 80
|
||||
measure_top = measure_text_cached(font, alert_alt_text, top_text_size, spacing)
|
||||
top_y = alert_rect.y + alert_rect.height / 3.5
|
||||
rl.draw_text_ex(font, alert_alt_text, rl.Vector2(center.x - measure_top.x / 2, top_y), top_text_size, spacing, rl.Color(255, 175, 3, 240))
|
||||
|
||||
# Timer
|
||||
timer_text_size = 100
|
||||
measure_timer = measure_text_cached(font, self._alert_text, timer_text_size, spacing)
|
||||
timer_y = (alert_rect.y + alert_rect.height) - (alert_rect.height / 5) - measure_timer.y
|
||||
rl.draw_text_ex(font, self._alert_text, rl.Vector2(center.x - measure_timer.x / 2, timer_y), timer_text_size, spacing, rl.WHITE)
|
||||
else:
|
||||
for line in lines:
|
||||
measure = measure_text_cached(font, line, text_size, spacing)
|
||||
line_x = center.x - measure.x / 2
|
||||
rl.draw_text_ex(font, line, rl.Vector2(line_x, current_y), text_size, spacing, txt_color)
|
||||
current_y += text_size * FONT_SCALE
|
||||
@@ -10,7 +10,7 @@ from openpilot.selfdrive.ui.sunnypilot.onroad.developer_ui.elements import (
|
||||
UiElement, RelDistElement, RelSpeedElement, SteeringAngleElement,
|
||||
DesiredLateralAccelElement, ActualLateralAccelElement, DesiredSteeringAngleElement,
|
||||
AEgoElement, LeadSpeedElement, FrictionCoefficientElement, LatAccelFactorElement,
|
||||
SteeringTorqueEpsElement, BearingDegElement, AltitudeElement
|
||||
SteeringTorqueEpsElement, BearingDegElement, AltitudeElement, DesiredSteeringPIDElement
|
||||
)
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
@@ -36,6 +36,7 @@ class DeveloperUiRenderer(Widget):
|
||||
self.desired_lat_accel_elem = DesiredLateralAccelElement()
|
||||
self.actual_lat_accel_elem = ActualLateralAccelElement()
|
||||
self.desired_steer_elem = DesiredSteeringAngleElement()
|
||||
self.desired_pid_steer_elem = DesiredSteeringPIDElement()
|
||||
self.a_ego_elem = AEgoElement()
|
||||
self.lead_speed_elem = LeadSpeedElement()
|
||||
self.friction_elem = FrictionCoefficientElement()
|
||||
@@ -85,8 +86,10 @@ class DeveloperUiRenderer(Widget):
|
||||
]
|
||||
if controls_state.lateralControlState.which() == 'torqueState':
|
||||
elements.append(self.desired_lat_accel_elem.update(sm, ui_state.is_metric))
|
||||
else:
|
||||
elif controls_state.lateralControlState.which() == 'angleState':
|
||||
elements.append(self.desired_steer_elem.update(sm, ui_state.is_metric))
|
||||
elif controls_state.lateralControlState.which() == 'pidState':
|
||||
elements.append(self.desired_pid_steer_elem.update(sm, ui_state.is_metric))
|
||||
|
||||
elements.append(self.actual_lat_accel_elem.update(sm, ui_state.is_metric))
|
||||
|
||||
|
||||
@@ -191,6 +191,31 @@ class DesiredLateralAccelElement(LateralControlElement):
|
||||
return UiElement(value, "DESIRED L.A.", self.unit, color)
|
||||
|
||||
|
||||
class DesiredSteeringPIDElement(LateralControlElement):
|
||||
def __init__(self):
|
||||
self.unit = ""
|
||||
|
||||
def update(self, sm, is_metric: bool) -> UiElement:
|
||||
car_state = sm['carState']
|
||||
controls_state = sm['controlsState']
|
||||
lat_active = sm['carControl'].latActive
|
||||
angle_steers = car_state.steeringAngleDeg
|
||||
steer_angle_desired = controls_state.lateralControlState.pidState.steeringAngleDesiredDeg
|
||||
|
||||
value = f"{steer_angle_desired:.1f}°" if lat_active else "-"
|
||||
|
||||
color = rl.WHITE
|
||||
if lat_active:
|
||||
if abs(angle_steers) > 180:
|
||||
color = rl.RED
|
||||
elif abs(angle_steers) > 90:
|
||||
color = rl.Color(255, 188, 0, 255)
|
||||
else:
|
||||
color = rl.Color(0, 255, 0, 255)
|
||||
|
||||
return UiElement(value, "DESIRED STEER", self.unit, color)
|
||||
|
||||
|
||||
class AEgoElement:
|
||||
def __init__(self):
|
||||
self.unit = "m/s^2"
|
||||
|
||||
@@ -6,13 +6,17 @@ See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.selfdrive.ui.mici.onroad.torque_bar import TorqueBar
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.selfdrive.ui.onroad.hud_renderer import HudRenderer
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.developer_ui import DeveloperUiRenderer
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.road_name import RoadNameRenderer
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.rocket_fuel import RocketFuel
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.speed_limit import SpeedLimitRenderer
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.smart_cruise_control import SmartCruiseControlRenderer
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.turn_signal import TurnSignalController
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.circular_alerts import CircularAlertsRenderer
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.speed_renderer import SpeedRenderer
|
||||
|
||||
|
||||
class HudRendererSP(HudRenderer):
|
||||
@@ -22,20 +26,37 @@ class HudRendererSP(HudRenderer):
|
||||
self.road_name_renderer = RoadNameRenderer()
|
||||
self.rocket_fuel = RocketFuel()
|
||||
self.speed_limit_renderer = SpeedLimitRenderer()
|
||||
self.smart_cruise_control_renderer = SmartCruiseControlRenderer()
|
||||
self.turn_signal_controller = TurnSignalController()
|
||||
self.circular_alerts_renderer = CircularAlertsRenderer()
|
||||
self.speed_renderer = SpeedRenderer()
|
||||
self._torque_bar = TorqueBar(scale=3.0, always=True)
|
||||
|
||||
def _update_state(self) -> None:
|
||||
super()._update_state()
|
||||
self.road_name_renderer.update()
|
||||
self.speed_limit_renderer.update()
|
||||
self.smart_cruise_control_renderer.update()
|
||||
self.turn_signal_controller.update()
|
||||
self.circular_alerts_renderer.update()
|
||||
self.speed_renderer.update()
|
||||
|
||||
def _draw_current_speed(self, rect: rl.Rectangle) -> None:
|
||||
self.speed_renderer.render(rect)
|
||||
|
||||
def _render(self, rect: rl.Rectangle) -> None:
|
||||
super()._render(rect)
|
||||
|
||||
if ui_state.torque_bar and ui_state.sm['controlsState'].lateralControlState.which() != 'angleState':
|
||||
torque_rect = rect
|
||||
if ui_state.developer_ui in (DeveloperUiRenderer.DEV_UI_BOTTOM, DeveloperUiRenderer.DEV_UI_BOTH):
|
||||
torque_rect = rl.Rectangle(rect.x, rect.y, rect.width, rect.height - DeveloperUiRenderer.BOTTOM_BAR_HEIGHT)
|
||||
self._torque_bar.render(torque_rect)
|
||||
|
||||
self.developer_ui.render(rect)
|
||||
self.road_name_renderer.render(rect)
|
||||
self.speed_limit_renderer.render(rect)
|
||||
self.smart_cruise_control_renderer.render(rect)
|
||||
self.turn_signal_controller.render(rect)
|
||||
|
||||
if ui_state.rocket_fuel:
|
||||
self.rocket_fuel.render(rect, ui_state.sm)
|
||||
self.circular_alerts_renderer.render(rect)
|
||||
self.rocket_fuel.render(rect, ui_state.sm)
|
||||
|
||||
@@ -31,7 +31,7 @@ class RoadNameRenderer(Widget):
|
||||
self.road_name = lmd.roadName
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
if not self.road_name:
|
||||
if not self.road_name or not ui_state.road_name_toggle:
|
||||
return
|
||||
|
||||
text = self.road_name
|
||||
|
||||
@@ -6,12 +6,17 @@ See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
|
||||
|
||||
class RocketFuel:
|
||||
def __init__(self):
|
||||
self.vc_accel = 0.0
|
||||
|
||||
def render(self, rect: rl.Rectangle, sm) -> None:
|
||||
if not ui_state.rocket_fuel:
|
||||
return
|
||||
|
||||
vc_accel0 = sm['carState'].aEgo
|
||||
|
||||
# Smooth the acceleration
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.selfdrive.ui.onroad.hud_renderer import COLORS
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
|
||||
|
||||
class SmartCruiseControlRenderer(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.vision_enabled = False
|
||||
self.vision_active = False
|
||||
self.vision_frame = 0
|
||||
self.map_enabled = False
|
||||
self.map_active = False
|
||||
self.map_frame = 0
|
||||
self.long_override = False
|
||||
|
||||
self.font = gui_app.font(FontWeight.BOLD)
|
||||
self.scc_tex = rl.load_render_texture(256, 128)
|
||||
|
||||
def update(self):
|
||||
sm = ui_state.sm
|
||||
if sm.updated["longitudinalPlanSP"]:
|
||||
lp_sp = sm["longitudinalPlanSP"]
|
||||
vision = lp_sp.smartCruiseControl.vision
|
||||
map_ = lp_sp.smartCruiseControl.map
|
||||
|
||||
self.vision_enabled = vision.enabled
|
||||
self.vision_active = vision.active
|
||||
self.map_enabled = map_.enabled
|
||||
self.map_active = map_.active
|
||||
|
||||
if sm.updated["carControl"]:
|
||||
self.long_override = sm["carControl"].cruiseControl.override
|
||||
|
||||
if self.vision_active:
|
||||
self.vision_frame += 1
|
||||
else:
|
||||
self.vision_frame = 0
|
||||
|
||||
if self.map_active:
|
||||
self.map_frame += 1
|
||||
else:
|
||||
self.map_frame = 0
|
||||
|
||||
@staticmethod
|
||||
def _pulse_element(frame):
|
||||
return not (frame % gui_app.target_fps < (gui_app.target_fps / 2.5))
|
||||
|
||||
def _draw_icon(self, rect_center_x, rect_height, x_offset, y_offset, name):
|
||||
text = name
|
||||
font_size = 36
|
||||
padding_v = 5
|
||||
box_width = 160
|
||||
|
||||
sz = measure_text_cached(self.font, text, font_size)
|
||||
box_height = int(sz.y + padding_v * 2)
|
||||
|
||||
texture_width = 256
|
||||
texture_height = 128
|
||||
|
||||
rl.begin_texture_mode(self.scc_tex)
|
||||
rl.clear_background(rl.Color(0, 0, 0, 0))
|
||||
|
||||
if self.long_override:
|
||||
box_color = COLORS.OVERRIDE
|
||||
else:
|
||||
box_color = rl.Color(0, 255, 0, 255)
|
||||
|
||||
# Center box in texture
|
||||
box_x = (texture_width - box_width) // 2
|
||||
box_y = (texture_height - box_height) // 2
|
||||
|
||||
rl.draw_rectangle_rounded(rl.Rectangle(box_x, box_y, box_width, box_height), 0.2, 10, box_color)
|
||||
|
||||
# Draw text with custom blend mode to punch hole
|
||||
rl.rl_set_blend_factors(rl.RL_ZERO, rl.RL_ONE_MINUS_SRC_ALPHA, 0x8006)
|
||||
rl.rl_set_blend_mode(rl.BLEND_CUSTOM)
|
||||
|
||||
text_pos_x = box_x + (box_width - sz.x) / 2
|
||||
text_pos_y = box_y + (box_height - sz.y) / 2
|
||||
|
||||
rl.draw_text_ex(self.font, text, rl.Vector2(text_pos_x, text_pos_y), font_size, 0, rl.WHITE)
|
||||
|
||||
rl.rl_set_blend_mode(rl.BLEND_ALPHA) # Reset
|
||||
rl.end_texture_mode()
|
||||
|
||||
screen_y = rect_height / 4 + y_offset
|
||||
|
||||
dest_x = rect_center_x + x_offset - texture_width / 2
|
||||
dest_y = screen_y - texture_height / 2
|
||||
|
||||
src_rect = rl.Rectangle(0, 0, texture_width, -texture_height)
|
||||
dst_rect = rl.Rectangle(dest_x, dest_y, texture_width, texture_height)
|
||||
|
||||
rl.draw_texture_pro(self.scc_tex.texture, src_rect, dst_rect, rl.Vector2(0, 0), 0, rl.WHITE)
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
x_offset = -260
|
||||
y1_offset = -40
|
||||
y2_offset = -100
|
||||
|
||||
orders = [y1_offset, y2_offset]
|
||||
y_scc_v = 0
|
||||
y_scc_m = 0
|
||||
idx = 0
|
||||
|
||||
if self.vision_enabled:
|
||||
y_scc_v = orders[idx]
|
||||
idx += 1
|
||||
|
||||
if self.map_enabled:
|
||||
y_scc_m = orders[idx]
|
||||
idx += 1
|
||||
|
||||
scc_vision_pulse = self._pulse_element(self.vision_frame)
|
||||
if (self.vision_enabled and not self.vision_active) or (self.vision_active and scc_vision_pulse):
|
||||
self._draw_icon(rect.x + rect.width / 2, rect.height, x_offset, y_scc_v, "SCC-V")
|
||||
|
||||
scc_map_pulse = self._pulse_element(self.map_frame)
|
||||
if (self.map_enabled and not self.map_active) or (self.map_active and scc_map_pulse):
|
||||
self._draw_icon(rect.x + rect.width / 2, rect.height, x_offset, y_scc_m, "SCC-M")
|
||||
@@ -0,0 +1,46 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.common.constants import CV
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.selfdrive.ui.onroad.hud_renderer import FONT_SIZES, COLORS
|
||||
|
||||
|
||||
class SpeedRenderer:
|
||||
def __init__(self):
|
||||
self.speed: float = 0.0
|
||||
self.v_ego_cluster_seen: bool = False
|
||||
|
||||
self._font_bold: rl.Font = gui_app.font(FontWeight.BOLD)
|
||||
self._font_medium: rl.Font = gui_app.font(FontWeight.MEDIUM)
|
||||
|
||||
def update(self) -> None:
|
||||
car_state = ui_state.sm['carState']
|
||||
v_ego_cluster = car_state.vEgoCluster
|
||||
self.v_ego_cluster_seen = self.v_ego_cluster_seen or v_ego_cluster != 0.0
|
||||
v_ego = v_ego_cluster if self.v_ego_cluster_seen and not ui_state.true_v_ego_ui else car_state.vEgo
|
||||
speed_conversion = CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH
|
||||
self.speed = max(0.0, v_ego * speed_conversion)
|
||||
|
||||
def render(self, rect: rl.Rectangle) -> None:
|
||||
if ui_state.hide_v_ego_ui:
|
||||
return
|
||||
|
||||
# Draw current speed and unit
|
||||
speed_text = str(round(self.speed))
|
||||
speed_text_size = measure_text_cached(self._font_bold, speed_text, FONT_SIZES.current_speed)
|
||||
speed_pos = rl.Vector2(rect.x + rect.width / 2 - speed_text_size.x / 2, 180 - speed_text_size.y / 2)
|
||||
rl.draw_text_ex(self._font_bold, speed_text, speed_pos, FONT_SIZES.current_speed, 0, COLORS.WHITE)
|
||||
|
||||
unit_text = tr("km/h") if ui_state.is_metric else tr("mph")
|
||||
unit_text_size = measure_text_cached(self._font_medium, unit_text, FONT_SIZES.speed_unit)
|
||||
unit_pos = rl.Vector2(rect.x + rect.width / 2 - unit_text_size.x / 2, 290 - unit_text_size.y / 2)
|
||||
rl.draw_text_ex(self._font_medium, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.WHITE_TRANSLUCENT)
|
||||
@@ -137,6 +137,9 @@ class TurnSignalController:
|
||||
self._right_signal.deactivate()
|
||||
|
||||
def render(self, rect: rl.Rectangle):
|
||||
if not ui_state.turn_signals:
|
||||
return
|
||||
|
||||
x = rect.x + rect.width / 2
|
||||
|
||||
left_x = x - self._config.left_x - self._config.size
|
||||
|
||||
@@ -120,18 +120,23 @@ class UIStateSP:
|
||||
CP_SP_bytes = self.params.get("CarParamsSPPersistent")
|
||||
if CP_SP_bytes is not None:
|
||||
self.CP_SP = messaging.log_from_bytes(CP_SP_bytes, custom.CarParamsSP)
|
||||
self.sunnylink_enabled = self.params.get_bool("SunnylinkEnabled")
|
||||
self.developer_ui = self.params.get("DevUIInfo")
|
||||
self.rocket_fuel = self.params.get_bool("RocketFuel")
|
||||
self.rainbow_path = self.params.get_bool("RainbowMode")
|
||||
self.chevron_metrics = self.params.get("ChevronInfo")
|
||||
self.active_bundle = self.params.get("ModelManager_ActiveBundle")
|
||||
self.blindspot = self.params.get_bool("BlindSpot")
|
||||
self.chevron_metrics = self.params.get("ChevronInfo")
|
||||
self.custom_interactive_timeout = self.params.get("InteractivityTimeout", return_default=True)
|
||||
self.speed_limit_mode = self.params.get("SpeedLimitMode", return_default=True)
|
||||
|
||||
# Onroad Screen Brightness
|
||||
self.developer_ui = self.params.get("DevUIInfo")
|
||||
self.hide_v_ego_ui = self.params.get_bool("HideVEgoUI")
|
||||
self.onroad_brightness = int(float(self.params.get("OnroadScreenOffBrightness", return_default=True)))
|
||||
self.onroad_brightness_timer_param = self.params.get("OnroadScreenOffTimer", return_default=True)
|
||||
self.rainbow_path = self.params.get_bool("RainbowMode")
|
||||
self.road_name_toggle = self.params.get_bool("RoadNameToggle")
|
||||
self.rocket_fuel = self.params.get_bool("RocketFuel")
|
||||
self.speed_limit_mode = self.params.get("SpeedLimitMode", return_default=True)
|
||||
self.standstill_timer = self.params.get_bool("StandstillTimer")
|
||||
self.sunnylink_enabled = self.params.get_bool("SunnylinkEnabled")
|
||||
self.torque_bar = self.params.get_bool("TorqueBar")
|
||||
self.true_v_ego_ui = self.params.get_bool("TrueVEgoUI")
|
||||
self.turn_signals = self.params.get_bool("ShowTurnSignals")
|
||||
|
||||
|
||||
class DeviceSP:
|
||||
|
||||
@@ -39,7 +39,7 @@ if PC:
|
||||
model_dir = Dir("models").abspath
|
||||
cmd = f'python3 {Dir("#sunnypilot/modeld_v2").abspath}/install_models_pc.py {model_dir}'
|
||||
|
||||
for model_name in ['supercombo', 'driving_vision', 'driving_policy']:
|
||||
for model_name in ['supercombo', 'driving_vision', 'driving_off_policy', 'driving_policy']:
|
||||
if File(f"models/{model_name}.onnx").exists():
|
||||
inputs.append(File(f"models/{model_name}.onnx"))
|
||||
inputs.append(File(f"models/{model_name}_tinygrad.pkl"))
|
||||
@@ -57,7 +57,7 @@ def tg_compile(flags, model_name):
|
||||
)
|
||||
|
||||
# Compile small models
|
||||
for model_name in ['supercombo', 'driving_vision', 'driving_policy']:
|
||||
for model_name in ['supercombo', 'driving_vision', 'driving_off_policy', 'driving_policy']:
|
||||
if File(f"models/{model_name}.onnx").exists():
|
||||
flags = {
|
||||
'larch64': 'DEV=QCOM',
|
||||
|
||||
@@ -44,7 +44,7 @@ def generate_metadata_pkl(model_path, output_path):
|
||||
|
||||
def install_models(model_dir):
|
||||
model_dir = Path(model_dir)
|
||||
models = ["driving_policy", "driving_vision"]
|
||||
models = ["driving_off_policy", "driving_policy", "driving_vision"]
|
||||
found_models = []
|
||||
|
||||
for model in models:
|
||||
|
||||
@@ -116,7 +116,7 @@ class ModelCache:
|
||||
|
||||
class ModelFetcher:
|
||||
"""Handles fetching and caching of model data from remote source"""
|
||||
MODEL_URL = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-docs/refs/heads/gh-pages/docs/driving_models_v10.json"
|
||||
MODEL_URL = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-docs/refs/heads/gh-pages/docs/driving_models_v11.json"
|
||||
|
||||
def __init__(self, params: Params):
|
||||
self.params = params
|
||||
|
||||
@@ -13,6 +13,22 @@ SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||
CUSTOM_MODEL_PATH = Paths.model_root()
|
||||
|
||||
|
||||
class OffPolicyTinygrad(ModularRunner, ABC):
|
||||
"""
|
||||
A TinygradRunner specialized for off-policy models.
|
||||
|
||||
Uses a SplitParser to handle outputs specific to the off-policy part of a split model setup.
|
||||
"""
|
||||
def __init__(self):
|
||||
self._off_policy_parser = SplitParser()
|
||||
self.parser_method_dict[ModelType.offPolicy] = self._parse_off_policy_outputs
|
||||
|
||||
def _parse_off_policy_outputs(self, model_outputs: np.ndarray) -> NumpyDict:
|
||||
"""Parses off-policy model outputs using SplitParser."""
|
||||
result: NumpyDict = self._off_policy_parser.parse_policy_outputs(self._slice_outputs(model_outputs))
|
||||
return result
|
||||
|
||||
|
||||
class PolicyTinygrad(ModularRunner, ABC):
|
||||
"""
|
||||
A TinygradRunner specialized for policy-only models.
|
||||
|
||||
@@ -4,7 +4,7 @@ import numpy as np
|
||||
from openpilot.sunnypilot.modeld_v2.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
|
||||
from openpilot.sunnypilot.models.runners.constants import CLMemDict, FrameDict, NumpyDict, ModelType, ShapeDict, CUSTOM_MODEL_PATH, SliceDict
|
||||
from openpilot.sunnypilot.models.runners.model_runner import ModelRunner
|
||||
from openpilot.sunnypilot.models.runners.tinygrad.model_types import PolicyTinygrad, VisionTinygrad, SupercomboTinygrad
|
||||
from openpilot.sunnypilot.models.runners.tinygrad.model_types import PolicyTinygrad, VisionTinygrad, SupercomboTinygrad, OffPolicyTinygrad
|
||||
from openpilot.system.hardware import TICI
|
||||
from openpilot.sunnypilot.models.split_model_constants import SplitModelConstants
|
||||
from openpilot.sunnypilot.modeld_v2.constants import ModelConstants
|
||||
@@ -12,7 +12,7 @@ from openpilot.sunnypilot.modeld_v2.constants import ModelConstants
|
||||
from tinygrad.tensor import Tensor
|
||||
|
||||
|
||||
class TinygradRunner(ModelRunner, SupercomboTinygrad, PolicyTinygrad, VisionTinygrad):
|
||||
class TinygradRunner(ModelRunner, SupercomboTinygrad, PolicyTinygrad, VisionTinygrad, OffPolicyTinygrad):
|
||||
"""
|
||||
A ModelRunner implementation for executing Tinygrad models.
|
||||
|
||||
@@ -27,6 +27,7 @@ class TinygradRunner(ModelRunner, SupercomboTinygrad, PolicyTinygrad, VisionTiny
|
||||
SupercomboTinygrad.__init__(self)
|
||||
PolicyTinygrad.__init__(self)
|
||||
VisionTinygrad.__init__(self)
|
||||
OffPolicyTinygrad.__init__(self)
|
||||
self._constants = ModelConstants
|
||||
self._model_data = self.models.get(model_type)
|
||||
if not self._model_data or not self._model_data.model:
|
||||
@@ -106,13 +107,20 @@ class TinygradSplitRunner(ModelRunner):
|
||||
self.is_20hz_3d = True
|
||||
self.vision_runner = TinygradRunner(ModelType.vision)
|
||||
self.policy_runner = TinygradRunner(ModelType.policy)
|
||||
self.off_policy_runner = TinygradRunner(ModelType.offPolicy) if self.models.get(ModelType.offPolicy) else None
|
||||
self._constants = SplitModelConstants
|
||||
|
||||
def _run_model(self) -> NumpyDict:
|
||||
"""Runs both vision and policy models and merges their parsed outputs."""
|
||||
policy_output = self.policy_runner.run_model()
|
||||
vision_output = self.vision_runner.run_model()
|
||||
return {**policy_output, **vision_output} # Combine results
|
||||
outputs = {**policy_output, **vision_output}
|
||||
|
||||
if self.off_policy_runner:
|
||||
off_policy_output = self.off_policy_runner.run_model()
|
||||
outputs.update(off_policy_output)
|
||||
|
||||
return outputs
|
||||
|
||||
@property
|
||||
def vision_input_names(self) -> list[str]:
|
||||
@@ -122,12 +130,18 @@ class TinygradSplitRunner(ModelRunner):
|
||||
@property
|
||||
def input_shapes(self) -> ShapeDict:
|
||||
"""Returns the combined input shapes from both vision and policy models."""
|
||||
return {**self.policy_runner.input_shapes, **self.vision_runner.input_shapes}
|
||||
shapes = {**self.policy_runner.input_shapes, **self.vision_runner.input_shapes}
|
||||
if self.off_policy_runner:
|
||||
shapes.update(self.off_policy_runner.input_shapes)
|
||||
return shapes
|
||||
|
||||
@property
|
||||
def output_slices(self) -> SliceDict:
|
||||
"""Returns the combined output slices from both vision and policy models."""
|
||||
return {**self.policy_runner.output_slices, **self.vision_runner.output_slices}
|
||||
slices = {**self.policy_runner.output_slices, **self.vision_runner.output_slices}
|
||||
if self.off_policy_runner:
|
||||
slices.update(self.off_policy_runner.output_slices)
|
||||
return slices
|
||||
|
||||
def prepare_inputs(self, imgs_cl: CLMemDict, numpy_inputs: NumpyDict, frames: FrameDict) -> dict:
|
||||
"""Prepares inputs for both vision and policy models."""
|
||||
@@ -135,5 +149,11 @@ class TinygradSplitRunner(ModelRunner):
|
||||
self.policy_runner.prepare_policy_inputs(numpy_inputs)
|
||||
# Vision inputs depend on imgs_cl and frames
|
||||
self.vision_runner.prepare_vision_inputs(imgs_cl, frames)
|
||||
inputs = {**self.policy_runner.inputs, **self.vision_runner.inputs}
|
||||
|
||||
if self.off_policy_runner:
|
||||
self.off_policy_runner.prepare_policy_inputs(numpy_inputs)
|
||||
inputs.update(self.off_policy_runner.inputs)
|
||||
|
||||
# Return combined inputs (though they are stored within respective runners)
|
||||
return {**self.policy_runner.inputs, **self.vision_runner.inputs}
|
||||
return inputs
|
||||
|
||||
@@ -90,8 +90,8 @@
|
||||
"description": ""
|
||||
},
|
||||
"BlindSpot": {
|
||||
"title": "Blind Spot Detection",
|
||||
"description": ""
|
||||
"title": "[TIZI/TICI only] Blind Spot Detection",
|
||||
"description": "Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported."
|
||||
},
|
||||
"BlinkerMinLateralControlSpeed": {
|
||||
"title": "Blinker Min Lateral Control Speed",
|
||||
@@ -339,8 +339,8 @@
|
||||
"description": ""
|
||||
},
|
||||
"GreenLightAlert": {
|
||||
"title": "Green Light Alert",
|
||||
"description": ""
|
||||
"title": "Green Traffic Light Alert (Beta)",
|
||||
"description": "A chime and on-screen alert (TIZI/TICI only) will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. <br>Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly."
|
||||
},
|
||||
"GsmApn": {
|
||||
"title": "GSM APN",
|
||||
@@ -367,8 +367,8 @@
|
||||
"description": ""
|
||||
},
|
||||
"HideVEgoUI": {
|
||||
"title": "Hide vEgo UI",
|
||||
"description": ""
|
||||
"title": "[TIZI/TICI only] Speedometer: Hide from Onroad Screen",
|
||||
"description": "When enabled, the speedometer on the onroad screen is not displayed."
|
||||
},
|
||||
"HyundaiLongitudinalTuning": {
|
||||
"title": "Hyundai Longitudinal Tuning",
|
||||
@@ -585,8 +585,8 @@
|
||||
"description": ""
|
||||
},
|
||||
"LeadDepartAlert": {
|
||||
"title": "Lead Depart Alert",
|
||||
"description": ""
|
||||
"title": "Lead Departure Alert (Beta)",
|
||||
"description": "A chime and on-screen alert (TIZI/TICI only) will play when you are stopped, and the vehicle in front of you start moving. <br>Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly."
|
||||
},
|
||||
"LiveDelay": {
|
||||
"title": "Live Delay",
|
||||
@@ -1079,12 +1079,12 @@
|
||||
"description": ""
|
||||
},
|
||||
"RoadNameToggle": {
|
||||
"title": "Display Road Name",
|
||||
"description": ""
|
||||
"title": "[TIZI/TICI only] Display Road Name",
|
||||
"description": "Displays the name of the road the car is traveling on. <br>The OpenStreetMap database of the location must be downloaded to fetch the road name."
|
||||
},
|
||||
"RocketFuel": {
|
||||
"title": "Display Rocket Fuel Bar",
|
||||
"description": "Show an indicator on the left side of the screen to display real-time vehicle acceleration and deceleration."
|
||||
"title": "[TIZI/TICI only] Real-time Acceleration Bar",
|
||||
"description": "Show an indicator on the left side of the screen to display real-time vehicle acceleration and deceleration. This displays what the car is currently doing, not what the planner is requesting."
|
||||
},
|
||||
"RouteCount": {
|
||||
"title": "Route Count",
|
||||
@@ -1103,8 +1103,8 @@
|
||||
"description": ""
|
||||
},
|
||||
"ShowTurnSignals": {
|
||||
"title": "Show Turn Signals",
|
||||
"description": ""
|
||||
"title": "[TIZI/TICI only] Display Turn Signals",
|
||||
"description": "When enabled, visual turn indicators are drawn on the HUD."
|
||||
},
|
||||
"SmartCruiseControlMap": {
|
||||
"title": "Smart Cruise Control - Map",
|
||||
@@ -1196,8 +1196,8 @@
|
||||
"description": ""
|
||||
},
|
||||
"StandstillTimer": {
|
||||
"title": "Standstill Timer",
|
||||
"description": ""
|
||||
"title": "[TIZI/TICI only] Standstill Timer",
|
||||
"description": "Show a timer on the HUD when the car is at a standstill."
|
||||
},
|
||||
"SubaruStopAndGo": {
|
||||
"title": "Subaru Stop and Go",
|
||||
@@ -1239,6 +1239,10 @@
|
||||
"title": "Tesla Coop Steering",
|
||||
"description": ""
|
||||
},
|
||||
"TorqueBar": {
|
||||
"title": "[TIZI/TICI only] Steering Arc",
|
||||
"description": "Display steering arc on the driving screen when lateral control is enabled."
|
||||
},
|
||||
"TorqueParamsOverrideEnabled": {
|
||||
"title": "Manual Real-Time Tuning",
|
||||
"description": ""
|
||||
@@ -1267,8 +1271,8 @@
|
||||
"description": ""
|
||||
},
|
||||
"TrueVEgoUI": {
|
||||
"title": "True vEgo UI",
|
||||
"description": ""
|
||||
"title": "[TIZI/TICI only] Speedometer: Always Display True Speed",
|
||||
"description": "For applicable vehicles, always display the true vehicle current speed from wheel speed sensors."
|
||||
},
|
||||
"UbloxAvailable": {
|
||||
"title": "Ublox Available",
|
||||
|
||||
@@ -20,6 +20,7 @@ from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_I
|
||||
from openpilot.common.swaglog import cloudlog, add_file_handler
|
||||
from openpilot.system.version import get_build_metadata
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.hardware import PC
|
||||
|
||||
|
||||
def manager_init() -> None:
|
||||
@@ -39,6 +40,12 @@ def manager_init() -> None:
|
||||
if params.get("DeviceBootMode") == 1: # start in Always Offroad mode
|
||||
params.put_bool("OffroadMode", True)
|
||||
|
||||
# quick boot
|
||||
if params.get_bool("QuickBootToggle") and not PC:
|
||||
prebuilt_path = "/data/openpilot/prebuilt"
|
||||
if not os.path.exists(prebuilt_path):
|
||||
open(prebuilt_path, 'x').close()
|
||||
|
||||
if params.get_bool("RecordFrontLock"):
|
||||
params.put_bool("RecordFront", True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user