mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-07-14 11:12:13 +08:00
Compare commits
494 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 87a67ac195 | |||
| dc1edf294e | |||
| aef9a95c42 | |||
| 0ccded8294 | |||
| c1614d197a | |||
| 15dac3d906 | |||
| 98ecfafcdd | |||
| 0306e59ac1 | |||
| 2d80f2db96 | |||
| 795ed7afb5 | |||
| 206368ec68 | |||
| aa141521fc | |||
| 6e421989ab | |||
| 9c3a73b4cf | |||
| d10349721c | |||
| b48214acd2 | |||
| 8adbd56acd | |||
| 65db08f4d1 | |||
| a1e305333f | |||
| c14b81585e | |||
| f3d8b24bf4 | |||
| 880ed98ffc | |||
| f1025f6ee9 | |||
| 08e85808c5 | |||
| cb03d08397 | |||
| 90cbb09482 | |||
| 9c19ec8409 | |||
| fc253fe1ee | |||
| d72a01d739 | |||
| f93b3f51c9 | |||
| 3d08a5048b | |||
| 9ee66008db | |||
| 6a257fe2de | |||
| dad7bb53a2 | |||
| 47ba86af33 | |||
| d106c192f2 | |||
| 98ffbe1308 | |||
| d09f74612f | |||
| 3af0d6e87f | |||
| 584269fced | |||
| 1dc5741e75 | |||
| b69da9e5ea | |||
| 9689de426b | |||
| 0ccd55a6b5 | |||
| 124eb42758 | |||
| 85404c184b | |||
| ed42cfe699 | |||
| 3099f4f12d | |||
| 8fceb9d957 | |||
| 48dc9dbb69 | |||
| 56ca486fe9 | |||
| 799e819e58 | |||
| aaac1c79d0 | |||
| f46de2d0d5 | |||
| d7fa10a827 | |||
| 22b010f674 | |||
| dcaf84d04c | |||
| 3a82a0797a | |||
| 365e978b42 | |||
| dd074cb6ef | |||
| 2d1f3833e4 | |||
| d4185a5d57 | |||
| 1262fca36b | |||
| ba176a6581 | |||
| 63e5d0a476 | |||
| e28dd1e1aa | |||
| 890b1cf512 | |||
| 1633641055 | |||
| 2dcb67091f | |||
| fb34601d5a | |||
| c1d3ae427b | |||
| 2ab45b552d | |||
| 8c1d59fecd | |||
| 43b4e4e271 | |||
| b6bcc8cca3 | |||
| cde88fd8ed | |||
| e5a7deb6ad | |||
| 10100e34e1 | |||
| 2d31b422c8 | |||
| 864c811ef6 | |||
| 89919c8832 | |||
| 906e9d7a80 | |||
| cfb8f3ae24 | |||
| 0cc5e56192 | |||
| 1a62ae821e | |||
| dc5f5eaf65 | |||
| ee8970dc42 | |||
| 0a44b48e21 | |||
| 36e53c7394 | |||
| 38eb400e41 | |||
| 4b5de0eddb | |||
| 5198b1b079 | |||
| e8a11591a8 | |||
| cbc8f98682 | |||
| ecdcb5d0c6 | |||
| c7494aed0f | |||
| 215ef16803 | |||
| 350b846d3a | |||
| 9ce9920ff7 | |||
| 1c0b087105 | |||
| 137d4b89b4 | |||
| 2cc4885a2e | |||
| 736e1fa7b7 | |||
| 177c7f1cf3 | |||
| 9bf904e8a6 | |||
| 071147baaf | |||
| 5ea5f6f267 | |||
| 18af4d6ad6 | |||
| 525b6e48e9 | |||
| c7b115b68e | |||
| b81d5bca3c | |||
| 682d738ffa | |||
| b6dd2d14db | |||
| 7d4e5bedaf | |||
| 1063114408 | |||
| 958b4df69f | |||
| f60c2b6a83 | |||
| f833819143 | |||
| 62aef9cd34 | |||
| f57617c944 | |||
| c4a0e57046 | |||
| 76c5cb6d87 | |||
| fc4e5007fd | |||
| af24fd6842 | |||
| 72998034e6 | |||
| cefb344183 | |||
| d17e80ad94 | |||
| c2b7087723 | |||
| 81b37712f1 | |||
| 18cd3633e5 | |||
| 9c6a4d4a57 | |||
| 1a4c48249b | |||
| 002a22a097 | |||
| 9f20eb8ce6 | |||
| 707e2aedae | |||
| 2e636458a6 | |||
| 47d0a95fd6 | |||
| 5d142326f5 | |||
| ef9683ee79 | |||
| 55147d8a55 | |||
| de7acc5466 | |||
| 5f5e3668eb | |||
| 8c07958f6f | |||
| ca1ce9bcc9 | |||
| 8a77534d02 | |||
| 73ed45f9d7 | |||
| 2d6df2e125 | |||
| e754b738ad | |||
| 1dadb3fcc9 | |||
| 4e88245745 | |||
| debc9bf7cf | |||
| e03673485b | |||
| 6efe4e1998 | |||
| ff6ed7055d | |||
| e4aada10a4 | |||
| 6c85e2c697 | |||
| 2d0340cefd | |||
| a974deeb59 | |||
| cf5bb4e16e | |||
| 0d4b0ee116 | |||
| 03cb3e9dc0 | |||
| 29f15dc8ed | |||
| 31a5a3b3c0 | |||
| 2a4b348497 | |||
| 3ef3aceb4b | |||
| f0dd0b5c8c | |||
| 3d8763b3ce | |||
| b460d5804c | |||
| eecb8e5c19 | |||
| 1a4ea66987 | |||
| b2427a5f20 | |||
| 94ca077e69 | |||
| e92e59ca78 | |||
| e0cabc1174 | |||
| 5e2f142704 | |||
| c1e15e5544 | |||
| 2beb0ffad1 | |||
| fa373af9b5 | |||
| 7909716c1f | |||
| c1cb971bca | |||
| 538ec25ad9 | |||
| 17152484c2 | |||
| 954b567b9b | |||
| 6061476d8e | |||
| ad903aeaa1 | |||
| c8c1b0f781 | |||
| 534f096bb8 | |||
| 7da36b2470 | |||
| f2db7f7665 | |||
| 40a1af97b9 | |||
| 3a45fff1b9 | |||
| ae9bd39883 | |||
| 43e7d87176 | |||
| 432c6050ed | |||
| 4e3b1f1f6b | |||
| 53ff5413cd | |||
| dc889587ce | |||
| ff4cc96a81 | |||
| 3b1ada64be | |||
| 6a08186434 | |||
| cf2b033c79 | |||
| 9fbef36c6b | |||
| 7b28c2f59a | |||
| 99d954de10 | |||
| b28f33481c | |||
| 589e33f665 | |||
| 39342d7b5e | |||
| 6486ab6cab | |||
| ab234c72a3 | |||
| 485c7b2725 | |||
| 5d47ffdb8a | |||
| 4861d15056 | |||
| 1c89e2b885 | |||
| 1e73025f86 | |||
| c552567ada | |||
| 378212e5ab | |||
| 4f52f3f3c5 | |||
| a0d48b6c63 | |||
| b14270bd71 | |||
| 8f720a54f6 | |||
| 2c41dbc472 | |||
| a8660b5b4f | |||
| 4ccafff123 | |||
| 856f8d3d47 | |||
| 00e20f1524 | |||
| 215acefbb4 | |||
| c33c9ff22a | |||
| 99fdd59042 | |||
| 5af1099fbf | |||
| f983df0c70 | |||
| 936740201c | |||
| 4489517eeb | |||
| b1b7c505a1 | |||
| a2e7f3788f | |||
| d2bb8fe537 | |||
| 7097e69aa3 | |||
| 5289b08bcf | |||
| 657ff0f8ec | |||
| b763f7aac1 | |||
| 450fcd4d55 | |||
| 551b4dea31 | |||
| bd269defb3 | |||
| 399ed08926 | |||
| 90f02040fe | |||
| 8423ecedb1 | |||
| dd1479ed82 | |||
| 641af6d7e7 | |||
| f57de1c5b2 | |||
| cc8f6eadfe | |||
| f82845ff42 | |||
| efcc5ccd15 | |||
| 6aac50ab56 | |||
| cb5d120136 | |||
| 091bce4a3a | |||
| 088f6aa407 | |||
| 211c8adcce | |||
| c85b6a0d1c | |||
| 9b2f7341d8 | |||
| 650946cd2a | |||
| 9801e486d9 | |||
| 3381192297 | |||
| b2e3dd17ea | |||
| 01715f6f9a | |||
| 8720e5d712 | |||
| 8752093801 | |||
| 3c957c6e9d | |||
| 3ef5037c16 | |||
| fe5366e5b2 | |||
| 1ecb0b0f66 | |||
| 51e455db79 | |||
| dc6672fa80 | |||
| 07b8e7783d | |||
| f17b0f200c | |||
| ad9bde8b1f | |||
| 8cf9f9fe23 | |||
| 713985d823 | |||
| 088f9d0b59 | |||
| 53bf5b0d41 | |||
| 8c33592628 | |||
| 3bbb33f6bd | |||
| 7534b2a160 | |||
| 025a930ce8 | |||
| 523c92c6fe | |||
| 72282f2d2e | |||
| 2825c00fcc | |||
| b28425b8c3 | |||
| 1f5e0b6f68 | |||
| 646f6a1006 | |||
| cc683f2040 | |||
| 18e8f648c2 | |||
| 821e4da2c7 | |||
| 13d98fd2d5 | |||
| 92cd656c68 | |||
| 727a750b34 | |||
| 5dabb678ce | |||
| ef988aca28 | |||
| 5bd9549bd1 | |||
| 64f3759fd0 | |||
| 3481702715 | |||
| c9781ee31d | |||
| d71d2bd2d0 | |||
| 702bebf176 | |||
| 25da8e9d44 | |||
| 845f6ec8cf | |||
| e1ad4daf8d | |||
| 783b717af8 | |||
| 65e1fd299e | |||
| b29b1964ba | |||
| 80a8df0643 | |||
| d9fc6c0086 | |||
| cb612a4b90 | |||
| 36d77debd0 | |||
| 530ad2925d | |||
| ec33519dc7 | |||
| 2fd4b53aaf | |||
| a2c4fe1c90 | |||
| 3e56612990 | |||
| 75858673c4 | |||
| 57223958b5 | |||
| 3553a754a4 | |||
| f290fb1e05 | |||
| 0c64818f52 | |||
| c44548ba0f | |||
| 59bddfba8d | |||
| 8a1fcd8991 | |||
| 3546b625e7 | |||
| 87443cd34d | |||
| 5f0e9fce61 | |||
| a2cce7f897 | |||
| f4041dc1f0 | |||
| 8e3757ac87 | |||
| 41fa0cdf82 | |||
| 692f5fdd72 | |||
| de0a1e66d8 | |||
| 129445cd1d | |||
| 13d0aefd7c | |||
| 5f7b05e808 | |||
| 32f65bae55 | |||
| 49d9b8bb00 | |||
| b3eba70b7a | |||
| cec7a5dc98 | |||
| 14993f58e3 | |||
| e8a17b4963 | |||
| fb77212221 | |||
| aa7f6973c0 | |||
| c2af5a82ff | |||
| 348114e5bd | |||
| a6e28ac2ee | |||
| 0e6f78a656 | |||
| 2305fb59a2 | |||
| cc816043c1 | |||
| b6dbb0fd8d | |||
| fdcf8b592e | |||
| 4ff77a4752 | |||
| f04ee80452 | |||
| ddbbcc6f5d | |||
| 0f40afa357 | |||
| cac8d3f405 | |||
| b521a913ab | |||
| d6651ccd82 | |||
| 2976798852 | |||
| 1b90b42647 | |||
| de805e4af7 | |||
| 4d085424f8 | |||
| d07981ea3c | |||
| 22d5cbd0fa | |||
| 4c9ca91b98 | |||
| 0736f325fc | |||
| dcc5afa8fa | |||
| 226465e882 | |||
| 0b62dbe16b | |||
| 2deb4e6f65 | |||
| 9f32f217e6 | |||
| e62781cccb | |||
| e1912fa5be | |||
| a7fe9db773 | |||
| 35296a8692 | |||
| 31801a7312 | |||
| cc7ecd53c7 | |||
| 586e49cab3 | |||
| ebe47a580c | |||
| 7933c10c97 | |||
| 2bc97ee23f | |||
| c88ab5cd12 | |||
| 943aaef76a | |||
| 3fd9e94a34 | |||
| e423f8f605 | |||
| 0eb90ecb3e | |||
| 703f3d0573 | |||
| 2337704602 | |||
| bd9888a439 | |||
| 12b3d0e08d | |||
| 89d350a791 | |||
| 99a83e5522 | |||
| 4d53a26a06 | |||
| a8328cb5ff | |||
| 844c328625 | |||
| 39b97d4e18 | |||
| 45f497e8f6 | |||
| edc5a0412c | |||
| 9670e3a5eb | |||
| 7b2b10bc9e | |||
| bd357adb8b | |||
| 670b6011da | |||
| 150ff72646 | |||
| d567442136 | |||
| 540fff5226 | |||
| 21273c921e | |||
| 75e52427d1 | |||
| 21fd3d0320 | |||
| 1ee798439a | |||
| cc52f980b3 | |||
| ec7e3192bb | |||
| 3fd352a7ef | |||
| 49570c11c6 | |||
| b8ae62a0b1 | |||
| 29a6f0504a | |||
| eadab06f59 | |||
| 9493f2a0eb | |||
| b593b7cc43 | |||
| 5c0c2a17b0 | |||
| 5f33b2fb2d | |||
| 63e0e038fa | |||
| d24a14cb39 | |||
| 3efa52f53b | |||
| 16a4206720 | |||
| e4784d44f6 | |||
| aaf2aac050 | |||
| b5ec0e9744 | |||
| 070a13096b | |||
| 7ccab2bdb9 | |||
| e9434befaa | |||
| 56c77fd5fa | |||
| e6bd88371e | |||
| bc30b01eb7 | |||
| ef93981bfa | |||
| 35e2fc7dd9 | |||
| 2feddf32b2 | |||
| ed185e90f6 | |||
| 19fc66f88a | |||
| 5cbfc7705b | |||
| 8de8c3eb00 | |||
| 04365f12ff | |||
| 9297cd2f3e | |||
| 0711160b1c | |||
| 33f01084d1 | |||
| 1fbec6f601 | |||
| cf5b743de6 | |||
| 2c377e534f | |||
| 1ca9fe35c2 | |||
| 56c49b3b42 | |||
| 5429748767 | |||
| 6aecf59536 | |||
| afc7ff1b7a | |||
| 222e880561 | |||
| 6901e3417b | |||
| cd33562379 | |||
| 073503a6f2 | |||
| 61d5a50534 | |||
| b6e0d4807a | |||
| c7a37c06d8 | |||
| efbd0b9ea0 | |||
| 6ed8f07cb6 | |||
| 5164555c4f | |||
| c5999702ae | |||
| 2a5de8e0f8 | |||
| d05cb31e2e | |||
| c7a9ea2bf4 | |||
| b637ad49d9 | |||
| c6a2c99123 | |||
| 852598fa0a | |||
| 3751d9cf51 | |||
| 30c388aea8 | |||
| b622e3e0a7 | |||
| 086e33dd6e | |||
| 889ce4c4fb | |||
| 96c00271e3 | |||
| a6adedf6e0 | |||
| eb821ceb5c | |||
| 98d61982f9 | |||
| 04a26ada69 | |||
| 3e0dd06374 | |||
| f18828228a | |||
| c812c3192d | |||
| 8d3b919ef6 | |||
| 63df46bf22 | |||
| 826c5e96a1 | |||
| 1870d4905b | |||
| 347b23055d | |||
| cbea5f198f | |||
| be379e188b | |||
| 42d9bd0516 | |||
| 3ca9f351a0 | |||
| a1d6a062a9 |
-19
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
Checks: '
|
|
||||||
bugprone-*,
|
|
||||||
-bugprone-integer-division,
|
|
||||||
-bugprone-narrowing-conversions,
|
|
||||||
performance-*,
|
|
||||||
clang-analyzer-*,
|
|
||||||
misc-*,
|
|
||||||
-misc-unused-parameters,
|
|
||||||
modernize-*,
|
|
||||||
-modernize-avoid-c-arrays,
|
|
||||||
-modernize-deprecated-headers,
|
|
||||||
-modernize-use-auto,
|
|
||||||
-modernize-use-using,
|
|
||||||
-modernize-use-nullptr,
|
|
||||||
-modernize-use-trailing-return-type,
|
|
||||||
'
|
|
||||||
CheckOptions:
|
|
||||||
...
|
|
||||||
@@ -3,3 +3,4 @@ REGIST
|
|||||||
PullRequest
|
PullRequest
|
||||||
cancelled
|
cancelled
|
||||||
FOF
|
FOF
|
||||||
|
NoO
|
||||||
|
|||||||
@@ -13,27 +13,6 @@
|
|||||||
*.o-*
|
*.o-*
|
||||||
*.os
|
*.os
|
||||||
*.os-*
|
*.os-*
|
||||||
*.so
|
|
||||||
*.a
|
|
||||||
|
|
||||||
venv/
|
venv/
|
||||||
.venv/
|
.venv/
|
||||||
|
|
||||||
notebooks
|
|
||||||
phone
|
|
||||||
massivemap
|
|
||||||
neos
|
|
||||||
installer
|
|
||||||
chffr/app2
|
|
||||||
chffr/backend/env
|
|
||||||
selfdrive/nav
|
|
||||||
selfdrive/baseui
|
|
||||||
selfdrive/test/simulator2
|
|
||||||
**/cache_data
|
|
||||||
xx/plus
|
|
||||||
xx/community
|
|
||||||
xx/projects
|
|
||||||
!xx/projects/eon_testing_master
|
|
||||||
!xx/projects/map3d
|
|
||||||
xx/ops
|
|
||||||
xx/junk
|
|
||||||
|
|||||||
+3
-1
@@ -7,10 +7,12 @@
|
|||||||
*.png filter=lfs diff=lfs merge=lfs -text
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
*.gif filter=lfs diff=lfs merge=lfs -text
|
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.otf filter=lfs diff=lfs merge=lfs -text
|
||||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
||||||
selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text
|
selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text
|
||||||
system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text
|
system/hardware/tici/updater_weston filter=lfs diff=lfs merge=lfs -text
|
||||||
|
system/hardware/tici/updater_magic filter=lfs diff=lfs merge=lfs -text
|
||||||
third_party/**/*.a filter=lfs diff=lfs merge=lfs -text
|
third_party/**/*.a filter=lfs diff=lfs merge=lfs -text
|
||||||
third_party/**/*.so filter=lfs diff=lfs merge=lfs -text
|
third_party/**/*.so filter=lfs diff=lfs merge=lfs -text
|
||||||
third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text
|
third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ simulation:
|
|||||||
|
|
||||||
ui:
|
ui:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-all-files: '{selfdrive/ui/**,system/ui/**}'
|
- any-glob-to-all-files: '{selfdrive/assets/**,selfdrive/ui/**,system/ui/**}'
|
||||||
|
|
||||||
tools:
|
tools:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
- uses: ./.github/workflows/setup-with-retry
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Push badges
|
- name: Push badges
|
||||||
run: |
|
run: |
|
||||||
${{ env.RUN }} "scons -j$(nproc) && python3 selfdrive/ui/translations/create_badges.py"
|
${{ env.RUN }} "python3 selfdrive/ui/translations/create_badges.py"
|
||||||
|
|
||||||
rm .gitattributes
|
rm .gitattributes
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/docs.sunnypilot.ai2.git gitlab_docs
|
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/${{ vars.MODELS_GITLAB }} gitlab_docs
|
||||||
cd gitlab_docs
|
cd gitlab_docs
|
||||||
git checkout main
|
git checkout main
|
||||||
git sparse-checkout set --no-cone models/
|
git sparse-checkout set --no-cone models/
|
||||||
@@ -191,7 +191,7 @@ jobs:
|
|||||||
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
||||||
run: |
|
run: |
|
||||||
echo "Cloning GitLab"
|
echo "Cloning GitLab"
|
||||||
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/docs.sunnypilot.ai2.git gitlab_docs
|
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/${{ vars.MODELS_GITLAB }} gitlab_docs
|
||||||
cd gitlab_docs
|
cd gitlab_docs
|
||||||
echo "checkout models/${RECOMPILED_DIR}"
|
echo "checkout models/${RECOMPILED_DIR}"
|
||||||
git sparse-checkout set --no-cone models/${RECOMPILED_DIR}
|
git sparse-checkout set --no-cone models/${RECOMPILED_DIR}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ jobs:
|
|||||||
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
GIT_SSH_COMMAND: 'ssh -o UserKnownHostsFile=~/.ssh/known_hosts'
|
||||||
run: |
|
run: |
|
||||||
echo "Cloning GitLab"
|
echo "Cloning GitLab"
|
||||||
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/docs.sunnypilot.ai2.git gitlab_docs
|
git clone --depth 1 --filter=tree:0 --sparse git@gitlab.com:sunnypilot/public/${{ vars.MODELS_GITLAB }} gitlab_docs
|
||||||
cd gitlab_docs
|
cd gitlab_docs
|
||||||
echo "checkout models/${RECOMPILED_DIR}"
|
echo "checkout models/${RECOMPILED_DIR}"
|
||||||
git sparse-checkout set --no-cone models/${RECOMPILED_DIR}
|
git sparse-checkout set --no-cone models/${RECOMPILED_DIR}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
selfdrive_tests:
|
tests:
|
||||||
uses: sunnypilot/sunnypilot/.github/workflows/selfdrive_tests.yaml@master
|
uses: sunnypilot/sunnypilot/.github/workflows/tests.yaml@master
|
||||||
with:
|
with:
|
||||||
run_number: ${{ inputs.run_number }}
|
run_number: ${{ inputs.run_number }}
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
name: 'Post to Discourse'
|
||||||
|
description: 'Posts a message to a Discourse topic (existing or new)'
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
discourse-url:
|
||||||
|
description: 'Discourse instance URL (e.g., https://discourse.example.com)'
|
||||||
|
required: true
|
||||||
|
api-key:
|
||||||
|
description: 'Discourse API key'
|
||||||
|
required: true
|
||||||
|
api-username:
|
||||||
|
description: 'Discourse API username'
|
||||||
|
required: true
|
||||||
|
topic-id:
|
||||||
|
description: 'Discourse topic ID to post to (use this OR category-id + title)'
|
||||||
|
required: false
|
||||||
|
category-id:
|
||||||
|
description: 'Category ID for new topic (required if topic-id not provided)'
|
||||||
|
required: false
|
||||||
|
title:
|
||||||
|
description: 'Title for new topic (required if topic-id not provided)'
|
||||||
|
required: false
|
||||||
|
message:
|
||||||
|
description: 'Message content (markdown supported)'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
post-number:
|
||||||
|
description: 'The post number in the topic'
|
||||||
|
value: ${{ steps.post.outputs.post_number }}
|
||||||
|
post-url:
|
||||||
|
description: 'Direct URL to the post'
|
||||||
|
value: ${{ steps.post.outputs.post_url }}
|
||||||
|
topic-id:
|
||||||
|
description: 'The topic ID (useful when creating a new topic)'
|
||||||
|
value: ${{ steps.post.outputs.topic_id }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Post to Discourse
|
||||||
|
id: post
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# Validate inputs
|
||||||
|
if [ -z "${{ inputs.topic-id }}" ] && ([ -z "${{ inputs.category-id }}" ] || [ -z "${{ inputs.title }}" ]); then
|
||||||
|
echo "❌ Error: Must provide either topic-id OR both category-id and title"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${{ inputs.topic-id }}" ] && ([ -n "${{ inputs.category-id }}" ] || [ -n "${{ inputs.title }}" ]); then
|
||||||
|
echo "⚠️ Warning: Both topic-id and category-id/title provided. Will post to existing topic."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine if creating new topic or posting to existing
|
||||||
|
if [ -n "${{ inputs.topic-id }}" ]; then
|
||||||
|
echo "📝 Posting to existing topic ID: ${{ inputs.topic-id }}"
|
||||||
|
|
||||||
|
# Create JSON payload for posting to existing topic
|
||||||
|
PAYLOAD=$(jq -n \
|
||||||
|
--arg content '${{ inputs.message }}' \
|
||||||
|
--arg topic_id "${{ inputs.topic-id }}" \
|
||||||
|
'{topic_id: $topic_id, raw: $content}')
|
||||||
|
else
|
||||||
|
echo "✨ Creating new topic: ${{ inputs.title }}"
|
||||||
|
|
||||||
|
# Create JSON payload for new topic
|
||||||
|
PAYLOAD=$(jq -n \
|
||||||
|
--arg content '${{ inputs.message }}' \
|
||||||
|
--arg title "${{ inputs.title }}" \
|
||||||
|
--arg category "${{ inputs.category-id }}" \
|
||||||
|
'{title: $title, category: ($category | tonumber), raw: $content}')
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Post to Discourse
|
||||||
|
RESPONSE=$(curl -s -w "\n%{http_code}" \
|
||||||
|
-X POST "${{ inputs.discourse-url }}/posts.json" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Api-Key: ${{ inputs.api-key }}" \
|
||||||
|
-H "Api-Username: ${{ inputs.api-username }}" \
|
||||||
|
-d "$PAYLOAD")
|
||||||
|
|
||||||
|
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||||
|
BODY=$(echo "$RESPONSE" | sed '$d')
|
||||||
|
|
||||||
|
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
|
||||||
|
echo "✅ Successfully posted to Discourse!"
|
||||||
|
|
||||||
|
POST_NUMBER=$(echo "$BODY" | jq -r '.post_number // "unknown"')
|
||||||
|
TOPIC_ID=$(echo "$BODY" | jq -r '.topic_id // "${{ inputs.topic-id }}"')
|
||||||
|
POST_URL="${{ inputs.discourse-url }}/t/${TOPIC_ID}/${POST_NUMBER}"
|
||||||
|
|
||||||
|
echo "post_number=${POST_NUMBER}" >> $GITHUB_OUTPUT
|
||||||
|
echo "post_url=${POST_URL}" >> $GITHUB_OUTPUT
|
||||||
|
echo "topic_id=${TOPIC_ID}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
echo "Topic ID: ${TOPIC_ID}"
|
||||||
|
echo "Post number: ${POST_NUMBER}"
|
||||||
|
echo "URL: ${POST_URL}"
|
||||||
|
else
|
||||||
|
echo "❌ Failed to post to Discourse"
|
||||||
|
echo "HTTP Code: ${HTTP_CODE}"
|
||||||
|
echo "Response: ${BODY}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: "ui preview"
|
name: "raylib ui preview"
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@@ -8,14 +8,16 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- 'master'
|
- 'master'
|
||||||
paths:
|
paths:
|
||||||
|
- 'selfdrive/assets/**'
|
||||||
- 'selfdrive/ui/**'
|
- 'selfdrive/ui/**'
|
||||||
|
- 'system/ui/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
UI_JOB_NAME: "Create UI Report"
|
UI_JOB_NAME: "Create raylib UI Report"
|
||||||
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
||||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}"
|
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib-ui"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
preview:
|
preview:
|
||||||
@@ -52,7 +54,7 @@ jobs:
|
|||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
||||||
search_artifacts: true
|
search_artifacts: true
|
||||||
name: report-1-${{ env.REPORT_NAME }}
|
name: raylib-report-1-${{ env.REPORT_NAME }}
|
||||||
path: ${{ github.workspace }}/pr_ui
|
path: ${{ github.workspace }}/pr_ui
|
||||||
|
|
||||||
- name: Getting master ui
|
- name: Getting master ui
|
||||||
@@ -60,23 +62,23 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repository: sunnypilot/ci-artifacts
|
repository: sunnypilot/ci-artifacts
|
||||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||||
path: ${{ github.workspace }}/master_ui
|
path: ${{ github.workspace }}/master_ui_raylib
|
||||||
ref: openpilot_master_ui
|
ref: openpilot_master_ui_raylib
|
||||||
|
|
||||||
- name: Saving new master ui
|
- name: Saving new master ui
|
||||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||||
working-directory: ${{ github.workspace }}/master_ui
|
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||||
run: |
|
run: |
|
||||||
git checkout --orphan=new_master_ui
|
git checkout --orphan=new_master_ui_raylib
|
||||||
git rm -rf *
|
git rm -rf *
|
||||||
git branch -D openpilot_master_ui
|
git branch -D openpilot_master_ui_raylib
|
||||||
git branch -m openpilot_master_ui
|
git branch -m openpilot_master_ui_raylib
|
||||||
git config user.name "GitHub Actions Bot"
|
git config user.name "GitHub Actions Bot"
|
||||||
git config user.email "<>"
|
git config user.email "<>"
|
||||||
mv ${{ github.workspace }}/pr_ui/*.png .
|
mv ${{ github.workspace }}/pr_ui/*.png .
|
||||||
git add .
|
git add .
|
||||||
git commit -m "screenshots for commit ${{ env.SHA }}"
|
git commit -m "raylib screenshots for commit ${{ env.SHA }}"
|
||||||
git push origin openpilot_master_ui --force
|
git push origin openpilot_master_ui_raylib --force
|
||||||
|
|
||||||
- name: Finding diff
|
- name: Finding diff
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
@@ -94,7 +96,7 @@ jobs:
|
|||||||
for ((i=0; i<${#A[*]}; i=i+1));
|
for ((i=0; i<${#A[*]}; i=i+1));
|
||||||
do
|
do
|
||||||
# Check if the master file exists
|
# Check if the master file exists
|
||||||
if [ ! -f "${{ github.workspace }}/master_ui/${A[$i]}.png" ]; then
|
if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then
|
||||||
# This is a new file in PR UI that doesn't exist in master
|
# This is a new file in PR UI that doesn't exist in master
|
||||||
DIFF="${DIFF}<details open>"
|
DIFF="${DIFF}<details open>"
|
||||||
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$</summary>"
|
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$</summary>"
|
||||||
@@ -106,12 +108,12 @@ jobs:
|
|||||||
|
|
||||||
DIFF="${DIFF}</table>"
|
DIFF="${DIFF}</table>"
|
||||||
DIFF="${DIFF}</details>"
|
DIFF="${DIFF}</details>"
|
||||||
elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
|
elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
|
||||||
convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png
|
convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png
|
||||||
composite mask.png ${{ github.workspace }}/master_ui/${A[$i]}.png composite_diff.png
|
composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png
|
||||||
convert -delay 100 ${{ github.workspace }}/master_ui/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif
|
convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif
|
||||||
|
|
||||||
mv ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png
|
mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png
|
||||||
|
|
||||||
DIFF="${DIFF}<details open>"
|
DIFF="${DIFF}<details open>"
|
||||||
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$</summary>"
|
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$</summary>"
|
||||||
@@ -149,7 +151,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Saving proposed ui
|
- name: Saving proposed ui
|
||||||
if: github.event_name == 'pull_request_target'
|
if: github.event_name == 'pull_request_target'
|
||||||
working-directory: ${{ github.workspace }}/master_ui
|
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||||
run: |
|
run: |
|
||||||
git config user.name "GitHub Actions Bot"
|
git config user.name "GitHub Actions Bot"
|
||||||
git config user.email "<>"
|
git config user.email "<>"
|
||||||
@@ -157,7 +159,7 @@ jobs:
|
|||||||
git rm -rf *
|
git rm -rf *
|
||||||
mv ${{ github.workspace }}/pr_ui/* .
|
mv ${{ github.workspace }}/pr_ui/* .
|
||||||
git add .
|
git add .
|
||||||
git commit -m "screenshots for PR #${{ github.event.number }}"
|
git commit -m "raylib screenshots for PR #${{ github.event.number }}"
|
||||||
git push origin ${{ env.BRANCH_NAME }} --force
|
git push origin ${{ env.BRANCH_NAME }} --force
|
||||||
|
|
||||||
- name: Comment Screenshots on PR
|
- name: Comment Screenshots on PR
|
||||||
@@ -165,9 +167,9 @@ jobs:
|
|||||||
uses: thollander/actions-comment-pull-request@v2
|
uses: thollander/actions-comment-pull-request@v2
|
||||||
with:
|
with:
|
||||||
message: |
|
message: |
|
||||||
<!-- _(run_id_screenshots **${{ github.run_id }}**)_ -->
|
<!-- _(run_id_screenshots_raylib **${{ github.run_id }}**)_ -->
|
||||||
## UI Preview
|
## raylib UI Preview
|
||||||
${{ steps.find_diff.outputs.DIFF }}
|
${{ steps.find_diff.outputs.DIFF }}
|
||||||
comment_tag: run_id_screenshots
|
comment_tag: run_id_screenshots_raylib
|
||||||
pr_number: ${{ github.event.number }}
|
pr_number: ${{ github.event.number }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -156,6 +156,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: models-${{ env.REF }}${{ inputs.artifact_suffix }}
|
name: models-${{ env.REF }}${{ inputs.artifact_suffix }}
|
||||||
path: ${{ github.workspace }}/selfdrive/modeld/models
|
path: ${{ github.workspace }}/selfdrive/modeld/models
|
||||||
|
- run: |
|
||||||
|
rm -f ${{ github.workspace }}/selfdrive/modeld/models/{dmonitoring_model,big_driving_policy,big_driving_vision}.onnx
|
||||||
|
|
||||||
- name: Build Model
|
- name: Build Model
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ jobs:
|
|||||||
is_stable_branch="$(echo "$CONFIG" | jq -r '.stable_branch // false')";
|
is_stable_branch="$(echo "$CONFIG" | jq -r '.stable_branch // false')";
|
||||||
echo "is_stable_branch=$is_stable_branch" >> $GITHUB_OUTPUT
|
echo "is_stable_branch=$is_stable_branch" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
stable_version=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g');
|
stable_version=$(cat sunnypilot/common/version.h | grep SUNNYPILOT_VERSION | sed -e 's/[^0-9|.]//g');
|
||||||
echo "version=$([ "$is_stable_branch" = "true" ] && echo "$stable_version" || echo "$BUILD")" >> $GITHUB_OUTPUT
|
echo "version=$([ "$is_stable_branch" = "true" ] && echo "$stable_version" || echo "$BUILD")" >> $GITHUB_OUTPUT
|
||||||
echo "extra_version_identifier=${environment}" >> $GITHUB_OUTPUT
|
echo "extra_version_identifier=${environment}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
@@ -302,36 +302,51 @@ jobs:
|
|||||||
git push -f origin ${TAG}
|
git push -f origin ${TAG}
|
||||||
|
|
||||||
notify:
|
notify:
|
||||||
needs: [ build, publish ]
|
needs:
|
||||||
|
- prepare_strategy
|
||||||
|
- build
|
||||||
|
- publish
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
if: ${{ (always() && !cancelled() && !failure()) && needs.publish.result == 'success' && !failure() && (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) }}
|
if: ${{ (always() && !cancelled() && !failure())
|
||||||
|
&& needs.publish.result == 'success'
|
||||||
|
&& (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt'))
|
||||||
|
&& (fromJSON(vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES_V2)[github.head_ref || github.ref_name] != null) }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Alpine Linux environment
|
|
||||||
uses: jirutka/setup-alpine@v1.2.0
|
|
||||||
with:
|
|
||||||
packages: 'jq gettext curl'
|
|
||||||
|
|
||||||
- name: Send Discord Notification
|
- name: Prepare notification message
|
||||||
env:
|
id: message
|
||||||
DISCORD_WEBHOOK: ${{ contains(fromJSON(vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES), env.SOURCE_BRANCH) && secrets.DISCORD_DEV_FEEDBACK_CHANNEL_WEBHOOK || secrets.DISCORD_DEV_PRIVATE_CHANNEL_WEBHOOK }}
|
|
||||||
run: |
|
run: |
|
||||||
TEMPLATE='${{ vars.DISCORD_GENERAL_UPDATE_NOTICE }}'
|
TEMPLATE='${{ vars.DISCOURSE_GENERAL_UPDATE_NOTICE }}'
|
||||||
export EXTRA_VERSION_IDENTIFIER="${{ needs.build.outputs.extra_version_identifier }}"
|
export VERSION="${{ needs.prepare_strategy.outputs.version }}"
|
||||||
export VERSION="${{ needs.build.outputs.version }}"
|
export branch_name="${{ env.SOURCE_BRANCH }}"
|
||||||
export branch_name=${{ env.SOURCE_BRANCH }}
|
export new_branch="${{ needs.prepare_strategy.outputs.new_branch }}"
|
||||||
export new_branch=${{ needs.build.outputs.new_branch }}
|
export commit_sha="${{ github.sha }}"
|
||||||
export extra_version_identifier=${{ needs.build.outputs.extra_version_identifier || github.run_number}}
|
export commit_short_sha="${{ github.sha }}"
|
||||||
echo ${TEMPLATE} | envsubst | jq -c '.' | tee payload.json
|
export commit_short_sha="${commit_short_sha:0:7}"
|
||||||
curl -X POST -H "Content-Type: application/json" -d @payload.json $DISCORD_WEBHOOK
|
export extra_version_identifier="${{ needs.prepare_strategy.outputs.extra_version_identifier || github.run_number }}"
|
||||||
|
export PUBLIC_REPO_URL="${{ env.PUBLIC_REPO_URL }}"
|
||||||
|
|
||||||
echo ""
|
MESSAGE=$(cat << 'EOF' | envsubst
|
||||||
echo "---- ℹ️ To update the list of branches that notify to dev-feedback -----"
|
${{ vars.DISCOURSE_GENERAL_UPDATE_NOTICE }}
|
||||||
echo ""
|
EOF
|
||||||
echo "1. Go to: ${{ github.server_url }}/${{ github.repository }}/settings/variables/actions/DEV_FEEDBACK_NOTIFICATION_BRANCHES"
|
)
|
||||||
echo "2. Current value: ${{ vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES }}"
|
|
||||||
echo "3. Update as needed (JSON array with no spaces)"
|
{
|
||||||
shell: alpine.sh {0}
|
echo 'content<<EOFMARKER'
|
||||||
|
echo "$MESSAGE"
|
||||||
|
echo 'EOFMARKER'
|
||||||
|
} >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Post to Discourse
|
||||||
|
uses: ./.github/workflows/post-to-discourse
|
||||||
|
with:
|
||||||
|
discourse-url: ${{ vars.DISCOURSE_URL }}
|
||||||
|
api-key: ${{ secrets.DISCOURSE_API_KEY }}
|
||||||
|
api-username: "system"
|
||||||
|
topic-id: ${{ fromJSON(vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES_V2)[github.head_ref || github.ref_name].topic_id }}
|
||||||
|
message: ${{ steps.message.outputs.content }}
|
||||||
|
|
||||||
manage-pr-labels:
|
manage-pr-labels:
|
||||||
name: Remove prebuilt label
|
name: Remove prebuilt label
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ name: Build dev
|
|||||||
env:
|
env:
|
||||||
DEFAULT_SOURCE_BRANCH: "master"
|
DEFAULT_SOURCE_BRANCH: "master"
|
||||||
DEFAULT_TARGET_BRANCH: "master-dev"
|
DEFAULT_TARGET_BRANCH: "master-dev"
|
||||||
PR_LABEL: "dev"
|
|
||||||
LFS_URL: 'https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs'
|
LFS_URL: 'https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs'
|
||||||
LFS_PUSH_URL: 'ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git'
|
LFS_PUSH_URL: 'ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git'
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ jobs:
|
|||||||
if: (
|
if: (
|
||||||
(github.event_name == 'workflow_dispatch')
|
(github.event_name == 'workflow_dispatch')
|
||||||
|| (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
|
|| (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
|
||||||
|| (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == 'dev' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev'))))
|
|| (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == vars.PREBUILT_PR_LABEL || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, vars.PREBUILT_PR_LABEL))))
|
||||||
)
|
)
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -55,7 +54,7 @@ jobs:
|
|||||||
uses: ./.github/workflows/wait-for-action # Path to where you place the action
|
uses: ./.github/workflows/wait-for-action # Path to where you place the action
|
||||||
if: (
|
if: (
|
||||||
(github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
|
(github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
|
||||||
|| (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == 'dev' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev'))))
|
|| (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == vars.PREBUILT_PR_LABEL || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, vars.PREBUILT_PR_LABEL))))
|
||||||
)
|
)
|
||||||
with:
|
with:
|
||||||
workflow: selfdrive_tests.yaml # The workflow file to monitor
|
workflow: selfdrive_tests.yaml # The workflow file to monitor
|
||||||
@@ -119,7 +118,7 @@ jobs:
|
|||||||
# Use GitHub API to get PRs with specific label, ordered by creation date
|
# Use GitHub API to get PRs with specific label, ordered by creation date
|
||||||
PR_LIST=$(gh api graphql -f query='
|
PR_LIST=$(gh api graphql -f query='
|
||||||
query($search_query:String!) {
|
query($search_query:String!) {
|
||||||
search(query: $search_query, type:ISSUE, first:100) {
|
search(query: $search_query, type:ISSUE, first:40) {
|
||||||
nodes {
|
nodes {
|
||||||
... on PullRequest {
|
... on PullRequest {
|
||||||
number
|
number
|
||||||
@@ -149,7 +148,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}' -F search_query="repo:${{ github.repository }} is:pr is:open label:${PR_LABEL},${PR_LABEL}-c3 draft:false sort:created-asc")
|
}' -F search_query="repo:${{ github.repository }} is:pr is:open label:${{ vars.PREBUILT_PR_LABEL }},${{ vars.PREBUILT_PR_LABEL }}-c3 draft:false sort:created-asc")
|
||||||
|
|
||||||
PR_LIST=${PR_LIST//\'/}
|
PR_LIST=${PR_LIST//\'/}
|
||||||
echo "PR_LIST=${PR_LIST}" >> $GITHUB_OUTPUT
|
echo "PR_LIST=${PR_LIST}" >> $GITHUB_OUTPUT
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
name: Debug Discourse Posting
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-discourse-post:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Post test message to Discourse
|
||||||
|
uses: ./.github/workflows/post-to-discourse
|
||||||
|
with:
|
||||||
|
discourse-url: ${{ vars.DISCOURSE_URL }}
|
||||||
|
api-key: ${{ secrets.DISCOURSE_API_KEY }}
|
||||||
|
api-username: ${{ secrets.DISCOURSE_API_USERNAME }}
|
||||||
|
topic-id: ${{ vars.DISCOURSE_UPDATES_TOPIC_ID }}
|
||||||
|
message: |
|
||||||
|
## 🧪 Test Post from GitHub Actions
|
||||||
|
|
||||||
|
**This is a test post to verify Discourse integration**
|
||||||
|
|
||||||
|
- **Workflow**: ${{ github.workflow }}
|
||||||
|
- **Run Number**: #${{ github.run_number }}
|
||||||
|
- **Branch**: `${{ github.ref_name }}`
|
||||||
|
- **Commit**: ${{ github.sha }}
|
||||||
|
- **Actor**: @${{ github.actor }}
|
||||||
|
- **Timestamp**: ${{ github.event.head_commit.timestamp }}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fake Build Info (for testing)
|
||||||
|
- **Version**: 0.9.8-test
|
||||||
|
- **Build**: #42
|
||||||
|
- **Branch**: release-test
|
||||||
|
|
||||||
|
[View workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||||
|
|
||||||
|
*This is an automated test message. Drive safe! 🚗💨*
|
||||||
|
|
||||||
|
|
||||||
|
- name: Create topic on Discourse
|
||||||
|
uses: ./.github/workflows/post-to-discourse
|
||||||
|
with:
|
||||||
|
discourse-url: ${{ vars.DISCOURSE_URL }}
|
||||||
|
api-key: ${{ secrets.DISCOURSE_API_KEY }}
|
||||||
|
api-username: ${{ secrets.DISCOURSE_API_USERNAME }}
|
||||||
|
#topic-id: ${{ vars.DISCOURSE_UPDATES_TOPIC_ID }}
|
||||||
|
category-id: 4
|
||||||
|
title: "This is a test of a new topic instead of a reply"
|
||||||
|
message: |
|
||||||
|
## 🧪 Test Post from GitHub Actions
|
||||||
|
|
||||||
|
**This is a test post to verify Discourse integration**
|
||||||
|
|
||||||
|
- **Workflow**: ${{ github.workflow }}
|
||||||
|
- **Run Number**: #${{ github.run_number }}
|
||||||
|
- **Branch**: `${{ github.ref_name }}`
|
||||||
|
- **Commit**: ${{ github.sha }}
|
||||||
|
- **Actor**: @${{ github.actor }}
|
||||||
|
- **Timestamp**: ${{ github.event.head_commit.timestamp }}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fake Build Info (for testing)
|
||||||
|
- **Version**: 0.9.8-test
|
||||||
|
- **Build**: #42
|
||||||
|
- **Branch**: release-test
|
||||||
|
|
||||||
|
[View workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||||
|
|
||||||
|
*This is an automated test message. Drive safe! 🚗💨*
|
||||||
|
- name: Display results
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
echo "::notice::Discourse post test completed"
|
||||||
|
echo "Check your Discourse topic to verify the post appeared correctly"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: selfdrive
|
name: tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -14,18 +14,19 @@ on:
|
|||||||
type: string
|
type: string
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: selfdrive-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
group: tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PYTHONWARNINGS: error
|
PYTHONWARNINGS: error
|
||||||
BASE_IMAGE: sunnypilot-base
|
BASE_IMAGE: sunnypilot-base
|
||||||
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
|
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
|
||||||
|
MAPBOX_TOKEN_CI: ${{ secrets.MAPBOX_TOKEN_CI }}
|
||||||
|
|
||||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||||
BUILD: release/ci/docker_build_sp.sh base
|
BUILD: release/ci/docker_build_sp.sh base
|
||||||
|
|
||||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -e MAPBOX_TOKEN_CI=$MAPBOX_TOKEN_CI -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||||
|
|
||||||
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical
|
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical
|
||||||
|
|
||||||
@@ -195,8 +196,6 @@ jobs:
|
|||||||
# Pre-compile Python bytecode so each pytest worker doesn't need to
|
# Pre-compile Python bytecode so each pytest worker doesn't need to
|
||||||
$PYTEST --collect-only -m 'not slow' -qq && \
|
$PYTEST --collect-only -m 'not slow' -qq && \
|
||||||
MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \
|
MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \
|
||||||
./selfdrive/ui/tests/create_test_translations.sh && \
|
|
||||||
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
|
|
||||||
chmod -R 777 /tmp/comma_download_cache"
|
chmod -R 777 /tmp/comma_download_cache"
|
||||||
|
|
||||||
process_replay:
|
process_replay:
|
||||||
@@ -257,7 +256,7 @@ jobs:
|
|||||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
if: (github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
if: false # FIXME: Started to timeout recently
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -274,38 +273,28 @@ jobs:
|
|||||||
source selfdrive/test/setup_vsound.sh && \
|
source selfdrive/test/setup_vsound.sh && \
|
||||||
CI=1 pytest -s tools/sim/tests/test_metadrive_bridge.py"
|
CI=1 pytest -s tools/sim/tests/test_metadrive_bridge.py"
|
||||||
|
|
||||||
create_ui_report:
|
create_raylib_ui_report:
|
||||||
# This job name needs to be the same as UI_JOB_NAME in ui_preview.yaml
|
name: Create raylib UI Report
|
||||||
name: Create UI Report
|
|
||||||
runs-on: ${{
|
runs-on: ${{
|
||||||
(github.repository == 'commaai/openpilot') &&
|
(github.repository == 'commaai/openpilot') &&
|
||||||
((github.event_name != 'pull_request') ||
|
((github.event_name != 'pull_request') ||
|
||||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
if: false # FIXME: FrameReader is broken on CI runners
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: ./.github/workflows/setup-with-retry
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: caching frames
|
|
||||||
id: frames-cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: .ci_cache/comma_download_cache
|
|
||||||
key: ui_screenshots_test_${{ hashFiles('selfdrive/ui/tests/test_ui/run.py') }}
|
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||||
- name: Create Test Report
|
- name: Create raylib UI Report
|
||||||
timeout-minutes: ${{ ((steps.frames-cache.outputs.cache-hit == 'true') && 2 || 4) }}
|
|
||||||
run: >
|
run: >
|
||||||
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
|
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
|
||||||
source selfdrive/test/setup_xvfb.sh &&
|
source selfdrive/test/setup_xvfb.sh &&
|
||||||
CACHE_ROOT=/tmp/comma_download_cache python3 selfdrive/ui/tests/test_ui/run.py &&
|
python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py"
|
||||||
chmod -R 777 /tmp/comma_download_cache"
|
- name: Upload Raylib UI Report
|
||||||
- name: Upload Test Report
|
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
name: raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
path: selfdrive/ui/tests/test_ui/report_1/screenshots
|
path: selfdrive/ui/tests/test_ui/raylib_report/screenshots
|
||||||
+3
-9
@@ -10,7 +10,6 @@ venv/
|
|||||||
.overlay_init
|
.overlay_init
|
||||||
.overlay_consistent
|
.overlay_consistent
|
||||||
.sconsign.dblite
|
.sconsign.dblite
|
||||||
model2.png
|
|
||||||
a.out
|
a.out
|
||||||
.hypothesis
|
.hypothesis
|
||||||
.cache/
|
.cache/
|
||||||
@@ -37,29 +36,23 @@ a.out
|
|||||||
*.class
|
*.class
|
||||||
*.pyxbldc
|
*.pyxbldc
|
||||||
*.vcd
|
*.vcd
|
||||||
*.qm
|
*.mo
|
||||||
*_pyx.cpp
|
*_pyx.cpp
|
||||||
|
*.stats
|
||||||
config.json
|
config.json
|
||||||
clcache
|
clcache
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
compare_runtime*.html
|
compare_runtime*.html
|
||||||
|
|
||||||
persist
|
|
||||||
selfdrive/pandad/pandad
|
selfdrive/pandad/pandad
|
||||||
cereal/services.h
|
cereal/services.h
|
||||||
cereal/gen
|
cereal/gen
|
||||||
cereal/messaging/bridge
|
cereal/messaging/bridge
|
||||||
selfdrive/mapd/default_speeds_by_region.json
|
|
||||||
selfdrive/ui/translations/tmp
|
selfdrive/ui/translations/tmp
|
||||||
selfdrive/test/longitudinal_maneuvers/out
|
|
||||||
selfdrive/car/tests/cars_dump
|
selfdrive/car/tests/cars_dump
|
||||||
system/camerad/camerad
|
system/camerad/camerad
|
||||||
system/camerad/test/ae_gray_test
|
system/camerad/test/ae_gray_test
|
||||||
|
|
||||||
notebooks
|
|
||||||
hyperthneed
|
|
||||||
provisioning
|
|
||||||
|
|
||||||
.coverage*
|
.coverage*
|
||||||
coverage.xml
|
coverage.xml
|
||||||
htmlcov
|
htmlcov
|
||||||
@@ -76,6 +69,7 @@ sunnypilot/modeld*/thneed/compile
|
|||||||
sunnypilot/modeld*/models/*.thneed
|
sunnypilot/modeld*/models/*.thneed
|
||||||
sunnypilot/modeld*/models/*.pkl
|
sunnypilot/modeld*/models/*.pkl
|
||||||
|
|
||||||
|
# openpilot log files
|
||||||
*.bz2
|
*.bz2
|
||||||
*.zst
|
*.zst
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
url = https://github.com/commaai/teleoprtc
|
url = https://github.com/commaai/teleoprtc
|
||||||
[submodule "tinygrad"]
|
[submodule "tinygrad"]
|
||||||
path = tinygrad_repo
|
path = tinygrad_repo
|
||||||
url = https://github.com/tinygrad/tinygrad.git
|
url = https://github.com/commaai/tinygrad.git
|
||||||
[submodule "sunnypilot/neural_network_data"]
|
[submodule "sunnypilot/neural_network_data"]
|
||||||
path = sunnypilot/neural_network_data
|
path = sunnypilot/neural_network_data
|
||||||
url = https://github.com/sunnypilot/neural-network-data.git
|
url = https://github.com/sunnypilot/neural-network-data.git
|
||||||
|
|||||||
+1104
File diff suppressed because it is too large
Load Diff
@@ -9,4 +9,6 @@ WORKDIR ${OPENPILOT_PATH}
|
|||||||
|
|
||||||
COPY . ${OPENPILOT_PATH}/
|
COPY . ${OPENPILOT_PATH}/
|
||||||
|
|
||||||
RUN scons --cache-readonly -j$(nproc)
|
ENV UV_BIN="/home/batman/.local/bin/"
|
||||||
|
ENV PATH="$UV_BIN:$PATH"
|
||||||
|
RUN UV_PROJECT_ENVIRONMENT=$VIRTUAL_ENV uv run scons --cache-readonly -j$(nproc)
|
||||||
|
|||||||
Vendored
+3
-3
@@ -167,7 +167,7 @@ node {
|
|||||||
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
|
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
|
||||||
|
|
||||||
def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging',
|
def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging',
|
||||||
'release-tici', 'testing-closet*', 'hotfix-*']
|
'release-tici', 'release-tizi', 'release-tizi-staging', 'testing-closet*', 'hotfix-*']
|
||||||
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
|
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
|
||||||
|
|
||||||
if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) {
|
if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) {
|
||||||
@@ -178,8 +178,8 @@ node {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (env.BRANCH_NAME == 'devel-staging') {
|
if (env.BRANCH_NAME == 'devel-staging') {
|
||||||
deviceStage("build release3-staging", "tizi-needs-can", [], [
|
deviceStage("build release-tizi-staging", "tizi-needs-can", [], [
|
||||||
step("build release3-staging", "RELEASE_BRANCH=release3-staging $SOURCE_DIR/release/build_release.sh"),
|
step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging $SOURCE_DIR/release/build_release.sh"),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
## 🌞 What is sunnypilot?
|
## 🌞 What is sunnypilot?
|
||||||
[sunnypilot](https://github.com/sunnyhaibin/sunnypilot) is a fork of comma.ai's openpilot, an open source driver assistance system. sunnypilot offers the user a unique driving experience for over 300+ supported car makes and models with modified behaviors of driving assist engagements. sunnypilot complies with comma.ai's safety rules as accurately as possible.
|
[sunnypilot](https://github.com/sunnyhaibin/sunnypilot) is a fork of comma.ai's openpilot, an open source driver assistance system. sunnypilot offers the user a unique driving experience for over 300+ supported car makes and models with modified behaviors of driving assist engagements. sunnypilot complies with comma.ai's safety rules as accurately as possible.
|
||||||
|
|
||||||
## 💭 Join our Discord
|
## 💭 Join our Community Forum
|
||||||
Join the official sunnypilot Discord server to stay up to date with all the latest features and be a part of shaping the future of sunnypilot!
|
Join the official sunnypilot community forum to stay up to date with all the latest features and be a part of shaping the future of sunnypilot!
|
||||||
* https://discord.gg/sunnypilot
|
* https://community.sunnypilot.ai/
|
||||||
|
|
||||||
 
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
https://docs.sunnypilot.ai/ is your one stop shop for everything from features to installation to FAQ about the sunnypilot
|
https://docs.sunnypilot.ai/ is your one stop shop for everything from features to installation to FAQ about the sunnypilot
|
||||||
@@ -16,13 +14,13 @@ https://docs.sunnypilot.ai/ is your one stop shop for everything from features t
|
|||||||
* A supported device to run this software
|
* A supported device to run this software
|
||||||
* a [comma three](https://comma.ai/shop/products/three) or a [C3X](https://comma.ai/shop/comma-3x)
|
* a [comma three](https://comma.ai/shop/products/three) or a [C3X](https://comma.ai/shop/comma-3x)
|
||||||
* This software
|
* This software
|
||||||
* One of [the 300+ supported cars](https://github.com/commaai/openpilot/blob/master/docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, Ford and more. If your car is not supported but has adaptive cruise control and lane-keeping assist, it's likely able to run sunnypilot.
|
* One of [the 325+ supported cars](https://github.com/sunnypilot/sunnypilot/blob/master/docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, Ford, and more. If your car is not supported but has adaptive cruise control and lane-keeping assist, it's likely able to run sunnypilot.
|
||||||
* A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car
|
* A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car
|
||||||
|
|
||||||
Detailed instructions for [how to mount the device in a car](https://comma.ai/setup).
|
Detailed instructions for [how to mount the device in a car](https://comma.ai/setup).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Please refer to [Recommended Branches](#-recommended-branches) to find your preferred/supported branch. This guide will assume you want to install the latest `staging-c3-new` branch.
|
Please refer to [Recommended Branches](#recommended-branches) to find your preferred/supported branch. This guide will assume you want to install the latest `staging` branch.
|
||||||
|
|
||||||
### If you want to use our newest branches (our rewrite)
|
### If you want to use our newest branches (our rewrite)
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
@@ -31,28 +29,28 @@ Please refer to [Recommended Branches](#-recommended-branches) to find your pref
|
|||||||
* sunnypilot not installed or you installed a version before 0.8.17?
|
* sunnypilot not installed or you installed a version before 0.8.17?
|
||||||
1. [Factory reset/uninstall](https://github.com/commaai/openpilot/wiki/FAQ#how-can-i-reset-the-device) the previous software if you have another software/fork installed.
|
1. [Factory reset/uninstall](https://github.com/commaai/openpilot/wiki/FAQ#how-can-i-reset-the-device) the previous software if you have another software/fork installed.
|
||||||
2. After factory reset/uninstall and upon reboot, select `Custom Software` when given the option.
|
2. After factory reset/uninstall and upon reboot, select `Custom Software` when given the option.
|
||||||
3. Input the installation URL per [Recommended Branches](#-recommended-branches). Example: ```https://staging-c3-new.sunnypilot.ai```.
|
3. Input the installation URL per [Recommended Branches](#recommended-branches). Example: ```https://staging.sunnypilot.ai```.
|
||||||
4. Complete the rest of the installation following the onscreen instructions.
|
4. Complete the rest of the installation following the onscreen instructions.
|
||||||
|
|
||||||
* sunnypilot already installed and you installed a version after 0.8.17?
|
* sunnypilot already installed and you installed a version after 0.8.17?
|
||||||
1. On the comma three, go to `Settings` ▶️ `Software`.
|
1. On the comma three/3X, go to `Settings` ▶️ `Software`.
|
||||||
2. At the `Download` option, press `CHECK`. This will fetch the list of latest branches from sunnypilot.
|
2. At the `Download` option, press `CHECK`. This will fetch the list of latest branches from sunnypilot.
|
||||||
3. At the `Target Branch` option, press `SELECT` to open the Target Branch selector.
|
3. At the `Target Branch` option, press `SELECT` to open the Target Branch selector.
|
||||||
4. Scroll to select the desired branch per Recommended Branches (see below). Example: `staging-c3-new`
|
4. Scroll to select the desired branch per Recommended Branches (see below). Example: `staging`
|
||||||
|
|
||||||
|
### Recommended Branches
|
||||||
| Branch | Installation URL |
|
| Branch | Installation URL |
|
||||||
|:----------------:|:---------------------------------------------:|
|
|:---------------:|:---------------------------------------------:|
|
||||||
| `staging-c3-new` | `https://staging-c3-new.sunnypilot.ai` |
|
| `release` | `https://release.sunnypilot.ai` |
|
||||||
| `dev-c3-new` | `https://dev-c3-new.sunnypilot.ai` |
|
| `staging` | `https://staging.sunnypilot.ai` |
|
||||||
| `custom-branch` | `https://install.sunnypilot.ai/{branch_name}` |
|
| `dev` | `https://dev.sunnypilot.ai` |
|
||||||
| `release-c3-new` | **Not yet available**. |
|
| `custom-branch` | `https://install.sunnypilot.ai/{branch_name}` |
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> You can use sunnypilot/targetbranch as an install URL. Example: 'sunnypilot/staging-c3-new'.
|
> You can use sunnypilot/targetbranch as an install URL. Example: 'sunnypilot/staging'.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Do you require further assistance with software installation? Join the [sunnypilot Discord server](https://discord.sunnypilot.com) and message us in the `#installation-help` channel.
|
> Do you require further assistance with software installation? Join the [sunnypilot community forum](https://community.sunnypilot.ai/new-topic?category=general/qa) and create a topic in the General/Q&A Category channel.
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
+8
-1
@@ -1,13 +1,20 @@
|
|||||||
|
Version 0.10.2 (2025-11-23)
|
||||||
|
========================
|
||||||
|
|
||||||
Version 0.10.1 (2025-09-08)
|
Version 0.10.1 (2025-09-08)
|
||||||
========================
|
========================
|
||||||
* New driving model
|
* New driving model #36276
|
||||||
* World Model: removed global localization inputs
|
* World Model: removed global localization inputs
|
||||||
* World Model: 2x the number of parameters
|
* World Model: 2x the number of parameters
|
||||||
* World Model: trained on 4x the number of segments
|
* World Model: trained on 4x the number of segments
|
||||||
|
* VAE Compression Model: new architecture and training objective
|
||||||
* Driving Vision Model: trained on 4x the number of segments
|
* Driving Vision Model: trained on 4x the number of segments
|
||||||
|
* New Driver Monitoring model #36198
|
||||||
|
* Acura TLX 2021 support thanks to MVL!
|
||||||
* Honda City 2023 support thanks to vanillagorillaa and drFritz!
|
* Honda City 2023 support thanks to vanillagorillaa and drFritz!
|
||||||
* Honda N-Box 2018 support thanks to miettal!
|
* Honda N-Box 2018 support thanks to miettal!
|
||||||
* Honda Odyssey 2021-25 support thanks to csouers and MVL!
|
* Honda Odyssey 2021-25 support thanks to csouers and MVL!
|
||||||
|
* Honda Passport 2026 support thanks to vanillagorillaa and MVL!
|
||||||
|
|
||||||
Version 0.10.0 (2025-08-05)
|
Version 0.10.0 (2025-08-05)
|
||||||
========================
|
========================
|
||||||
|
|||||||
+103
-248
@@ -3,176 +3,52 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
import platform
|
import platform
|
||||||
|
import shlex
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
import SCons.Errors
|
import SCons.Errors
|
||||||
|
|
||||||
SCons.Warnings.warningAsException(True)
|
SCons.Warnings.warningAsException(True)
|
||||||
|
|
||||||
# pending upstream fix - https://github.com/SCons/scons/issues/4461
|
|
||||||
#SetOption('warn', 'all')
|
|
||||||
|
|
||||||
TICI = os.path.isfile('/TICI')
|
|
||||||
AGNOS = TICI
|
|
||||||
|
|
||||||
Decider('MD5-timestamp')
|
Decider('MD5-timestamp')
|
||||||
|
|
||||||
SetOption('num_jobs', max(1, int(os.cpu_count()/2)))
|
SetOption('num_jobs', max(1, int(os.cpu_count()/2)))
|
||||||
|
|
||||||
AddOption('--kaitai',
|
AddOption('--kaitai', action='store_true', help='Regenerate kaitai struct parsers')
|
||||||
action='store_true',
|
AddOption('--asan', action='store_true', help='turn on ASAN')
|
||||||
help='Regenerate kaitai struct parsers')
|
AddOption('--ubsan', action='store_true', help='turn on UBSan')
|
||||||
|
AddOption('--mutation', action='store_true', help='generate mutation-ready code')
|
||||||
AddOption('--asan',
|
AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line')
|
||||||
action='store_true',
|
|
||||||
help='turn on ASAN')
|
|
||||||
|
|
||||||
AddOption('--ubsan',
|
|
||||||
action='store_true',
|
|
||||||
help='turn on UBSan')
|
|
||||||
|
|
||||||
AddOption('--coverage',
|
|
||||||
action='store_true',
|
|
||||||
help='build with test coverage options')
|
|
||||||
|
|
||||||
AddOption('--clazy',
|
|
||||||
action='store_true',
|
|
||||||
help='build with clazy')
|
|
||||||
|
|
||||||
AddOption('--ccflags',
|
|
||||||
action='store',
|
|
||||||
type='string',
|
|
||||||
default='',
|
|
||||||
help='pass arbitrary flags over the command line')
|
|
||||||
|
|
||||||
AddOption('--external-sconscript',
|
|
||||||
action='store',
|
|
||||||
metavar='FILE',
|
|
||||||
dest='external_sconscript',
|
|
||||||
help='add an external SConscript to the build')
|
|
||||||
|
|
||||||
AddOption('--mutation',
|
|
||||||
action='store_true',
|
|
||||||
help='generate mutation-ready code')
|
|
||||||
|
|
||||||
AddOption('--minimal',
|
AddOption('--minimal',
|
||||||
action='store_false',
|
action='store_false',
|
||||||
dest='extras',
|
dest='extras',
|
||||||
default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS)
|
default=os.path.exists(File('#.gitattributes').abspath), # minimal by default on release branch (where there's no LFS)
|
||||||
help='the minimum build to run openpilot. no tests, tools, etc.')
|
help='the minimum build to run openpilot. no tests, tools, etc.')
|
||||||
|
|
||||||
AddOption('--stock-ui',
|
# Detect platform
|
||||||
action='store_true',
|
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
||||||
dest='stock_ui',
|
|
||||||
default=False,
|
|
||||||
help='Build stock openpilot UI instead of sunnypilot UI')
|
|
||||||
|
|
||||||
## Architecture name breakdown (arch)
|
|
||||||
## - larch64: linux tici aarch64
|
|
||||||
## - aarch64: linux pc aarch64
|
|
||||||
## - x86_64: linux pc x64
|
|
||||||
## - Darwin: mac x64 or arm64
|
|
||||||
real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
arch = "Darwin"
|
arch = "Darwin"
|
||||||
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
|
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
|
||||||
elif arch == "aarch64" and AGNOS:
|
elif arch == "aarch64" and os.path.isfile('/TICI'):
|
||||||
arch = "larch64"
|
arch = "larch64"
|
||||||
assert arch in ["larch64", "aarch64", "x86_64", "Darwin"]
|
assert arch in [
|
||||||
|
"larch64", # linux tici arm64
|
||||||
lenv = {
|
"aarch64", # linux pc arm64
|
||||||
"PATH": os.environ['PATH'],
|
"x86_64", # linux pc x64
|
||||||
"PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
|
"Darwin", # macOS arm64 (x86 not supported)
|
||||||
|
]
|
||||||
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
|
|
||||||
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
|
||||||
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
|
||||||
}
|
|
||||||
|
|
||||||
rpath = []
|
|
||||||
|
|
||||||
if arch == "larch64":
|
|
||||||
cpppath = [
|
|
||||||
"#third_party/opencl/include",
|
|
||||||
]
|
|
||||||
|
|
||||||
libpath = [
|
|
||||||
"/usr/local/lib",
|
|
||||||
"/system/vendor/lib64",
|
|
||||||
f"#third_party/acados/{arch}/lib",
|
|
||||||
]
|
|
||||||
|
|
||||||
libpath += [
|
|
||||||
"#third_party/snpe/larch64",
|
|
||||||
"#third_party/libyuv/larch64/lib",
|
|
||||||
"/usr/lib/aarch64-linux-gnu"
|
|
||||||
]
|
|
||||||
cflags = ["-DQCOM2", "-mcpu=cortex-a57"]
|
|
||||||
cxxflags = ["-DQCOM2", "-mcpu=cortex-a57"]
|
|
||||||
rpath += ["/usr/local/lib"]
|
|
||||||
else:
|
|
||||||
cflags = []
|
|
||||||
cxxflags = []
|
|
||||||
cpppath = []
|
|
||||||
rpath += []
|
|
||||||
|
|
||||||
# MacOS
|
|
||||||
if arch == "Darwin":
|
|
||||||
libpath = [
|
|
||||||
f"#third_party/libyuv/{arch}/lib",
|
|
||||||
f"#third_party/acados/{arch}/lib",
|
|
||||||
f"{brew_prefix}/lib",
|
|
||||||
f"{brew_prefix}/opt/openssl@3.0/lib",
|
|
||||||
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
|
||||||
]
|
|
||||||
|
|
||||||
cflags += ["-DGL_SILENCE_DEPRECATION"]
|
|
||||||
cxxflags += ["-DGL_SILENCE_DEPRECATION"]
|
|
||||||
cpppath += [
|
|
||||||
f"{brew_prefix}/include",
|
|
||||||
f"{brew_prefix}/opt/openssl@3.0/include",
|
|
||||||
]
|
|
||||||
# Linux
|
|
||||||
else:
|
|
||||||
libpath = [
|
|
||||||
f"#third_party/acados/{arch}/lib",
|
|
||||||
f"#third_party/libyuv/{arch}/lib",
|
|
||||||
"/usr/lib",
|
|
||||||
"/usr/local/lib",
|
|
||||||
]
|
|
||||||
|
|
||||||
if arch == "x86_64":
|
|
||||||
libpath += [
|
|
||||||
f"#third_party/snpe/{arch}"
|
|
||||||
]
|
|
||||||
rpath += [
|
|
||||||
Dir(f"#third_party/snpe/{arch}").abspath,
|
|
||||||
]
|
|
||||||
|
|
||||||
if GetOption('asan'):
|
|
||||||
ccflags = ["-fsanitize=address", "-fno-omit-frame-pointer"]
|
|
||||||
ldflags = ["-fsanitize=address"]
|
|
||||||
elif GetOption('ubsan'):
|
|
||||||
ccflags = ["-fsanitize=undefined"]
|
|
||||||
ldflags = ["-fsanitize=undefined"]
|
|
||||||
else:
|
|
||||||
ccflags = []
|
|
||||||
ldflags = []
|
|
||||||
|
|
||||||
# no --as-needed on mac linker
|
|
||||||
if arch != "Darwin":
|
|
||||||
ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"]
|
|
||||||
|
|
||||||
if not GetOption('stock_ui'):
|
|
||||||
cflags += ["-DSUNNYPILOT"]
|
|
||||||
cxxflags += ["-DSUNNYPILOT"]
|
|
||||||
|
|
||||||
ccflags_option = GetOption('ccflags')
|
|
||||||
if ccflags_option:
|
|
||||||
ccflags += ccflags_option.split(' ')
|
|
||||||
|
|
||||||
env = Environment(
|
env = Environment(
|
||||||
ENV=lenv,
|
ENV={
|
||||||
|
"PATH": os.environ['PATH'],
|
||||||
|
"PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
|
||||||
|
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
|
||||||
|
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
||||||
|
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
||||||
|
},
|
||||||
|
CC='clang',
|
||||||
|
CXX='clang++',
|
||||||
CCFLAGS=[
|
CCFLAGS=[
|
||||||
"-g",
|
"-g",
|
||||||
"-fPIC",
|
"-fPIC",
|
||||||
@@ -185,37 +61,32 @@ env = Environment(
|
|||||||
"-Wno-c99-designator",
|
"-Wno-c99-designator",
|
||||||
"-Wno-reorder-init-list",
|
"-Wno-reorder-init-list",
|
||||||
"-Wno-vla-cxx-extension",
|
"-Wno-vla-cxx-extension",
|
||||||
] + cflags + ccflags,
|
],
|
||||||
|
CFLAGS=["-std=gnu11"],
|
||||||
CPPPATH=cpppath + [
|
CXXFLAGS=["-std=c++1z"],
|
||||||
|
CPPPATH=[
|
||||||
"#",
|
"#",
|
||||||
|
"#msgq",
|
||||||
|
"#third_party",
|
||||||
|
"#third_party/json11",
|
||||||
|
"#third_party/linux/include",
|
||||||
"#third_party/acados/include",
|
"#third_party/acados/include",
|
||||||
"#third_party/acados/include/blasfeo/include",
|
"#third_party/acados/include/blasfeo/include",
|
||||||
"#third_party/acados/include/hpipm/include",
|
"#third_party/acados/include/hpipm/include",
|
||||||
"#third_party/catch2/include",
|
"#third_party/catch2/include",
|
||||||
"#third_party/libyuv/include",
|
"#third_party/libyuv/include",
|
||||||
"#third_party/json11",
|
|
||||||
"#third_party/linux/include",
|
|
||||||
"#third_party/snpe/include",
|
"#third_party/snpe/include",
|
||||||
"#third_party",
|
|
||||||
"#msgq",
|
|
||||||
],
|
],
|
||||||
|
LIBPATH=[
|
||||||
CC='clang',
|
"#common",
|
||||||
CXX='clang++',
|
|
||||||
LINKFLAGS=ldflags,
|
|
||||||
|
|
||||||
RPATH=rpath,
|
|
||||||
|
|
||||||
CFLAGS=["-std=gnu11"] + cflags,
|
|
||||||
CXXFLAGS=["-std=c++1z"] + cxxflags,
|
|
||||||
LIBPATH=libpath + [
|
|
||||||
"#msgq_repo",
|
"#msgq_repo",
|
||||||
"#third_party",
|
"#third_party",
|
||||||
"#selfdrive/pandad",
|
"#selfdrive/pandad",
|
||||||
"#common",
|
|
||||||
"#rednose/helpers",
|
"#rednose/helpers",
|
||||||
|
f"#third_party/libyuv/{arch}/lib",
|
||||||
|
f"#third_party/acados/{arch}/lib",
|
||||||
],
|
],
|
||||||
|
RPATH=[],
|
||||||
CYTHONCFILESUFFIX=".cpp",
|
CYTHONCFILESUFFIX=".cpp",
|
||||||
COMPILATIONDB_USE_ABSPATH=True,
|
COMPILATIONDB_USE_ABSPATH=True,
|
||||||
REDNOSE_ROOT="#",
|
REDNOSE_ROOT="#",
|
||||||
@@ -223,30 +94,72 @@ env = Environment(
|
|||||||
toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
|
toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
|
||||||
)
|
)
|
||||||
|
|
||||||
if arch == "Darwin":
|
# Arch-specific flags and paths
|
||||||
# RPATH is not supported on macOS, instead use the linker flags
|
if arch == "larch64":
|
||||||
darwin_rpath_link_flags = [f"-Wl,-rpath,{path}" for path in env["RPATH"]]
|
env.Append(CPPPATH=["#third_party/opencl/include"])
|
||||||
env["LINKFLAGS"] += darwin_rpath_link_flags
|
env.Append(LIBPATH=[
|
||||||
|
"/usr/local/lib",
|
||||||
|
"/system/vendor/lib64",
|
||||||
|
"/usr/lib/aarch64-linux-gnu",
|
||||||
|
"#third_party/snpe/larch64",
|
||||||
|
])
|
||||||
|
arch_flags = ["-D__TICI__", "-mcpu=cortex-a57", "-DQCOM2"]
|
||||||
|
env.Append(CCFLAGS=arch_flags)
|
||||||
|
env.Append(CXXFLAGS=arch_flags)
|
||||||
|
elif arch == "Darwin":
|
||||||
|
env.Append(LIBPATH=[
|
||||||
|
f"{brew_prefix}/lib",
|
||||||
|
f"{brew_prefix}/opt/openssl@3.0/lib",
|
||||||
|
f"{brew_prefix}/opt/llvm/lib/c++",
|
||||||
|
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
||||||
|
])
|
||||||
|
env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
||||||
|
env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
||||||
|
env.Append(CPPPATH=[
|
||||||
|
f"{brew_prefix}/include",
|
||||||
|
f"{brew_prefix}/opt/openssl@3.0/include",
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
env.Append(LIBPATH=[
|
||||||
|
"/usr/lib",
|
||||||
|
"/usr/local/lib",
|
||||||
|
])
|
||||||
|
|
||||||
env.CompilationDatabase('compile_commands.json')
|
if arch == "x86_64":
|
||||||
|
env.Append(LIBPATH=[
|
||||||
|
f"#third_party/snpe/{arch}"
|
||||||
|
])
|
||||||
|
env.Append(RPATH=[
|
||||||
|
Dir(f"#third_party/snpe/{arch}").abspath,
|
||||||
|
])
|
||||||
|
|
||||||
# Setup cache dir
|
# Sanitizers and extra CCFLAGS from CLI
|
||||||
default_cache_dir = '/data/scons_cache' if AGNOS else '/tmp/scons_cache'
|
if GetOption('asan'):
|
||||||
cache_dir = ARGUMENTS.get('cache_dir', default_cache_dir)
|
env.Append(CCFLAGS=["-fsanitize=address", "-fno-omit-frame-pointer"])
|
||||||
CacheDir(cache_dir)
|
env.Append(LINKFLAGS=["-fsanitize=address"])
|
||||||
Clean(["."], cache_dir)
|
elif GetOption('ubsan'):
|
||||||
|
env.Append(CCFLAGS=["-fsanitize=undefined"])
|
||||||
|
env.Append(LINKFLAGS=["-fsanitize=undefined"])
|
||||||
|
|
||||||
|
_extra_cc = shlex.split(GetOption('ccflags') or '')
|
||||||
|
if _extra_cc:
|
||||||
|
env.Append(CCFLAGS=_extra_cc)
|
||||||
|
|
||||||
|
# no --as-needed on mac linker
|
||||||
|
if arch != "Darwin":
|
||||||
|
env.Append(LINKFLAGS=["-Wl,--as-needed", "-Wl,--no-undefined"])
|
||||||
|
|
||||||
|
# progress output
|
||||||
node_interval = 5
|
node_interval = 5
|
||||||
node_count = 0
|
node_count = 0
|
||||||
def progress_function(node):
|
def progress_function(node):
|
||||||
global node_count
|
global node_count
|
||||||
node_count += node_interval
|
node_count += node_interval
|
||||||
sys.stderr.write("progress: %d\n" % node_count)
|
sys.stderr.write("progress: %d\n" % node_count)
|
||||||
|
|
||||||
if os.environ.get('SCONS_PROGRESS'):
|
if os.environ.get('SCONS_PROGRESS'):
|
||||||
Progress(progress_function, interval=node_interval)
|
Progress(progress_function, interval=node_interval)
|
||||||
|
|
||||||
# Cython build environment
|
# ********** Cython build environment **********
|
||||||
py_include = sysconfig.get_paths()['include']
|
py_include = sysconfig.get_paths()['include']
|
||||||
envCython = env.Clone()
|
envCython = env.Clone()
|
||||||
envCython["CPPPATH"] += [py_include, np.get_include()]
|
envCython["CPPPATH"] += [py_include, np.get_include()]
|
||||||
@@ -255,84 +168,27 @@ envCython["CCFLAGS"].remove("-Werror")
|
|||||||
|
|
||||||
envCython["LIBS"] = []
|
envCython["LIBS"] = []
|
||||||
if arch == "Darwin":
|
if arch == "Darwin":
|
||||||
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"] + darwin_rpath_link_flags
|
envCython["LINKFLAGS"] = env["LINKFLAGS"] + ["-bundle", "-undefined", "dynamic_lookup"]
|
||||||
else:
|
else:
|
||||||
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
|
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
|
||||||
|
|
||||||
np_version = SCons.Script.Value(np.__version__)
|
np_version = SCons.Script.Value(np.__version__)
|
||||||
Export('envCython', 'np_version')
|
Export('envCython', 'np_version')
|
||||||
|
|
||||||
# Qt build environment
|
Export('env', 'arch')
|
||||||
qt_env = env.Clone()
|
|
||||||
qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "DBus", "Xml"]
|
|
||||||
|
|
||||||
qt_libs = []
|
# Setup cache dir
|
||||||
if arch == "Darwin":
|
cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache'
|
||||||
qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5"
|
CacheDir(cache_dir)
|
||||||
qt_dirs = [
|
Clean(["."], cache_dir)
|
||||||
os.path.join(qt_env['QTDIR'], "include"),
|
|
||||||
]
|
|
||||||
qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules]
|
|
||||||
qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")]
|
|
||||||
qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"]
|
|
||||||
qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin"))
|
|
||||||
else:
|
|
||||||
qt_install_prefix = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_PREFIX'], encoding='utf8').strip()
|
|
||||||
qt_install_headers = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_HEADERS'], encoding='utf8').strip()
|
|
||||||
|
|
||||||
qt_env['QTDIR'] = qt_install_prefix
|
# ********** start building stuff **********
|
||||||
qt_dirs = [
|
|
||||||
f"{qt_install_headers}",
|
|
||||||
]
|
|
||||||
|
|
||||||
qt_gui_path = os.path.join(qt_install_headers, "QtGui")
|
|
||||||
qt_gui_dirs = [d for d in os.listdir(qt_gui_path) if os.path.isdir(os.path.join(qt_gui_path, d))]
|
|
||||||
qt_dirs += [f"{qt_install_headers}/QtGui/{qt_gui_dirs[0]}/QtGui", ] if qt_gui_dirs else []
|
|
||||||
qt_dirs += [f"{qt_install_headers}/Qt{m}" for m in qt_modules]
|
|
||||||
|
|
||||||
qt_libs = [f"Qt5{m}" for m in qt_modules]
|
|
||||||
if arch == "larch64":
|
|
||||||
qt_libs += ["GLESv2", "wayland-client"]
|
|
||||||
qt_env.PrependENVPath('PATH', Dir("#third_party/qt5/larch64/bin/").abspath)
|
|
||||||
elif arch != "Darwin":
|
|
||||||
qt_libs += ["GL"]
|
|
||||||
qt_env['QT3DIR'] = qt_env['QTDIR']
|
|
||||||
qt_env.Tool('qt3')
|
|
||||||
|
|
||||||
qt_env['CPPPATH'] += qt_dirs + ["#third_party/qrcode"]
|
|
||||||
qt_flags = [
|
|
||||||
"-D_REENTRANT",
|
|
||||||
"-DQT_NO_DEBUG",
|
|
||||||
"-DQT_WIDGETS_LIB",
|
|
||||||
"-DQT_GUI_LIB",
|
|
||||||
"-DQT_CORE_LIB",
|
|
||||||
"-DQT_MESSAGELOGCONTEXT",
|
|
||||||
]
|
|
||||||
qt_env['CXXFLAGS'] += qt_flags
|
|
||||||
qt_env['LIBPATH'] += ['#selfdrive/ui', ]
|
|
||||||
qt_env['LIBS'] = qt_libs
|
|
||||||
|
|
||||||
if GetOption("clazy"):
|
|
||||||
checks = [
|
|
||||||
"level0",
|
|
||||||
"level1",
|
|
||||||
"no-range-loop",
|
|
||||||
"no-non-pod-global-static",
|
|
||||||
]
|
|
||||||
qt_env['CXX'] = 'clazy'
|
|
||||||
qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0]
|
|
||||||
qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks)
|
|
||||||
|
|
||||||
Export('env', 'qt_env', 'arch', 'real_arch')
|
|
||||||
|
|
||||||
# Build common module
|
# Build common module
|
||||||
SConscript(['common/SConscript'])
|
SConscript(['common/SConscript'])
|
||||||
Import('_common', '_gpucommon')
|
Import('_common')
|
||||||
|
|
||||||
common = [_common, 'json11', 'zmq']
|
common = [_common, 'json11', 'zmq']
|
||||||
gpucommon = [_gpucommon]
|
Export('common')
|
||||||
|
|
||||||
Export('common', 'gpucommon')
|
|
||||||
|
|
||||||
# Build messaging (cereal + msgq + socketmaster + their dependencies)
|
# Build messaging (cereal + msgq + socketmaster + their dependencies)
|
||||||
# Enable swaglog include in submodules
|
# Enable swaglog include in submodules
|
||||||
@@ -375,6 +231,5 @@ if Dir('#tools/cabana/').exists() and GetOption('extras'):
|
|||||||
if arch != "larch64":
|
if arch != "larch64":
|
||||||
SConscript(['tools/cabana/SConscript'])
|
SConscript(['tools/cabana/SConscript'])
|
||||||
|
|
||||||
external_sconscript = GetOption('external_sconscript')
|
|
||||||
if external_sconscript:
|
env.CompilationDatabase('compile_commands.json')
|
||||||
SConscript([external_sconscript])
|
|
||||||
|
|||||||
+15
-1
@@ -369,6 +369,7 @@ struct CarControlSP @0xa5cd762cd951a455 {
|
|||||||
leadOne @2 :LeadData;
|
leadOne @2 :LeadData;
|
||||||
leadTwo @3 :LeadData;
|
leadTwo @3 :LeadData;
|
||||||
intelligentCruiseButtonManagement @4 :IntelligentCruiseButtonManagement;
|
intelligentCruiseButtonManagement @4 :IntelligentCruiseButtonManagement;
|
||||||
|
speed @5 :Float32;
|
||||||
|
|
||||||
struct Param {
|
struct Param {
|
||||||
key @0 :Text;
|
key @0 :Text;
|
||||||
@@ -454,7 +455,20 @@ struct ModelDataV2SP @0xa1680744031fdb2d {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CustomReserved10 @0xcb9fd56c7057593a {
|
struct Navigationd @0xcb9fd56c7057593a {
|
||||||
|
upcomingTurn @0 :Text;
|
||||||
|
currentSpeedLimit @1 :UInt16;
|
||||||
|
bannerInstructions @2 :Text;
|
||||||
|
distanceFromRoute @3 :Float32;
|
||||||
|
allManeuvers @4 :List(Maneuver);
|
||||||
|
valid @5 :Bool;
|
||||||
|
|
||||||
|
struct Maneuver {
|
||||||
|
distance @0 :Float32;
|
||||||
|
type @1 :Text;
|
||||||
|
modifier @2 :Text;
|
||||||
|
instruction @3 :Text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CustomReserved11 @0xc2243c65e0340384 {
|
struct CustomReserved11 @0xc2243c65e0340384 {
|
||||||
|
|||||||
+9
-6
@@ -918,6 +918,8 @@ struct ControlsState @0x97ff69c53601abf1 {
|
|||||||
saturated @7 :Bool;
|
saturated @7 :Bool;
|
||||||
actualLateralAccel @9 :Float32;
|
actualLateralAccel @9 :Float32;
|
||||||
desiredLateralAccel @10 :Float32;
|
desiredLateralAccel @10 :Float32;
|
||||||
|
desiredLateralJerk @11 :Float32;
|
||||||
|
version @12 :Int32;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LateralLQRState {
|
struct LateralLQRState {
|
||||||
@@ -2146,13 +2148,10 @@ struct Joystick {
|
|||||||
struct DriverStateV2 {
|
struct DriverStateV2 {
|
||||||
frameId @0 :UInt32;
|
frameId @0 :UInt32;
|
||||||
modelExecutionTime @1 :Float32;
|
modelExecutionTime @1 :Float32;
|
||||||
dspExecutionTimeDEPRECATED @2 :Float32;
|
|
||||||
gpuExecutionTime @8 :Float32;
|
gpuExecutionTime @8 :Float32;
|
||||||
rawPredictions @3 :Data;
|
rawPredictions @3 :Data;
|
||||||
|
|
||||||
poorVisionProb @4 :Float32;
|
|
||||||
wheelOnRightProb @5 :Float32;
|
wheelOnRightProb @5 :Float32;
|
||||||
|
|
||||||
leftDriverData @6 :DriverData;
|
leftDriverData @6 :DriverData;
|
||||||
rightDriverData @7 :DriverData;
|
rightDriverData @7 :DriverData;
|
||||||
|
|
||||||
@@ -2167,10 +2166,13 @@ struct DriverStateV2 {
|
|||||||
leftBlinkProb @7 :Float32;
|
leftBlinkProb @7 :Float32;
|
||||||
rightBlinkProb @8 :Float32;
|
rightBlinkProb @8 :Float32;
|
||||||
sunglassesProb @9 :Float32;
|
sunglassesProb @9 :Float32;
|
||||||
occludedProb @10 :Float32;
|
|
||||||
readyProb @11 :List(Float32);
|
|
||||||
notReadyProb @12 :List(Float32);
|
notReadyProb @12 :List(Float32);
|
||||||
|
occludedProbDEPRECATED @10 :Float32;
|
||||||
|
readyProbDEPRECATED @11 :List(Float32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dspExecutionTimeDEPRECATED @2 :Float32;
|
||||||
|
poorVisionProbDEPRECATED @4 :Float32;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
|
struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
|
||||||
@@ -2222,6 +2224,7 @@ struct DriverMonitoringState @0xb83cda094a1da284 {
|
|||||||
hiStdCount @14 :UInt32;
|
hiStdCount @14 :UInt32;
|
||||||
isActiveMode @16 :Bool;
|
isActiveMode @16 :Bool;
|
||||||
isRHD @4 :Bool;
|
isRHD @4 :Bool;
|
||||||
|
uncertainCount @19 :UInt32;
|
||||||
|
|
||||||
isPreviewDEPRECATED @15 :Bool;
|
isPreviewDEPRECATED @15 :Bool;
|
||||||
rhdCheckedDEPRECATED @5 :Bool;
|
rhdCheckedDEPRECATED @5 :Bool;
|
||||||
@@ -2632,7 +2635,7 @@ struct Event {
|
|||||||
carStateSP @114 :Custom.CarStateSP;
|
carStateSP @114 :Custom.CarStateSP;
|
||||||
liveMapDataSP @115 :Custom.LiveMapDataSP;
|
liveMapDataSP @115 :Custom.LiveMapDataSP;
|
||||||
modelDataV2SP @116 :Custom.ModelDataV2SP;
|
modelDataV2SP @116 :Custom.ModelDataV2SP;
|
||||||
customReserved10 @136 :Custom.CustomReserved10;
|
navigationd @136 :Custom.Navigationd;
|
||||||
customReserved11 @137 :Custom.CustomReserved11;
|
customReserved11 @137 :Custom.CustomReserved11;
|
||||||
customReserved12 @138 :Custom.CustomReserved12;
|
customReserved12 @138 :Custom.CustomReserved12;
|
||||||
customReserved13 @139 :Custom.CustomReserved13;
|
customReserved13 @139 :Custom.CustomReserved13;
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ _services: dict[str, tuple] = {
|
|||||||
"carStateSP": (True, 100., 10),
|
"carStateSP": (True, 100., 10),
|
||||||
"liveMapDataSP": (True, 1., 1),
|
"liveMapDataSP": (True, 1., 1),
|
||||||
"modelDataV2SP": (True, 20.),
|
"modelDataV2SP": (True, 20.),
|
||||||
|
"navigationd": (True, 3.),
|
||||||
"liveLocationKalman": (True, 20.),
|
"liveLocationKalman": (True, 20.),
|
||||||
|
|
||||||
# debug
|
# debug
|
||||||
|
|||||||
+3
-9
@@ -4,18 +4,12 @@ common_libs = [
|
|||||||
'params.cc',
|
'params.cc',
|
||||||
'swaglog.cc',
|
'swaglog.cc',
|
||||||
'util.cc',
|
'util.cc',
|
||||||
'watchdog.cc',
|
'ratekeeper.cc',
|
||||||
'ratekeeper.cc'
|
|
||||||
]
|
|
||||||
|
|
||||||
_common = env.Library('common', common_libs, LIBS="json11")
|
|
||||||
|
|
||||||
files = [
|
|
||||||
'clutil.cc',
|
'clutil.cc',
|
||||||
]
|
]
|
||||||
|
|
||||||
_gpucommon = env.Library('gpucommon', files)
|
_common = env.Library('common', common_libs, LIBS="json11")
|
||||||
Export('_common', '_gpucommon')
|
Export('_common')
|
||||||
|
|
||||||
if GetOption('extras'):
|
if GetOption('extras'):
|
||||||
env.Program('tests/test_common',
|
env.Program('tests/test_common',
|
||||||
|
|||||||
@@ -14,9 +14,13 @@ class Api:
|
|||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
return self.service.post(*args, **kwargs)
|
return self.service.post(*args, **kwargs)
|
||||||
|
|
||||||
def get_token(self, expiry_hours=1):
|
def get_token(self, payload_extra=None, expiry_hours=1):
|
||||||
return self.service.get_token(expiry_hours)
|
return self.service.get_token(payload_extra, expiry_hours)
|
||||||
|
|
||||||
|
|
||||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
|
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
|
||||||
return CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, **params)
|
return CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, **params)
|
||||||
|
|
||||||
|
|
||||||
|
def get_key_pair():
|
||||||
|
return CommaConnectApi(None).get_key_pair()
|
||||||
|
|||||||
+20
-6
@@ -1,18 +1,22 @@
|
|||||||
import jwt
|
import jwt
|
||||||
|
import os
|
||||||
import requests
|
import requests
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from datetime import datetime, timedelta, UTC
|
from datetime import datetime, timedelta, UTC
|
||||||
from openpilot.system.hardware.hw import Paths
|
from openpilot.system.hardware.hw import Paths
|
||||||
from openpilot.system.version import get_version
|
from openpilot.system.version import get_version
|
||||||
|
|
||||||
|
# name : jwt signature algorithm
|
||||||
|
KEYS = {"id_rsa" : "RS256",
|
||||||
|
"id_ecdsa" : "ES256"}
|
||||||
|
|
||||||
|
|
||||||
class BaseApi:
|
class BaseApi:
|
||||||
def __init__(self, dongle_id, api_host, user_agent="openpilot-"):
|
def __init__(self, dongle_id, api_host, user_agent="openpilot-"):
|
||||||
self.dongle_id = dongle_id
|
self.dongle_id = dongle_id
|
||||||
self.api_host = api_host
|
self.api_host = api_host
|
||||||
self.user_agent = user_agent
|
self.user_agent = user_agent
|
||||||
with open(f'{Paths.persist_root()}/comma/id_rsa') as f:
|
self.jwt_algorithm, self.private_key, _ = self.get_key_pair()
|
||||||
self.private_key = f.read()
|
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
return self.request('GET', *args, **kwargs)
|
return self.request('GET', *args, **kwargs)
|
||||||
@@ -23,7 +27,7 @@ class BaseApi:
|
|||||||
def request(self, method, endpoint, timeout=None, access_token=None, **params):
|
def request(self, method, endpoint, timeout=None, access_token=None, **params):
|
||||||
return self.api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
|
return self.api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
|
||||||
|
|
||||||
def _get_token(self, expiry_hours=1, **extra_payload):
|
def _get_token(self, payload_extra=None, expiry_hours=1, **extra_payload):
|
||||||
now = datetime.now(UTC).replace(tzinfo=None)
|
now = datetime.now(UTC).replace(tzinfo=None)
|
||||||
payload = {
|
payload = {
|
||||||
'identity': self.dongle_id,
|
'identity': self.dongle_id,
|
||||||
@@ -32,13 +36,15 @@ class BaseApi:
|
|||||||
'exp': now + timedelta(hours=expiry_hours),
|
'exp': now + timedelta(hours=expiry_hours),
|
||||||
**extra_payload
|
**extra_payload
|
||||||
}
|
}
|
||||||
token = jwt.encode(payload, self.private_key, algorithm='RS256')
|
if payload_extra is not None:
|
||||||
|
payload.update(payload_extra)
|
||||||
|
token = jwt.encode(payload, self.private_key, algorithm=self.jwt_algorithm)
|
||||||
if isinstance(token, bytes):
|
if isinstance(token, bytes):
|
||||||
token = token.decode('utf8')
|
token = token.decode('utf8')
|
||||||
return token
|
return token
|
||||||
|
|
||||||
def get_token(self, expiry_hours=1):
|
def get_token(self, payload_extra=None, expiry_hours=1):
|
||||||
return self._get_token(expiry_hours)
|
return self._get_token(payload_extra, expiry_hours)
|
||||||
|
|
||||||
def remove_non_ascii_chars(self, text):
|
def remove_non_ascii_chars(self, text):
|
||||||
normalized_text = unicodedata.normalize('NFD', text)
|
normalized_text = unicodedata.normalize('NFD', text)
|
||||||
@@ -54,3 +60,11 @@ class BaseApi:
|
|||||||
headers['User-Agent'] = self.user_agent + version
|
headers['User-Agent'] = self.user_agent + version
|
||||||
|
|
||||||
return requests.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, json=json, params=params)
|
return requests.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, json=json, params=params)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_key_pair():
|
||||||
|
for key in KEYS:
|
||||||
|
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
|
||||||
|
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
|
||||||
|
return KEYS[key], private.read(), public.read()
|
||||||
|
return None, None, None
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
# remove all keys that end in DEPRECATED
|
|
||||||
def strip_deprecated_keys(d):
|
|
||||||
for k in list(d.keys()):
|
|
||||||
if isinstance(k, str):
|
|
||||||
if k.endswith('DEPRECATED'):
|
|
||||||
d.pop(k)
|
|
||||||
elif isinstance(d[k], dict):
|
|
||||||
strip_deprecated_keys(d[k])
|
|
||||||
return d
|
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
from functools import cache
|
from functools import cache
|
||||||
import subprocess
|
import subprocess
|
||||||
from openpilot.common.run import run_cmd, run_cmd_default
|
from openpilot.common.utils import run_cmd, run_cmd_default
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
#define DEFAULT_MODEL "Firehose (Default)"
|
#define DEFAULT_MODEL "The Cool People (Default)"
|
||||||
|
|||||||
+15
-1
@@ -66,7 +66,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"JoystickDebugMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"JoystickDebugMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
{"LanguageSetting", {PERSISTENT | BACKUP, STRING, "main_en"}},
|
{"LanguageSetting", {PERSISTENT | BACKUP, STRING, "en"}},
|
||||||
{"LastAthenaPingTime", {CLEAR_ON_MANAGER_START, INT}},
|
{"LastAthenaPingTime", {CLEAR_ON_MANAGER_START, INT}},
|
||||||
{"LastGPSPosition", {PERSISTENT, STRING}},
|
{"LastGPSPosition", {PERSISTENT, STRING}},
|
||||||
{"LastManagerExitReason", {CLEAR_ON_MANAGER_START, STRING}},
|
{"LastManagerExitReason", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
@@ -97,6 +97,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"Offroad_TemperatureTooHigh", {CLEAR_ON_MANAGER_START, JSON}},
|
{"Offroad_TemperatureTooHigh", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
{"Offroad_UnregisteredHardware", {CLEAR_ON_MANAGER_START, JSON}},
|
{"Offroad_UnregisteredHardware", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
{"Offroad_UpdateFailed", {CLEAR_ON_MANAGER_START, JSON}},
|
{"Offroad_UpdateFailed", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
|
{"Offroad_DriverMonitoringUncertain", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||||
{"OnroadCycleRequested", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"OnroadCycleRequested", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"OpenpilotEnabledToggle", {PERSISTENT | BACKUP, BOOL, "1"}},
|
{"OpenpilotEnabledToggle", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||||
{"PandaHeartbeatLost", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"PandaHeartbeatLost", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
@@ -108,6 +109,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"RecordFront", {PERSISTENT | BACKUP, BOOL}},
|
{"RecordFront", {PERSISTENT | BACKUP, BOOL}},
|
||||||
{"RecordFrontLock", {PERSISTENT, BOOL}}, // for the internal fleet
|
{"RecordFrontLock", {PERSISTENT, BOOL}}, // for the internal fleet
|
||||||
{"SecOCKey", {PERSISTENT | DONT_LOG | BACKUP, STRING}},
|
{"SecOCKey", {PERSISTENT | DONT_LOG | BACKUP, STRING}},
|
||||||
|
{"ShowDebugInfo", {PERSISTENT, BOOL}},
|
||||||
{"RouteCount", {PERSISTENT, INT, "0"}},
|
{"RouteCount", {PERSISTENT, INT, "0"}},
|
||||||
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
{"SshEnabled", {PERSISTENT | BACKUP, BOOL}},
|
{"SshEnabled", {PERSISTENT | BACKUP, BOOL}},
|
||||||
@@ -154,6 +156,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"IntelligentCruiseButtonManagement", {PERSISTENT | BACKUP , BOOL}},
|
{"IntelligentCruiseButtonManagement", {PERSISTENT | BACKUP , BOOL}},
|
||||||
{"InteractivityTimeout", {PERSISTENT | BACKUP, INT, "0"}},
|
{"InteractivityTimeout", {PERSISTENT | BACKUP, INT, "0"}},
|
||||||
{"IsDevelopmentBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"IsDevelopmentBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
|
{"IsReleaseSpBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"LastGPSPositionLLK", {PERSISTENT, STRING}},
|
{"LastGPSPositionLLK", {PERSISTENT, STRING}},
|
||||||
{"LeadDepartAlert", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"LeadDepartAlert", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"MaxTimeOffroad", {PERSISTENT | BACKUP, INT, "1800"}},
|
{"MaxTimeOffroad", {PERSISTENT | BACKUP, INT, "1800"}},
|
||||||
@@ -170,6 +173,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"ShowAdvancedControls", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"ShowAdvancedControls", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
{"sunnypilot_ui", {PERSISTENT, BOOL, "1"}},
|
||||||
{"TrueVEgoUI", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"TrueVEgoUI", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
|
||||||
// MADS params
|
// MADS params
|
||||||
@@ -186,6 +190,15 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"ModelManager_LastSyncTime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}},
|
{"ModelManager_LastSyncTime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}},
|
||||||
{"ModelManager_ModelsCache", {PERSISTENT | BACKUP, JSON}},
|
{"ModelManager_ModelsCache", {PERSISTENT | BACKUP, JSON}},
|
||||||
|
|
||||||
|
// Navigation params
|
||||||
|
{"AllowNavigation", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
{"MapboxFavorites", {PERSISTENT | BACKUP, STRING}},
|
||||||
|
{"MapboxToken", {PERSISTENT | BACKUP, STRING}},
|
||||||
|
{"MapboxSettings", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
|
{"MapboxRoute", {PERSISTENT, STRING}},
|
||||||
|
{"MapboxRecompute", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
{"NavDesiresAllowed", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
|
||||||
// Neural Network Lateral Control
|
// Neural Network Lateral Control
|
||||||
{"NeuralNetworkLateralControl", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"NeuralNetworkLateralControl", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
|
||||||
@@ -207,6 +220,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"HyundaiLongitudinalTuning", {PERSISTENT | BACKUP, INT, "0"}},
|
{"HyundaiLongitudinalTuning", {PERSISTENT | BACKUP, INT, "0"}},
|
||||||
{"SubaruStopAndGo", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"SubaruStopAndGo", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"SubaruStopAndGoManualParkingBrake", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"SubaruStopAndGoManualParkingBrake", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
{"TeslaCoopSteering", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
|
||||||
{"DynamicExperimentalControl", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"DynamicExperimentalControl", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"BlindSpot", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"BlindSpot", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
|||||||
+6
-7
@@ -2,11 +2,10 @@ import numpy as np
|
|||||||
from numbers import Number
|
from numbers import Number
|
||||||
|
|
||||||
class PIDController:
|
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):
|
||||||
self._k_p = k_p
|
self._k_p = k_p
|
||||||
self._k_i = k_i
|
self._k_i = k_i
|
||||||
self._k_d = k_d
|
self._k_d = k_d
|
||||||
self.k_f = k_f # feedforward gain
|
|
||||||
if isinstance(self._k_p, Number):
|
if isinstance(self._k_p, Number):
|
||||||
self._k_p = [[0], [self._k_p]]
|
self._k_p = [[0], [self._k_p]]
|
||||||
if isinstance(self._k_i, Number):
|
if isinstance(self._k_i, Number):
|
||||||
@@ -16,7 +15,7 @@ class PIDController:
|
|||||||
|
|
||||||
self.set_limits(pos_limit, neg_limit)
|
self.set_limits(pos_limit, neg_limit)
|
||||||
|
|
||||||
self.i_rate = 1.0 / rate
|
self.i_dt = 1.0 / rate
|
||||||
self.speed = 0.0
|
self.speed = 0.0
|
||||||
|
|
||||||
self.reset()
|
self.reset()
|
||||||
@@ -46,12 +45,12 @@ class PIDController:
|
|||||||
|
|
||||||
def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False):
|
def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False):
|
||||||
self.speed = speed
|
self.speed = speed
|
||||||
self.p = float(error) * self.k_p
|
self.p = self.k_p * float(error)
|
||||||
self.f = feedforward * self.k_f
|
self.d = self.k_d * error_rate
|
||||||
self.d = error_rate * self.k_d
|
self.f = feedforward
|
||||||
|
|
||||||
if not freeze_integrator:
|
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
|
# Don't allow windup if already clipping
|
||||||
test_control = self.p + i + self.d + self.f
|
test_control = self.p + i + self.d + self.f
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import time
|
|
||||||
import functools
|
|
||||||
|
|
||||||
from openpilot.common.swaglog import cloudlog
|
|
||||||
|
|
||||||
|
|
||||||
def retry(attempts=3, delay=1.0, ignore_failure=False):
|
|
||||||
def decorator(func):
|
|
||||||
@functools.wraps(func)
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
for _ in range(attempts):
|
|
||||||
try:
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
except Exception:
|
|
||||||
cloudlog.exception(f"{func.__name__} failed, trying again")
|
|
||||||
time.sleep(delay)
|
|
||||||
|
|
||||||
if ignore_failure:
|
|
||||||
cloudlog.error(f"{func.__name__} failed after retry")
|
|
||||||
else:
|
|
||||||
raise Exception(f"{func.__name__} failed after retry")
|
|
||||||
return wrapper
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
@retry(attempts=10)
|
|
||||||
def abc():
|
|
||||||
raise ValueError("abc failed :(")
|
|
||||||
abc()
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
from contextlib import contextmanager
|
|
||||||
from subprocess import Popen, PIPE, TimeoutExpired
|
|
||||||
|
|
||||||
|
|
||||||
def run_cmd(cmd: list[str], cwd=None, env=None) -> str:
|
|
||||||
return subprocess.check_output(cmd, encoding='utf8', cwd=cwd, env=env).strip()
|
|
||||||
|
|
||||||
|
|
||||||
def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> str:
|
|
||||||
try:
|
|
||||||
return run_cmd(cmd, cwd=cwd, env=env)
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def managed_proc(cmd: list[str], env: dict[str, str]):
|
|
||||||
proc = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE)
|
|
||||||
try:
|
|
||||||
yield proc
|
|
||||||
finally:
|
|
||||||
if proc.poll() is None:
|
|
||||||
proc.terminate()
|
|
||||||
try:
|
|
||||||
proc.wait(timeout=5)
|
|
||||||
except TimeoutExpired:
|
|
||||||
proc.kill()
|
|
||||||
+3
-1
@@ -15,6 +15,8 @@
|
|||||||
#include "common/version.h"
|
#include "common/version.h"
|
||||||
#include "system/hardware/hw.h"
|
#include "system/hardware/hw.h"
|
||||||
|
|
||||||
|
#include "sunnypilot/common/version.h"
|
||||||
|
|
||||||
class SwaglogState {
|
class SwaglogState {
|
||||||
public:
|
public:
|
||||||
SwaglogState() {
|
SwaglogState() {
|
||||||
@@ -56,7 +58,7 @@ public:
|
|||||||
if (char* daemon_name = getenv("MANAGER_DAEMON")) {
|
if (char* daemon_name = getenv("MANAGER_DAEMON")) {
|
||||||
ctx_j["daemon"] = daemon_name;
|
ctx_j["daemon"] = daemon_name;
|
||||||
}
|
}
|
||||||
ctx_j["version"] = COMMA_VERSION;
|
ctx_j["version"] = SUNNYPILOT_VERSION;
|
||||||
ctx_j["dirty"] = !getenv("CLEAN");
|
ctx_j["dirty"] = !getenv("CLEAN");
|
||||||
ctx_j["device"] = Hardware::get_name();
|
ctx_j["device"] = Hardware::get_name();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from openpilot.common.file_helpers import atomic_write_in_dir
|
from openpilot.common.utils import atomic_write_in_dir
|
||||||
|
|
||||||
|
|
||||||
class TestFileHelpers:
|
class TestFileHelpers:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from openpilot.common.markdown import parse_markdown
|
|||||||
|
|
||||||
class TestMarkdown:
|
class TestMarkdown:
|
||||||
def test_all_release_notes(self):
|
def test_all_release_notes(self):
|
||||||
with open(os.path.join(BASEDIR, "RELEASES.md")) as f:
|
with open(os.path.join(BASEDIR, "CHANGELOG.md")) as f:
|
||||||
release_notes = f.read().split("\n\n")
|
release_notes = f.read().split("\n\n")
|
||||||
assert len(release_notes) > 10
|
assert len(release_notes) > 10
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include "system/hardware/hw.h"
|
#include "system/hardware/hw.h"
|
||||||
#include "third_party/json11/json11.hpp"
|
#include "third_party/json11/json11.hpp"
|
||||||
|
|
||||||
|
#include "sunnypilot/common/version.h"
|
||||||
|
|
||||||
std::string daemon_name = "testy";
|
std::string daemon_name = "testy";
|
||||||
std::string dongle_id = "test_dongle_id";
|
std::string dongle_id = "test_dongle_id";
|
||||||
int LINE_NO = 0;
|
int LINE_NO = 0;
|
||||||
@@ -53,7 +55,7 @@ void recv_log(int thread_cnt, int thread_msg_cnt) {
|
|||||||
REQUIRE(ctx["dongle_id"].string_value() == dongle_id);
|
REQUIRE(ctx["dongle_id"].string_value() == dongle_id);
|
||||||
REQUIRE(ctx["dirty"].bool_value() == true);
|
REQUIRE(ctx["dirty"].bool_value() == true);
|
||||||
|
|
||||||
REQUIRE(ctx["version"].string_value() == COMMA_VERSION);
|
REQUIRE(ctx["version"].string_value() == SUNNYPILOT_VERSION);
|
||||||
|
|
||||||
std::string device = Hardware::get_name();
|
std::string device = Hardware::get_name();
|
||||||
REQUIRE(ctx["device"].string_value() == device);
|
REQUIRE(ctx["device"].string_value() == device);
|
||||||
|
|||||||
@@ -2,9 +2,14 @@ import io
|
|||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import functools
|
||||||
|
from subprocess import Popen, PIPE, TimeoutExpired
|
||||||
import zstandard as zstd
|
import zstandard as zstd
|
||||||
|
from openpilot.common.swaglog import cloudlog
|
||||||
|
|
||||||
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
|
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
|
||||||
|
|
||||||
|
|
||||||
class CallbackReader:
|
class CallbackReader:
|
||||||
@@ -27,7 +32,7 @@ class CallbackReader:
|
|||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str = None, newline: str = None,
|
def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str | None = None, newline: str | None = None,
|
||||||
overwrite: bool = False):
|
overwrite: bool = False):
|
||||||
"""Write to a file atomically using a temporary file in the same directory as the destination file."""
|
"""Write to a file atomically using a temporary file in the same directory as the destination file."""
|
||||||
dir_name = os.path.dirname(path)
|
dir_name = os.path.dirname(path)
|
||||||
@@ -56,3 +61,58 @@ def get_upload_stream(filepath: str, should_compress: bool) -> tuple[io.Buffered
|
|||||||
compressed_size = compressed_stream.tell()
|
compressed_size = compressed_stream.tell()
|
||||||
compressed_stream.seek(0)
|
compressed_stream.seek(0)
|
||||||
return compressed_stream, compressed_size
|
return compressed_stream, compressed_size
|
||||||
|
|
||||||
|
|
||||||
|
# remove all keys that end in DEPRECATED
|
||||||
|
def strip_deprecated_keys(d):
|
||||||
|
for k in list(d.keys()):
|
||||||
|
if isinstance(k, str):
|
||||||
|
if k.endswith('DEPRECATED'):
|
||||||
|
d.pop(k)
|
||||||
|
elif isinstance(d[k], dict):
|
||||||
|
strip_deprecated_keys(d[k])
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def run_cmd(cmd: list[str], cwd=None, env=None) -> str:
|
||||||
|
return subprocess.check_output(cmd, encoding='utf8', cwd=cwd, env=env).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> str:
|
||||||
|
try:
|
||||||
|
return run_cmd(cmd, cwd=cwd, env=env)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def managed_proc(cmd: list[str], env: dict[str, str]):
|
||||||
|
proc = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE)
|
||||||
|
try:
|
||||||
|
yield proc
|
||||||
|
finally:
|
||||||
|
if proc.poll() is None:
|
||||||
|
proc.terminate()
|
||||||
|
try:
|
||||||
|
proc.wait(timeout=5)
|
||||||
|
except TimeoutExpired:
|
||||||
|
proc.kill()
|
||||||
|
|
||||||
|
|
||||||
|
def retry(attempts=3, delay=1.0, ignore_failure=False):
|
||||||
|
def decorator(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
for _ in range(attempts):
|
||||||
|
try:
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
except Exception:
|
||||||
|
cloudlog.exception(f"{func.__name__} failed, trying again")
|
||||||
|
time.sleep(delay)
|
||||||
|
|
||||||
|
if ignore_failure:
|
||||||
|
cloudlog.error(f"{func.__name__} failed after retry")
|
||||||
|
else:
|
||||||
|
raise Exception(f"{func.__name__} failed after retry")
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
#define COMMA_VERSION "0.10.1"
|
#define COMMA_VERSION "0.10.2"
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
#include <string>
|
|
||||||
|
|
||||||
#include "common/watchdog.h"
|
|
||||||
#include "common/util.h"
|
|
||||||
#include "system/hardware/hw.h"
|
|
||||||
|
|
||||||
const std::string watchdog_fn_prefix = Path::shm_path() + "/wd_"; // + <pid>
|
|
||||||
|
|
||||||
bool watchdog_kick(uint64_t ts) {
|
|
||||||
static std::string fn = watchdog_fn_prefix + std::to_string(getpid());
|
|
||||||
return util::write_file(fn.c_str(), &ts, sizeof(ts), O_WRONLY | O_CREAT) > 0;
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
bool watchdog_kick(uint64_t ts);
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import os
|
|
||||||
import time
|
|
||||||
import struct
|
|
||||||
from openpilot.system.hardware.hw import Paths
|
|
||||||
|
|
||||||
WATCHDOG_FN = f"{Paths.shm_path()}/wd_"
|
|
||||||
_LAST_KICK = 0.0
|
|
||||||
|
|
||||||
def kick_watchdog():
|
|
||||||
global _LAST_KICK
|
|
||||||
current_time = time.monotonic()
|
|
||||||
|
|
||||||
if current_time - _LAST_KICK < 1.0:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(f"{WATCHDOG_FN}{os.getpid()}", 'wb') as f:
|
|
||||||
f.write(struct.pack('<Q', int(current_time * 1e9)))
|
|
||||||
f.flush()
|
|
||||||
_LAST_KICK = current_time
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
+16
-14
@@ -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.
|
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.
|
||||||
|
|
||||||
# 337 Supported Cars
|
# 339 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|
|
|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|
|
||||||
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
@@ -21,7 +21,10 @@ 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,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Audi S3 2015-17">Buy Here</a></sub></details>|||
|
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<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 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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EV 2022-23">Buy Here</a></sub></details>|||
|
|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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Equinox 2019-22">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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Silverado 1500 2020-21">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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Trailblazer 2021-22">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 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<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 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica 2017-18">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 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica 2017-18">Buy Here</a></sub></details>|||
|
||||||
@@ -236,20 +239,20 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Rivian A connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1T 2022-24">Buy Here</a></sub></details>||<a href="https://youtu.be/uaISd1j7Z4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
|Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Rivian A connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1T 2022-24">Buy Here</a></sub></details>||<a href="https://youtu.be/uaISd1j7Z4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Ateca 2016-23">Buy Here</a></sub></details>|||
|
|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Ateca 2016-23">Buy Here</a></sub></details>|||
|
||||||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Leon 2014-20">Buy Here</a></sub></details>|||
|
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Leon 2014-20">Buy Here</a></sub></details>|||
|
||||||
|Subaru|Ascent 2019-21|All[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Ascent 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Ascent 2019-21|All[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Ascent 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2020-23">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2020-23">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Forester 2017-18|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Forester 2017-18">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Forester 2017-18|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Forester 2017-18">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Forester 2019-21|All[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Forester 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Forester 2019-21|All[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Forester 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Impreza 2017-19|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Impreza 2017-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Impreza 2017-19|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Impreza 2017-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Impreza 2020-22|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Impreza 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Impreza 2020-22|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Impreza 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Legacy 2015-18|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Legacy 2015-18">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Legacy 2015-18|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Legacy 2015-18">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Legacy 2020-22|All[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru B connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Legacy 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Legacy 2020-22|All[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru B connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Legacy 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Outback 2015-17|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2015-17">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Outback 2015-17|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2015-17">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Outback 2018-19|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Outback 2018-19|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Outback 2020-22|All[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru B connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Outback 2020-22|All[<sup>8</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru B connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2020-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>8</sup>](#footnotes)|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Subaru A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2020-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Škoda|Fabia 2022-23[<sup>15</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Fabia 2022-23">Buy Here</a></sub></details>[<sup>17</sup>](#footnotes)|||
|
|Škoda|Fabia 2022-23[<sup>15</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Fabia 2022-23">Buy Here</a></sub></details>[<sup>17</sup>](#footnotes)|||
|
||||||
|Škoda|Kamiq 2021-23[<sup>13,15</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Kamiq 2021-23">Buy Here</a></sub></details>[<sup>17</sup>](#footnotes)|||
|
|Škoda|Kamiq 2021-23[<sup>13,15</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Kamiq 2021-23">Buy Here</a></sub></details>[<sup>17</sup>](#footnotes)|||
|
||||||
|Škoda|Karoq 2019-23[<sup>15</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Karoq 2019-23">Buy Here</a></sub></details>|||
|
|Škoda|Karoq 2019-23[<sup>15</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Karoq 2019-23">Buy Here</a></sub></details>|||
|
||||||
@@ -308,7 +311,6 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Toyota|RAV4 Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Toyota|RAV4 Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|RAV4 Hybrid 2023-25|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2023-25">Buy Here</a></sub></details>|<a href="https://youtu.be/4eIsEq4L4Ng" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Toyota|RAV4 Hybrid 2023-25|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2023-25">Buy Here</a></sub></details>|<a href="https://youtu.be/4eIsEq4L4Ng" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Sienna 2018-20|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Sienna 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Toyota|Sienna 2018-20|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Sienna 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Wildlander PHEV 2021|All|openpilot|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Wildlander PHEV 2021">Buy Here</a></sub></details>|||
|
|
||||||
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon R 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon R 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# CarState signals
|
||||||
|
|
||||||
|
## Required for basic lateral control
|
||||||
|
|
||||||
|
* `brakePressed`
|
||||||
|
* `cruiseState`
|
||||||
|
* `doorOpen`
|
||||||
|
* `espDisabled`
|
||||||
|
* `gasPressed`
|
||||||
|
* `gearShifter`
|
||||||
|
* `leftBlinker` / `rightBlinker`
|
||||||
|
* `seatbeltUnlatched`
|
||||||
|
* `standstill`
|
||||||
|
* `steeringAngleDeg`
|
||||||
|
* `steeringPressed`
|
||||||
|
* `steeringTorque`
|
||||||
|
* `steerFaultPermanent`
|
||||||
|
* `steerFaultTemporary`
|
||||||
|
* `vCruise`
|
||||||
|
* `wheelSpeeds.[fl|fr|rl|rr]`: Speed of each of the car's four wheels, in m/s. The car's CAN bus often broadcasts the
|
||||||
|
speed in kph, so the helper function `parse_wheel_speeds` performs this conversion by default.
|
||||||
|
|
||||||
|
## Recommended / Required for openpilot longitudinal control
|
||||||
|
|
||||||
|
* `accFaulted`
|
||||||
|
* `espActive`
|
||||||
|
* `parkingBrake`
|
||||||
|
|
||||||
|
## Application Dependent
|
||||||
|
|
||||||
|
* `blockPcmEnable`
|
||||||
|
* `buttonEnable`
|
||||||
|
* `brakeHoldActive`
|
||||||
|
* `carFaultedNonCritical`
|
||||||
|
* `invalidLkasSetting`
|
||||||
|
* `lowSpeedAlert`
|
||||||
|
* `regenBraking`
|
||||||
|
* `steeringAngleOffsetDeg`
|
||||||
|
* `steeringDisengage`
|
||||||
|
* `steeringTorqueEps`
|
||||||
|
* `stockLkas`
|
||||||
|
* `vCruiseCluster`
|
||||||
|
* `vEgoCluster`
|
||||||
|
* `vehicleSensorsInvalid`
|
||||||
|
|
||||||
|
## Automatically populated
|
||||||
|
|
||||||
|
* `buttonEvents`
|
||||||
|
|
||||||
|
These values are populated automatically by `parse_wheel_speeds`:
|
||||||
|
|
||||||
|
* `aEgo`: Acceleration of the ego vehicle, Kalman filtered derivative of `vEgo`.
|
||||||
|
* `vEgo`: Speed of the ego vehicle, Kalman filtered from `vEgoRaw`.
|
||||||
|
* `vEgoRaw`: Speed of the ego vehicle, based on the average of all four wheel speeds, unfiltered.
|
||||||
|
|
||||||
|
## Optional
|
||||||
|
|
||||||
|
* `brake`
|
||||||
|
* `charging`
|
||||||
|
* `fuelGauge`
|
||||||
|
* `leftBlindspot` / `rightBlindspot`
|
||||||
|
* `steeringRateDeg`
|
||||||
|
* `stockAeb`
|
||||||
|
* `stockFcw`
|
||||||
|
* `yawRate`
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# Stimulus-Response Tests
|
||||||
|
|
||||||
|
These are example test drives that can help identify the CAN bus messaging necessary for ADAS control. Each scripted
|
||||||
|
test should be done in a separate route (ignition cycle). These tests are a guide, not necessarily exhaustive.
|
||||||
|
|
||||||
|
While testing, constant power to the comma device is highly recommended, using [comma power](https://comma.ai/shop/comma-power) if
|
||||||
|
necessary to make sure all test activity is fully captured and for ease of uploading. If constant power isn't
|
||||||
|
available, keep the ignition on for at least one minute after your test to make sure power loss doesn't result
|
||||||
|
in loss of the last minute of testing data.
|
||||||
|
|
||||||
|
## Stationary ignition-only tests, part 1
|
||||||
|
|
||||||
|
1. Ignition on, but don't start engine, remain in Park
|
||||||
|
2. Open and close each door in a defined order: driver, passenger, rear left, rear right
|
||||||
|
3. Re-enter the vehicle, close the driver's door, and fasten the driver's seatbelt
|
||||||
|
4. Slowly press and release the accelerator pedal 3 times
|
||||||
|
5. Slowly press and release the brake pedal 3 times
|
||||||
|
6. Hold the brake and move the gearshift to reverse, then neutral, then drive, then sport/eco/etc if applicable
|
||||||
|
7. Return to Park, ignition off
|
||||||
|
|
||||||
|
Brake-pressed information may show up in several messages and signals, both as on/off states and as a percentage or
|
||||||
|
pressure. It may reflect a switch on the driver's brake pedal, or a pressure-threshold state, or signals to turn on
|
||||||
|
the rear brake lights. Start by identifying all the potential signals, and confirm while driving with ACC later.
|
||||||
|
|
||||||
|
Locate signals for all four door states if possible, but some cars only expose the driver's door state on the ADAS bus.
|
||||||
|
Driver/passenger door signals may or may not change positions for LHD vs RHD cars. For cars where only the driver's
|
||||||
|
door signal is available, the same signal may follow the driver.
|
||||||
|
|
||||||
|
## Stationary ignition-only tests, part 2
|
||||||
|
|
||||||
|
1. Ignition on, but don't start engine, remain in Park
|
||||||
|
2. Press each ACC button in a defined order: main switch on/off, set, resume, cancel, accel, decel, gap adjust
|
||||||
|
3. Set the left turn signal for about five seconds
|
||||||
|
4. Operate the left turn signal one time in its touch-to-pass mode
|
||||||
|
5. Set the right turn signal for about five seconds
|
||||||
|
6. Operate the right turn signal one time in its touch-to-pass mode
|
||||||
|
7. Set the hazard / emergency indicator switch for about five seconds
|
||||||
|
8. Ignition off
|
||||||
|
|
||||||
|
Your vehicle may have a momentary-press main ACC switch or a physical toggle that remains set. Actual ACC engagement
|
||||||
|
isn't necessary for purposes of detecting the ACC button presses.
|
||||||
|
|
||||||
|
## Steering angle and steering torque tests
|
||||||
|
|
||||||
|
Power steering should be available. On ICE cars, engine RPM may be present.
|
||||||
|
|
||||||
|
1. Ignition on, start engine if applicable, remain in Park
|
||||||
|
2. Rotate the steering wheel as follows, with a few seconds pause between each step
|
||||||
|
* Start as close to exact center as possible
|
||||||
|
* Turn to 45 degrees right and hold
|
||||||
|
* Turn to 90 degrees right and hold
|
||||||
|
* Turn to 180 degrees right and hold
|
||||||
|
* Turn to full lock right and hold, with firm pressure against lock
|
||||||
|
* Release the wheel and allow it to bounce back slightly from lock
|
||||||
|
* Turn to 180 degrees left and hold
|
||||||
|
* Return to center and release
|
||||||
|
3. Ignition off
|
||||||
|
|
||||||
|
Performing the full test to the right, followed by an abbreviated test to the left, helps give additional confirmation
|
||||||
|
of signal scale, and sign/direction for both the steering wheel angle and driver input torque signals.
|
||||||
|
|
||||||
|
## Low speed / parking lot driving tests
|
||||||
|
|
||||||
|
Before this test, drive to a place like an empty parking lot where you are free to drive in a series of curves.
|
||||||
|
|
||||||
|
1. Ignition on, start engine if applicable, prepare to drive
|
||||||
|
2. Slowly (10-20mph at most) drive a figure-8 if possible, or at least one sharp left and one sharp right.
|
||||||
|
3. Come to a complete stop
|
||||||
|
4. When and where safe, drive in reverse for a short distance (10-15 feet)
|
||||||
|
5. Park the car in a safe place, ignition off
|
||||||
|
|
||||||
|
## High speed / highway driving tests
|
||||||
|
|
||||||
|
Select a place and time where you can safely set cruise control at normal travel speeds with little interference from
|
||||||
|
traffic ahead, and safely test the response of your factory lane guidance system.
|
||||||
|
|
||||||
|
1. Ignition on, start engine if applicable, prepare to drive
|
||||||
|
2. When safely able, engage adaptive cruise control below 50 mph
|
||||||
|
3. When safely able, use the ACC buttons to accelerate to 50mph, then 55mph, then 60mph
|
||||||
|
4. Disengage adaptive cruise
|
||||||
|
5. When safely able, allow your factory lane guidance to prevent lane departures, 2-3 times on both the left and right
|
||||||
|
|
||||||
|
The series of setpoints can be adjusted to local traffic regulations, and of course metric units. The specific cruise
|
||||||
|
setpoints are useful for locating the ACC HUD signals later, and confirming their precise scaling. When the car reaches
|
||||||
|
and holds the setpoint, that can also provide additional confirmation of wheel speed scaling.
|
||||||
+10
-1
@@ -6,8 +6,17 @@ export NUMEXPR_NUM_THREADS=1
|
|||||||
export OPENBLAS_NUM_THREADS=1
|
export OPENBLAS_NUM_THREADS=1
|
||||||
export VECLIB_MAXIMUM_THREADS=1
|
export VECLIB_MAXIMUM_THREADS=1
|
||||||
|
|
||||||
|
# models get lower priority than ui
|
||||||
|
# - ui is ~5ms
|
||||||
|
# - modeld is 20ms
|
||||||
|
# - DM is 10ms
|
||||||
|
# in order to run ui at 60fps (16.67ms), we need to allow
|
||||||
|
# it to preempt the model workloads. we have enough
|
||||||
|
# headroom for this until ui is moved to the CPU.
|
||||||
|
export QCOM_PRIORITY=12
|
||||||
|
|
||||||
if [ -z "$AGNOS_VERSION" ]; then
|
if [ -z "$AGNOS_VERSION" ]; then
|
||||||
export AGNOS_VERSION="13.1"
|
export AGNOS_VERSION="15"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export STAGING_ROOT="/data/safe_staging"
|
export STAGING_ROOT="/data/safe_staging"
|
||||||
|
|||||||
+1
-1
Submodule opendbc_repo updated: b8a00bddda...b054629f5e
+1
-1
Submodule panda updated: 69ab12ee2a...dee9061b2a
+10
-5
@@ -23,7 +23,7 @@ dependencies = [
|
|||||||
# core
|
# core
|
||||||
"cffi",
|
"cffi",
|
||||||
"scons",
|
"scons",
|
||||||
"pycapnp",
|
"pycapnp==2.1.0",
|
||||||
"Cython",
|
"Cython",
|
||||||
"setuptools",
|
"setuptools",
|
||||||
"numpy >=2.0",
|
"numpy >=2.0",
|
||||||
@@ -72,7 +72,9 @@ dependencies = [
|
|||||||
"zstandard",
|
"zstandard",
|
||||||
|
|
||||||
# ui
|
# ui
|
||||||
|
"raylib < 5.5.0.3", # TODO: unpin when they fix https://github.com/electronstudio/raylib-python-cffi/issues/186
|
||||||
"qrcode",
|
"qrcode",
|
||||||
|
"mapbox-earcut",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
@@ -119,7 +121,6 @@ dev = [
|
|||||||
"tabulate",
|
"tabulate",
|
||||||
"types-requests",
|
"types-requests",
|
||||||
"types-tabulate",
|
"types-tabulate",
|
||||||
"raylib",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
tools = [
|
tools = [
|
||||||
@@ -177,7 +178,7 @@ quiet-level = 3
|
|||||||
# if you've got a short variable name that's getting flagged, add it here
|
# if you've got a short variable name that's getting flagged, add it here
|
||||||
ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite"
|
ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite"
|
||||||
builtin = "clear,rare,informal,code,names,en-GB_to_en-US"
|
builtin = "clear,rare,informal,code,names,en-GB_to_en-US"
|
||||||
skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.ts, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*"
|
skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.po, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*"
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
python_version = "3.11"
|
python_version = "3.11"
|
||||||
@@ -235,7 +236,6 @@ lint.ignore = [
|
|||||||
"B027",
|
"B027",
|
||||||
"B024",
|
"B024",
|
||||||
"NPY002", # new numpy random syntax is worse
|
"NPY002", # new numpy random syntax is worse
|
||||||
"UP038", # (x, y) -> x|y for isinstance
|
|
||||||
]
|
]
|
||||||
line-length = 160
|
line-length = 160
|
||||||
target-version ="py311"
|
target-version ="py311"
|
||||||
@@ -263,8 +263,13 @@ lint.flake8-implicit-str-concat.allow-multiline = false
|
|||||||
"tools".msg = "Use openpilot.tools"
|
"tools".msg = "Use openpilot.tools"
|
||||||
"pytest.main".msg = "pytest.main requires special handling that is easy to mess up!"
|
"pytest.main".msg = "pytest.main requires special handling that is easy to mess up!"
|
||||||
"unittest".msg = "Use pytest"
|
"unittest".msg = "Use pytest"
|
||||||
"pyray.measure_text_ex".msg = "Use openpilot.system.ui.lib.text_measure"
|
|
||||||
"time.time".msg = "Use time.monotonic"
|
"time.time".msg = "Use time.monotonic"
|
||||||
|
|
||||||
|
# raylib banned APIs
|
||||||
|
"pyray.measure_text_ex".msg = "Use openpilot.system.ui.lib.text_measure"
|
||||||
|
"pyray.is_mouse_button_pressed".msg = "This can miss events. Use Widget._handle_mouse_press"
|
||||||
|
"pyray.is_mouse_button_released".msg = "This can miss events. Use Widget._handle_mouse_release"
|
||||||
|
"pyray.draw_text".msg = "Use a function (such as rl.draw_font_ex) that takes font as an argument"
|
||||||
|
|
||||||
[tool.ruff.format]
|
[tool.ruff.format]
|
||||||
quote-style = "preserve"
|
quote-style = "preserve"
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ cd $BUILD_DIR
|
|||||||
rm -f panda/board/obj/panda.bin.signed
|
rm -f panda/board/obj/panda.bin.signed
|
||||||
rm -f panda/board/obj/panda_h7.bin.signed
|
rm -f panda/board/obj/panda_h7.bin.signed
|
||||||
|
|
||||||
VERSION=$(cat common/version.h | awk -F[\"-] '{print $2}')
|
VERSION=$(cat sunnypilot/common/version.h | awk -F[\"-] '{print $2}')
|
||||||
echo "[-] committing version $VERSION T=$SECONDS"
|
echo "[-] committing version $VERSION T=$SECONDS"
|
||||||
git add -f .
|
git add -f .
|
||||||
git commit -a -m "openpilot v$VERSION release"
|
git commit -a -m "openpilot v$VERSION release"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ rm -f panda/board/obj/panda.bin.signed
|
|||||||
GIT_HASH=$(git --git-dir=$SOURCE_DIR/.git rev-parse HEAD)
|
GIT_HASH=$(git --git-dir=$SOURCE_DIR/.git rev-parse HEAD)
|
||||||
GIT_COMMIT_DATE=$(git --git-dir=$SOURCE_DIR/.git show --no-patch --format='%ct %ci' HEAD)
|
GIT_COMMIT_DATE=$(git --git-dir=$SOURCE_DIR/.git show --no-patch --format='%ct %ci' HEAD)
|
||||||
DATETIME=$(date '+%Y-%m-%dT%H:%M:%S')
|
DATETIME=$(date '+%Y-%m-%dT%H:%M:%S')
|
||||||
VERSION=$(cat $SOURCE_DIR/common/version.h | awk -F\" '{print $2}')
|
VERSION=$(cat $SOURCE_DIR/sunnypilot/common/version.h | awk -F\" '{print $2}')
|
||||||
|
|
||||||
echo -n "$GIT_HASH" > git_src_commit
|
echo -n "$GIT_HASH" > git_src_commit
|
||||||
echo -n "$GIT_COMMIT_DATE" > git_src_commit_date
|
echo -n "$GIT_COMMIT_DATE" > git_src_commit_date
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ if [ -z "$GIT_ORIGIN" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# "Tagging"
|
# "Tagging"
|
||||||
echo "#define COMMA_VERSION \"$VERSION\"" > ${OUTPUT_DIR}/common/version.h
|
echo "#define SUNNYPILOT_VERSION \"$VERSION\"" > ${OUTPUT_DIR}/sunnypilot/common/version.h
|
||||||
|
|
||||||
## set git identity
|
## set git identity
|
||||||
#source $DIR/identity.sh
|
#source $DIR/identity.sh
|
||||||
@@ -55,7 +55,7 @@ git add -f .
|
|||||||
# include source commit hash and build date in commit
|
# include source commit hash and build date in commit
|
||||||
GIT_HASH=$(git --git-dir=$SOURCE_DIR/.git rev-parse HEAD)
|
GIT_HASH=$(git --git-dir=$SOURCE_DIR/.git rev-parse HEAD)
|
||||||
DATETIME=$(date '+%Y-%m-%dT%H:%M:%S')
|
DATETIME=$(date '+%Y-%m-%dT%H:%M:%S')
|
||||||
SP_VERSION=$(awk -F\" '{print $2}' $SOURCE_DIR/common/version.h)
|
SP_VERSION=$(awk -F\" '{print $2}' $SOURCE_DIR/sunnypilot/common/version.h)
|
||||||
|
|
||||||
# Commit with detailed message
|
# Commit with detailed message
|
||||||
git commit -a -m "sunnypilot v$VERSION
|
git commit -a -m "sunnypilot v$VERSION
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ from openpilot.common.basedir import BASEDIR
|
|||||||
|
|
||||||
|
|
||||||
DIRS = ['cereal', 'openpilot']
|
DIRS = ['cereal', 'openpilot']
|
||||||
EXTS = ['.png', '.py', '.ttf', '.capnp']
|
EXTS = ['.png', '.py', '.ttf', '.capnp', '.json', '.fnt', '.mo']
|
||||||
INTERPRETER = '/usr/bin/env python3'
|
INTERPRETER = '/usr/bin/env python3'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
*.cc
|
*.cc
|
||||||
|
fonts/*.fnt
|
||||||
|
fonts/*.png
|
||||||
translations_assets.qrc
|
translations_assets.qrc
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:93cdc4ee9aa40e2afceecc63da0ca05ec7aab4bec991ece51a6b52389f48a477
|
||||||
|
size 10788068
|
||||||
Executable
+128
@@ -0,0 +1,128 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from pathlib import Path
|
||||||
|
import json
|
||||||
|
|
||||||
|
import pyray as rl
|
||||||
|
|
||||||
|
FONT_DIR = Path(__file__).resolve().parent
|
||||||
|
SELFDRIVE_DIR = FONT_DIR.parents[1]
|
||||||
|
TRANSLATIONS_DIR = SELFDRIVE_DIR / "ui" / "translations"
|
||||||
|
LANGUAGES_FILE = TRANSLATIONS_DIR / "languages.json"
|
||||||
|
|
||||||
|
GLYPH_PADDING = 6
|
||||||
|
EXTRA_CHARS = "–‑✓×°§•€£¥"
|
||||||
|
UNIFONT_LANGUAGES = {"ar", "th", "zh-CHT", "zh-CHS", "ko", "ja"}
|
||||||
|
|
||||||
|
|
||||||
|
def _languages():
|
||||||
|
if not LANGUAGES_FILE.exists():
|
||||||
|
return {}
|
||||||
|
with LANGUAGES_FILE.open(encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def _char_sets():
|
||||||
|
base = set(map(chr, range(32, 127))) | set(EXTRA_CHARS)
|
||||||
|
unifont = set(base)
|
||||||
|
|
||||||
|
for language, code in _languages().items():
|
||||||
|
unifont.update(language)
|
||||||
|
po_path = TRANSLATIONS_DIR / f"app_{code}.po"
|
||||||
|
try:
|
||||||
|
chars = set(po_path.read_text(encoding="utf-8"))
|
||||||
|
except FileNotFoundError:
|
||||||
|
continue
|
||||||
|
(unifont if code in UNIFONT_LANGUAGES else base).update(chars)
|
||||||
|
|
||||||
|
return tuple(sorted(ord(c) for c in base)), tuple(sorted(ord(c) for c in unifont))
|
||||||
|
|
||||||
|
|
||||||
|
def _glyph_metrics(glyphs, rects, codepoints):
|
||||||
|
entries = []
|
||||||
|
min_offset_y, max_extent = None, 0
|
||||||
|
for idx, codepoint in enumerate(codepoints):
|
||||||
|
glyph = glyphs[idx]
|
||||||
|
rect = rects[idx]
|
||||||
|
width = int(round(rect.width))
|
||||||
|
height = int(round(rect.height))
|
||||||
|
offset_y = int(round(glyph.offsetY))
|
||||||
|
min_offset_y = offset_y if min_offset_y is None else min(min_offset_y, offset_y)
|
||||||
|
max_extent = max(max_extent, offset_y + height)
|
||||||
|
entries.append({
|
||||||
|
"id": codepoint,
|
||||||
|
"x": int(round(rect.x)),
|
||||||
|
"y": int(round(rect.y)),
|
||||||
|
"width": width,
|
||||||
|
"height": height,
|
||||||
|
"xoffset": int(round(glyph.offsetX)),
|
||||||
|
"yoffset": offset_y,
|
||||||
|
"xadvance": int(round(glyph.advanceX)),
|
||||||
|
})
|
||||||
|
|
||||||
|
if min_offset_y is None:
|
||||||
|
raise RuntimeError("No glyphs were generated")
|
||||||
|
|
||||||
|
line_height = int(round(max_extent - min_offset_y))
|
||||||
|
base = int(round(max_extent))
|
||||||
|
return entries, line_height, base
|
||||||
|
|
||||||
|
|
||||||
|
def _write_bmfont(path: Path, font_size: int, face: str, atlas_name: str, line_height: int, base: int, atlas_size, entries):
|
||||||
|
lines = [
|
||||||
|
f"info face=\"{face}\" size=-{font_size} bold=0 italic=0 charset=\"\" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0",
|
||||||
|
f"common lineHeight={line_height} base={base} scaleW={atlas_size[0]} scaleH={atlas_size[1]} pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4",
|
||||||
|
f"page id=0 file=\"{atlas_name}\"",
|
||||||
|
f"chars count={len(entries)}",
|
||||||
|
]
|
||||||
|
for entry in entries:
|
||||||
|
lines.append(
|
||||||
|
("char id={id:<4} x={x:<5} y={y:<5} width={width:<5} height={height:<5} " +
|
||||||
|
"xoffset={xoffset:<5} yoffset={yoffset:<5} xadvance={xadvance:<5} page=0 chnl=15").format(**entry)
|
||||||
|
)
|
||||||
|
path.write_text("\n".join(lines) + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
def _process_font(font_path: Path, codepoints: tuple[int, ...]):
|
||||||
|
print(f"Processing {font_path.name}...")
|
||||||
|
|
||||||
|
font_size = {
|
||||||
|
"unifont.otf": 16, # unifont is only 16x8 or 16x16 pixels per glyph
|
||||||
|
}.get(font_path.name, 200)
|
||||||
|
|
||||||
|
data = font_path.read_bytes()
|
||||||
|
file_buf = rl.ffi.new("unsigned char[]", data)
|
||||||
|
cp_buffer = rl.ffi.new("int[]", codepoints)
|
||||||
|
cp_ptr = rl.ffi.cast("int *", cp_buffer)
|
||||||
|
glyphs = rl.load_font_data(rl.ffi.cast("unsigned char *", file_buf), len(data), font_size, cp_ptr, len(codepoints), rl.FontType.FONT_DEFAULT)
|
||||||
|
if glyphs == rl.ffi.NULL:
|
||||||
|
raise RuntimeError("raylib failed to load font data")
|
||||||
|
|
||||||
|
rects_ptr = rl.ffi.new("Rectangle **")
|
||||||
|
image = rl.gen_image_font_atlas(glyphs, rects_ptr, len(codepoints), font_size, GLYPH_PADDING, 0)
|
||||||
|
if image.width == 0 or image.height == 0:
|
||||||
|
raise RuntimeError("raylib returned an empty atlas")
|
||||||
|
|
||||||
|
rects = rects_ptr[0]
|
||||||
|
atlas_name = f"{font_path.stem}.png"
|
||||||
|
atlas_path = FONT_DIR / atlas_name
|
||||||
|
entries, line_height, base = _glyph_metrics(glyphs, rects, codepoints)
|
||||||
|
|
||||||
|
if not rl.export_image(image, atlas_path.as_posix()):
|
||||||
|
raise RuntimeError("Failed to export atlas image")
|
||||||
|
|
||||||
|
_write_bmfont(FONT_DIR / f"{font_path.stem}.fnt", font_size, font_path.stem, atlas_name, line_height, base, (image.width, image.height), entries)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
base_cp, unifont_cp = _char_sets()
|
||||||
|
fonts = sorted(FONT_DIR.glob("*.ttf")) + sorted(FONT_DIR.glob("*.otf"))
|
||||||
|
for font in fonts:
|
||||||
|
if "emoji" in font.name.lower():
|
||||||
|
continue
|
||||||
|
glyphs = unifont_cp if font.stem.lower().startswith("unifont") else base_cp
|
||||||
|
_process_font(font, glyphs)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:9712a9bc089af7ddc06e0826aa84f2ee23ed2f1a1dddaf2a89c2483e753a8475
|
||||||
|
size 5321484
|
||||||
@@ -88,6 +88,7 @@ class Car:
|
|||||||
self.can_callbacks = can_comm_callbacks(self.can_sock, self.pm.sock['sendcan'])
|
self.can_callbacks = can_comm_callbacks(self.can_sock, self.pm.sock['sendcan'])
|
||||||
|
|
||||||
is_release = self.params.get_bool("IsReleaseBranch")
|
is_release = self.params.get_bool("IsReleaseBranch")
|
||||||
|
is_release_sp = self.params.get_bool("IsReleaseSpBranch")
|
||||||
|
|
||||||
if CI is None:
|
if CI is None:
|
||||||
# wait for one pandaState and one CAN packet
|
# wait for one pandaState and one CAN packet
|
||||||
@@ -110,7 +111,7 @@ class Car:
|
|||||||
init_params_list_sp = sunnypilot_interfaces.initialize_params(self.params)
|
init_params_list_sp = sunnypilot_interfaces.initialize_params(self.params)
|
||||||
|
|
||||||
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, num_pandas, cached_params,
|
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, num_pandas, cached_params,
|
||||||
fixed_fingerprint, init_params_list_sp)
|
fixed_fingerprint, init_params_list_sp, is_release_sp)
|
||||||
sunnypilot_interfaces.setup_interfaces(self.CI, self.params)
|
sunnypilot_interfaces.setup_interfaces(self.CI, self.params)
|
||||||
self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP)
|
self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP)
|
||||||
self.CP = self.CI.CP
|
self.CP = self.CI.CP
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ class TestCarInterfaces:
|
|||||||
# hypothesis also slows down significantly with just one more message draw
|
# hypothesis also slows down significantly with just one more message draw
|
||||||
LongControl(car_params, car_params_sp)
|
LongControl(car_params, car_params_sp)
|
||||||
if car_params.steerControlType == CarParams.SteerControlType.angle:
|
if car_params.steerControlType == CarParams.SteerControlType.angle:
|
||||||
LatControlAngle(car_params, car_params_sp, car_interface)
|
LatControlAngle(car_params, car_params_sp, car_interface, DT_CTRL)
|
||||||
elif car_params.lateralTuning.which() == 'pid':
|
elif car_params.lateralTuning.which() == 'pid':
|
||||||
LatControlPID(car_params, car_params_sp, car_interface)
|
LatControlPID(car_params, car_params_sp, car_interface, DT_CTRL)
|
||||||
elif car_params.lateralTuning.which() == 'torque':
|
elif car_params.lateralTuning.which() == 'torque':
|
||||||
LatControlTorque(car_params, car_params_sp, car_interface)
|
LatControlTorque(car_params, car_params_sp, car_interface, DT_CTRL)
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ class TestCarModelBase(unittest.TestCase):
|
|||||||
|
|
||||||
cls.CarInterface = interfaces[cls.platform]
|
cls.CarInterface = interfaces[cls.platform]
|
||||||
cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, alpha_long, False, docs=False)
|
cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, alpha_long, False, docs=False)
|
||||||
cls.CP_SP = cls.CarInterface.get_params_sp(cls.CP, cls.platform, cls.fingerprint, car_fw, alpha_long, docs=False)
|
cls.CP_SP = cls.CarInterface.get_params_sp(cls.CP, cls.platform, cls.fingerprint, car_fw, alpha_long, False, docs=False)
|
||||||
assert cls.CP
|
assert cls.CP
|
||||||
assert cls.CP_SP
|
assert cls.CP_SP
|
||||||
assert cls.CP.carFingerprint == cls.platform
|
assert cls.CP.carFingerprint == cls.platform
|
||||||
@@ -189,7 +189,7 @@ class TestCarModelBase(unittest.TestCase):
|
|||||||
if tuning == 'pid':
|
if tuning == 'pid':
|
||||||
self.assertTrue(len(self.CP.lateralTuning.pid.kpV))
|
self.assertTrue(len(self.CP.lateralTuning.pid.kpV))
|
||||||
elif tuning == 'torque':
|
elif tuning == 'torque':
|
||||||
self.assertTrue(self.CP.lateralTuning.torque.kf > 0)
|
self.assertTrue(self.CP.lateralTuning.torque.latAccelFactor > 0)
|
||||||
else:
|
else:
|
||||||
raise Exception("unknown tuning")
|
raise Exception("unknown tuning")
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from cereal import car, log
|
|||||||
import cereal.messaging as messaging
|
import cereal.messaging as messaging
|
||||||
from openpilot.common.constants import CV
|
from openpilot.common.constants import CV
|
||||||
from openpilot.common.params import Params
|
from openpilot.common.params import Params
|
||||||
from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper
|
from openpilot.common.realtime import config_realtime_process, DT_CTRL, Priority, Ratekeeper
|
||||||
from openpilot.common.swaglog import cloudlog
|
from openpilot.common.swaglog import cloudlog
|
||||||
|
|
||||||
from opendbc.car.car_helpers import interfaces
|
from opendbc.car.car_helpers import interfaces
|
||||||
@@ -19,6 +19,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
|
|||||||
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, STEER_ANGLE_SATURATION_THRESHOLD
|
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, STEER_ANGLE_SATURATION_THRESHOLD
|
||||||
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
||||||
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
|
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
|
||||||
|
from openpilot.selfdrive.modeld.modeld import LAT_SMOOTH_SECONDS
|
||||||
from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose
|
from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose
|
||||||
|
|
||||||
from openpilot.sunnypilot.livedelay.helpers import get_lat_delay
|
from openpilot.sunnypilot.livedelay.helpers import get_lat_delay
|
||||||
@@ -45,7 +46,7 @@ class Controls(ControlsExt, ModelStateBase):
|
|||||||
|
|
||||||
self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP)
|
self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP)
|
||||||
|
|
||||||
self.sm = messaging.SubMaster(['liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState',
|
self.sm = messaging.SubMaster(['liveDelay', 'liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState',
|
||||||
'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput',
|
'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput',
|
||||||
'driverMonitoringState', 'onroadEvents', 'driverAssistance', 'liveDelay'] + self.sm_services_ext,
|
'driverMonitoringState', 'onroadEvents', 'driverAssistance', 'liveDelay'] + self.sm_services_ext,
|
||||||
poll='selfdriveState')
|
poll='selfdriveState')
|
||||||
@@ -62,11 +63,11 @@ class Controls(ControlsExt, ModelStateBase):
|
|||||||
self.VM = VehicleModel(self.CP)
|
self.VM = VehicleModel(self.CP)
|
||||||
self.LaC: LatControl
|
self.LaC: LatControl
|
||||||
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
|
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
|
||||||
self.LaC = LatControlAngle(self.CP, self.CP_SP, self.CI)
|
self.LaC = LatControlAngle(self.CP, self.CP_SP, self.CI, DT_CTRL)
|
||||||
elif self.CP.lateralTuning.which() == 'pid':
|
elif self.CP.lateralTuning.which() == 'pid':
|
||||||
self.LaC = LatControlPID(self.CP, self.CP_SP, self.CI)
|
self.LaC = LatControlPID(self.CP, self.CP_SP, self.CI, DT_CTRL)
|
||||||
elif self.CP.lateralTuning.which() == 'torque':
|
elif self.CP.lateralTuning.which() == 'torque':
|
||||||
self.LaC = LatControlTorque(self.CP, self.CP_SP, self.CI)
|
self.LaC = LatControlTorque(self.CP, self.CP_SP, self.CI, DT_CTRL)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.sm.update(15)
|
self.sm.update(15)
|
||||||
@@ -99,7 +100,6 @@ class Controls(ControlsExt, ModelStateBase):
|
|||||||
|
|
||||||
self.LaC.extension.update_model_v2(self.sm['modelV2'])
|
self.LaC.extension.update_model_v2(self.sm['modelV2'])
|
||||||
|
|
||||||
self.lat_delay = get_lat_delay(self.params, self.sm["liveDelay"].lateralDelay)
|
|
||||||
self.LaC.extension.update_lateral_lag(self.lat_delay)
|
self.LaC.extension.update_lateral_lag(self.lat_delay)
|
||||||
|
|
||||||
long_plan = self.sm['longitudinalPlan']
|
long_plan = self.sm['longitudinalPlan']
|
||||||
@@ -133,18 +133,19 @@ class Controls(ControlsExt, ModelStateBase):
|
|||||||
self.LoC.reset()
|
self.LoC.reset()
|
||||||
|
|
||||||
# accel PID loop
|
# accel PID loop
|
||||||
pid_accel_limits = self.CI.get_pid_accel_limits(self.CP, CS.vEgo, CS.vCruise * CV.KPH_TO_MS)
|
pid_accel_limits = self.CI.get_pid_accel_limits(self.CP, self.CP_SP, CS.vEgo, CS.vCruise * CV.KPH_TO_MS)
|
||||||
actuators.accel = float(self.LoC.update(CC.longActive, CS, long_plan.aTarget, long_plan.shouldStop, pid_accel_limits))
|
actuators.accel = float(self.LoC.update(CC.longActive, CS, long_plan.aTarget, long_plan.shouldStop, pid_accel_limits))
|
||||||
|
|
||||||
# Steering PID loop and lateral MPC
|
# Steering PID loop and lateral MPC
|
||||||
# Reset desired curvature to current to avoid violating the limits on engage
|
# 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
|
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)
|
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
|
actuators.curvature = self.desired_curvature
|
||||||
steer, steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp,
|
steer, steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp,
|
||||||
self.steer_limited_by_safety, self.desired_curvature,
|
self.steer_limited_by_safety, self.desired_curvature,
|
||||||
self.calibrated_pose, curvature_limited) # TODO what if not available
|
self.calibrated_pose, curvature_limited, lat_delay)
|
||||||
actuators.torque = float(steer)
|
actuators.torque = float(steer)
|
||||||
actuators.steeringAngleDeg = float(steeringAngleDeg)
|
actuators.steeringAngleDeg = float(steeringAngleDeg)
|
||||||
# Ensure no NaNs/Infs
|
# Ensure no NaNs/Infs
|
||||||
@@ -234,6 +235,9 @@ class Controls(ControlsExt, ModelStateBase):
|
|||||||
while not evt.is_set():
|
while not evt.is_set():
|
||||||
self.get_params_sp()
|
self.get_params_sp()
|
||||||
|
|
||||||
|
if self.CP.lateralTuning.which() == 'torque':
|
||||||
|
self.lat_delay = get_lat_delay(self.params, self.sm["liveDelay"].lateralDelay)
|
||||||
|
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from openpilot.common.constants import CV
|
|||||||
from openpilot.common.realtime import DT_MDL
|
from openpilot.common.realtime import DT_MDL
|
||||||
from openpilot.sunnypilot.selfdrive.controls.lib.auto_lane_change import AutoLaneChangeController, AutoLaneChangeMode
|
from openpilot.sunnypilot.selfdrive.controls.lib.auto_lane_change import AutoLaneChangeController, AutoLaneChangeMode
|
||||||
from openpilot.sunnypilot.selfdrive.controls.lib.lane_turn_desire import LaneTurnController
|
from openpilot.sunnypilot.selfdrive.controls.lib.lane_turn_desire import LaneTurnController
|
||||||
|
from openpilot.sunnypilot.navd.navigation_desires.navigation_desires import NavigationDesires
|
||||||
|
|
||||||
LaneChangeState = log.LaneChangeState
|
LaneChangeState = log.LaneChangeState
|
||||||
LaneChangeDirection = log.LaneChangeDirection
|
LaneChangeDirection = log.LaneChangeDirection
|
||||||
@@ -51,6 +52,7 @@ class DesireHelper:
|
|||||||
self.alc = AutoLaneChangeController(self)
|
self.alc = AutoLaneChangeController(self)
|
||||||
self.lane_turn_controller = LaneTurnController(self)
|
self.lane_turn_controller = LaneTurnController(self)
|
||||||
self.lane_turn_direction = TurnDirection.none
|
self.lane_turn_direction = TurnDirection.none
|
||||||
|
self.navigation_desires = NavigationDesires()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_lane_change_direction(CS):
|
def get_lane_change_direction(CS):
|
||||||
@@ -143,3 +145,7 @@ class DesireHelper:
|
|||||||
self.desire = log.Desire.none
|
self.desire = log.Desire.none
|
||||||
|
|
||||||
self.alc.update_state()
|
self.alc.update_state()
|
||||||
|
|
||||||
|
nav_desire = self.navigation_desires.update(carstate, lateral_active)
|
||||||
|
if nav_desire != log.Desire.none and (self.desire == log.Desire.none or self.desire in (log.Desire.turnLeft, log.Desire.turnRight)):
|
||||||
|
self.desire = nav_desire
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ def smooth_value(val, prev_val, tau, dt=DT_MDL):
|
|||||||
alpha = 1 - np.exp(-dt/tau) if tau > 0 else 1
|
alpha = 1 - np.exp(-dt/tau) if tau > 0 else 1
|
||||||
return alpha * val + (1 - alpha) * prev_val
|
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
|
# This function respects ISO lateral jerk and acceleration limits + a max curvature
|
||||||
v_ego = max(v_ego, MIN_SPEED)
|
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
|
max_curvature_rate = MAX_LATERAL_JERK / (v_ego ** 2) # inexact calculation, check https://github.com/commaai/openpilot/pull/24755
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from abc import abstractmethod, ABC
|
from abc import abstractmethod, ABC
|
||||||
|
from openpilot.selfdrive.locationd.helpers import Pose
|
||||||
from openpilot.common.realtime import DT_CTRL
|
|
||||||
|
|
||||||
|
|
||||||
class LatControl(ABC):
|
class LatControl(ABC):
|
||||||
def __init__(self, CP, CP_SP, CI):
|
def __init__(self, CP, CP_SP, CI, dt):
|
||||||
self.sat_count_rate = 1.0 * DT_CTRL
|
self.dt = dt
|
||||||
self.sat_limit = CP.steerLimitTimer
|
self.sat_limit = CP.steerLimitTimer
|
||||||
self.sat_count = 0.
|
self.sat_time = 0.
|
||||||
self.sat_check_min_speed = 10.
|
self.sat_check_min_speed = 10.
|
||||||
|
|
||||||
# we define the steer torque scale as [-1.0...1.0]
|
# we define the steer torque scale as [-1.0...1.0]
|
||||||
self.steer_max = 1.0
|
self.steer_max = 1.0
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited):
|
def update(self, active: bool, CS, VM, params, steer_limited_by_safety: bool, desired_curvature: float, calibrated_pose: Pose,
|
||||||
|
curvature_limited: bool, lat_delay: float):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.sat_count = 0.
|
self.sat_time = 0.
|
||||||
|
|
||||||
def _check_saturation(self, saturated, CS, steer_limited_by_safety, curvature_limited):
|
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
|
# 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:
|
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:
|
else:
|
||||||
self.sat_count -= self.sat_count_rate
|
self.sat_time -= self.dt
|
||||||
self.sat_count = np.clip(self.sat_count, 0.0, self.sat_limit)
|
self.sat_time = np.clip(self.sat_time, 0.0, self.sat_limit)
|
||||||
return self.sat_count > (self.sat_limit - 1e-3)
|
return self.sat_time > (self.sat_limit - 1e-3)
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ STEER_ANGLE_SATURATION_THRESHOLD = 2.5 # Degrees
|
|||||||
|
|
||||||
|
|
||||||
class LatControlAngle(LatControl):
|
class LatControlAngle(LatControl):
|
||||||
def __init__(self, CP, CP_SP, CI):
|
def __init__(self, CP, CP_SP, CI, dt):
|
||||||
super().__init__(CP, CP_SP, CI)
|
super().__init__(CP, CP_SP, CI, dt)
|
||||||
self.sat_check_min_speed = 5.
|
self.sat_check_min_speed = 5.
|
||||||
self.use_steer_limited_by_safety = CP.brand == "tesla"
|
self.use_steer_limited_by_safety = CP.brand == "tesla"
|
||||||
|
|
||||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited):
|
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited, lat_delay):
|
||||||
angle_log = log.ControlsState.LateralAngleState.new_message()
|
angle_log = log.ControlsState.LateralAngleState.new_message()
|
||||||
|
|
||||||
if not active:
|
if not active:
|
||||||
|
|||||||
@@ -6,14 +6,15 @@ from openpilot.common.pid import PIDController
|
|||||||
|
|
||||||
|
|
||||||
class LatControlPID(LatControl):
|
class LatControlPID(LatControl):
|
||||||
def __init__(self, CP, CP_SP, CI):
|
def __init__(self, CP, CP_SP, CI, dt):
|
||||||
super().__init__(CP, CP_SP, CI)
|
super().__init__(CP, CP_SP, CI, dt)
|
||||||
self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
|
self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
|
||||||
(CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
|
(CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
|
||||||
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.get_steer_feedforward = CI.get_steer_feedforward_function()
|
||||||
|
|
||||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited):
|
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited, lat_delay):
|
||||||
pid_log = log.ControlsState.LateralPIDState.new_message()
|
pid_log = log.ControlsState.LateralPIDState.new_message()
|
||||||
pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
|
pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
|
||||||
pid_log.steeringRateDeg = float(CS.steeringRateDeg)
|
pid_log.steeringRateDeg = float(CS.steeringRateDeg)
|
||||||
@@ -30,7 +31,7 @@ class LatControlPID(LatControl):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# offset does not contribute to resistive torque
|
# offset does not contribute to resistive torque
|
||||||
ff = self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo)
|
ff = self.ff_factor * self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo)
|
||||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
||||||
|
|
||||||
output_torque = self.pid.update(error,
|
output_torque = self.pid.update(error,
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import math
|
import math
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
from cereal import log
|
from cereal import log
|
||||||
from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction
|
from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction
|
||||||
from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY
|
from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY
|
||||||
|
from openpilot.common.filter_simple import FirstOrderFilter
|
||||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
||||||
from openpilot.common.pid import PIDController
|
from openpilot.common.pid import PIDController
|
||||||
|
|
||||||
@@ -15,25 +17,34 @@ from openpilot.sunnypilot.selfdrive.controls.lib.latcontrol_torque_ext import La
|
|||||||
# wheel slip, or to speed.
|
# wheel slip, or to speed.
|
||||||
|
|
||||||
# This controller applies torque to achieve desired lateral
|
# This controller applies torque to achieve desired lateral
|
||||||
# accelerations. To compensate for the low speed effects we
|
# accelerations. To compensate for the low speed effects the
|
||||||
# use a LOW_SPEED_FACTOR in the error. Additionally, there is
|
# proportional gain is increased at low speeds by the PID controller.
|
||||||
# friction in the steering wheel that needs to be overcome to
|
# Additionally, there is friction in the steering wheel that needs
|
||||||
# move it at all, this is compensated for too.
|
# to be overcome to move it at all, this is compensated for too.
|
||||||
|
|
||||||
LOW_SPEED_X = [0, 10, 20, 30]
|
KP = 1.0
|
||||||
LOW_SPEED_Y = [15, 13, 10, 5]
|
KI = 0.3
|
||||||
|
KD = 0.0
|
||||||
|
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]
|
||||||
|
|
||||||
|
LP_FILTER_CUTOFF_HZ = 1.2
|
||||||
|
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
|
||||||
|
VERSION = 0
|
||||||
|
|
||||||
class LatControlTorque(LatControl):
|
class LatControlTorque(LatControl):
|
||||||
def __init__(self, CP, CP_SP, CI):
|
def __init__(self, CP, CP_SP, CI, dt):
|
||||||
super().__init__(CP, CP_SP, CI)
|
super().__init__(CP, CP_SP, CI, dt)
|
||||||
self.torque_params = CP.lateralTuning.torque.as_builder()
|
self.torque_params = CP.lateralTuning.torque.as_builder()
|
||||||
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
|
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
|
||||||
self.lateral_accel_from_torque = CI.lateral_accel_from_torque()
|
self.lateral_accel_from_torque = CI.lateral_accel_from_torque()
|
||||||
self.pid = PIDController(self.torque_params.kp, self.torque_params.ki,
|
self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, KD, rate=1/self.dt)
|
||||||
k_f=self.torque_params.kf)
|
|
||||||
self.update_limits()
|
self.update_limits()
|
||||||
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
|
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
|
||||||
|
self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt)
|
||||||
|
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
|
||||||
|
self.previous_measurement = 0.0
|
||||||
|
self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt)
|
||||||
|
|
||||||
self.extension = LatControlTorqueExt(self, CP, CP_SP, CI)
|
self.extension = LatControlTorqueExt(self, CP, CP_SP, CI)
|
||||||
|
|
||||||
@@ -47,57 +58,68 @@ class LatControlTorque(LatControl):
|
|||||||
self.pid.set_limits(self.lateral_accel_from_torque(self.steer_max, self.torque_params),
|
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))
|
self.lateral_accel_from_torque(-self.steer_max, self.torque_params))
|
||||||
|
|
||||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited):
|
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited, lat_delay):
|
||||||
# Override torque params from extension
|
# Override torque params from extension
|
||||||
if self.extension.update_override_torque_params(self.torque_params):
|
if self.extension.update_override_torque_params(self.torque_params):
|
||||||
self.update_limits()
|
self.update_limits()
|
||||||
|
|
||||||
pid_log = log.ControlsState.LateralTorqueState.new_message()
|
pid_log = log.ControlsState.LateralTorqueState.new_message()
|
||||||
|
pid_log.version = VERSION
|
||||||
if not active:
|
if not active:
|
||||||
output_torque = 0.0
|
output_torque = 0.0
|
||||||
pid_log.active = False
|
pid_log.active = False
|
||||||
else:
|
else:
|
||||||
actual_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
|
measured_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
|
||||||
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
|
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
|
||||||
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
|
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
|
||||||
|
|
||||||
desired_lateral_accel = desired_curvature * CS.vEgo ** 2
|
|
||||||
actual_lateral_accel = actual_curvature * CS.vEgo ** 2
|
|
||||||
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2
|
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2
|
||||||
|
|
||||||
low_speed_factor = np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2
|
delay_frames = int(np.clip(lat_delay / self.dt, 1, self.lat_accel_request_buffer_len))
|
||||||
setpoint = desired_lateral_accel + low_speed_factor * desired_curvature
|
expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames]
|
||||||
measurement = actual_lateral_accel + low_speed_factor * actual_curvature
|
# TODO factor out lateral jerk from error to later replace it with delay independent alternative
|
||||||
gravity_adjusted_lateral_accel = desired_lateral_accel - roll_compensation
|
future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2
|
||||||
|
self.lat_accel_request_buffer.append(future_desired_lateral_accel)
|
||||||
|
gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
|
||||||
|
desired_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / lat_delay
|
||||||
|
|
||||||
|
measurement = measured_curvature * CS.vEgo ** 2
|
||||||
|
measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt)
|
||||||
|
self.previous_measurement = measurement
|
||||||
|
|
||||||
|
setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel
|
||||||
|
error = setpoint - measurement
|
||||||
|
|
||||||
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
|
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
|
||||||
pid_log.error = float(setpoint - measurement)
|
pid_log.error = float(error)
|
||||||
ff = gravity_adjusted_lateral_accel
|
ff = gravity_adjusted_future_lateral_accel
|
||||||
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
|
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
|
||||||
ff -= self.torque_params.latAccelOffset
|
ff -= self.torque_params.latAccelOffset
|
||||||
ff += get_friction(desired_lateral_accel - actual_lateral_accel, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)
|
# TODO jerk is weighted by lat_delay for legacy reasons, but should be made independent of it
|
||||||
|
ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)
|
||||||
|
|
||||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
||||||
output_lataccel = self.pid.update(pid_log.error,
|
output_lataccel = self.pid.update(pid_log.error,
|
||||||
feedforward=ff,
|
-measurement_rate,
|
||||||
speed=CS.vEgo,
|
feedforward=ff,
|
||||||
freeze_integrator=freeze_integrator)
|
speed=CS.vEgo,
|
||||||
|
freeze_integrator=freeze_integrator)
|
||||||
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
|
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
|
||||||
|
|
||||||
# Lateral acceleration torque controller extension updates
|
# Lateral acceleration torque controller extension updates
|
||||||
# Overrides pid_log.error and output_torque
|
# Overrides pid_log.error and output_torque
|
||||||
pid_log, output_torque = self.extension.update(CS, VM, self.pid, params, ff, pid_log, setpoint, measurement, calibrated_pose, roll_compensation,
|
pid_log, output_torque = self.extension.update(CS, VM, self.pid, params, ff, pid_log, setpoint, measurement, calibrated_pose, roll_compensation,
|
||||||
desired_lateral_accel, actual_lateral_accel, lateral_accel_deadzone, gravity_adjusted_lateral_accel,
|
future_desired_lateral_accel, measurement, lateral_accel_deadzone, gravity_adjusted_future_lateral_accel,
|
||||||
desired_curvature, actual_curvature, steer_limited_by_safety, output_torque)
|
desired_curvature, measured_curvature, steer_limited_by_safety, output_torque)
|
||||||
|
|
||||||
pid_log.active = True
|
pid_log.active = True
|
||||||
pid_log.p = float(self.pid.p)
|
pid_log.p = float(self.pid.p)
|
||||||
pid_log.i = float(self.pid.i)
|
pid_log.i = float(self.pid.i)
|
||||||
pid_log.d = float(self.pid.d)
|
pid_log.d = float(self.pid.d)
|
||||||
pid_log.f = float(self.pid.f)
|
pid_log.f = float(self.pid.f)
|
||||||
pid_log.output = float(-output_torque) # TODO: log lat accel?
|
pid_log.output = float(-output_torque) # TODO: log lat accel?
|
||||||
pid_log.actualLateralAccel = float(actual_lateral_accel)
|
pid_log.actualLateralAccel = float(measurement)
|
||||||
pid_log.desiredLateralAccel = float(desired_lateral_accel)
|
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))
|
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
|
# TODO left is positive in this convention
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class LongControl:
|
|||||||
self.long_control_state = LongCtrlState.off
|
self.long_control_state = LongCtrlState.off
|
||||||
self.pid = PIDController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
|
self.pid = PIDController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
|
||||||
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
|
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
|
||||||
k_f=CP.longitudinalTuning.kf, rate=1 / DT_CTRL)
|
rate=1 / DT_CTRL)
|
||||||
self.last_output_accel = 0.0
|
self.last_output_accel = 0.0
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
|||||||
@@ -51,12 +51,12 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP):
|
|||||||
|
|
||||||
|
|
||||||
class LongitudinalPlanner(LongitudinalPlannerSP):
|
class LongitudinalPlanner(LongitudinalPlannerSP):
|
||||||
def __init__(self, CP, init_v=0.0, init_a=0.0, dt=DT_MDL):
|
def __init__(self, CP, CP_SP, init_v=0.0, init_a=0.0, dt=DT_MDL):
|
||||||
self.CP = CP
|
self.CP = CP
|
||||||
self.mpc = LongitudinalMpc(dt=dt)
|
self.mpc = LongitudinalMpc(dt=dt)
|
||||||
# TODO remove mpc modes when TR released
|
# TODO remove mpc modes when TR released
|
||||||
self.mpc.mode = 'acc'
|
self.mpc.mode = 'acc'
|
||||||
LongitudinalPlannerSP.__init__(self, self.CP, self.mpc)
|
LongitudinalPlannerSP.__init__(self, self.CP, CP_SP, self.mpc)
|
||||||
self.fcw = False
|
self.fcw = False
|
||||||
self.dt = dt
|
self.dt = dt
|
||||||
self.allow_throttle = True
|
self.allow_throttle = True
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from cereal import car
|
from cereal import car, custom
|
||||||
from openpilot.common.gps import get_gps_location_service
|
from openpilot.common.gps import get_gps_location_service
|
||||||
from openpilot.common.params import Params
|
from openpilot.common.params import Params
|
||||||
from openpilot.common.realtime import Priority, config_realtime_process
|
from openpilot.common.realtime import Priority, config_realtime_process
|
||||||
@@ -17,10 +17,14 @@ def main():
|
|||||||
CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)
|
CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)
|
||||||
cloudlog.info("plannerd got CarParams: %s", CP.brand)
|
cloudlog.info("plannerd got CarParams: %s", CP.brand)
|
||||||
|
|
||||||
|
cloudlog.info("plannerd is waiting for CarParamsSP")
|
||||||
|
CP_SP = messaging.log_from_bytes(params.get("CarParamsSP", block=True), custom.CarParamsSP)
|
||||||
|
cloudlog.info("plannerd got CarParamsSP")
|
||||||
|
|
||||||
gps_location_service = get_gps_location_service(params)
|
gps_location_service = get_gps_location_service(params)
|
||||||
|
|
||||||
ldw = LaneDepartureWarning()
|
ldw = LaneDepartureWarning()
|
||||||
longitudinal_planner = LongitudinalPlanner(CP)
|
longitudinal_planner = LongitudinalPlanner(CP, CP_SP)
|
||||||
pm = messaging.PubMaster(['longitudinalPlan', 'driverAssistance', 'longitudinalPlanSP'])
|
pm = messaging.PubMaster(['longitudinalPlan', 'driverAssistance', 'longitudinalPlanSP'])
|
||||||
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'liveParameters', 'radarState', 'modelV2', 'selfdriveState',
|
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'liveParameters', 'radarState', 'modelV2', 'selfdriveState',
|
||||||
'liveMapDataSP', 'carStateSP', gps_location_service],
|
'liveMapDataSP', 'carStateSP', gps_location_service],
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from opendbc.car.toyota.values import CAR as TOYOTA
|
|||||||
from opendbc.car.nissan.values import CAR as NISSAN
|
from opendbc.car.nissan.values import CAR as NISSAN
|
||||||
from opendbc.car.gm.values import CAR as GM
|
from opendbc.car.gm.values import CAR as GM
|
||||||
from opendbc.car.vehicle_model import VehicleModel
|
from opendbc.car.vehicle_model import VehicleModel
|
||||||
|
from openpilot.common.realtime import DT_CTRL
|
||||||
from openpilot.selfdrive.car.helpers import convert_to_capnp
|
from openpilot.selfdrive.car.helpers import convert_to_capnp
|
||||||
from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
|
from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
|
||||||
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
||||||
@@ -29,7 +30,7 @@ class TestLatControl:
|
|||||||
CP_SP = convert_to_capnp(CP_SP)
|
CP_SP = convert_to_capnp(CP_SP)
|
||||||
VM = VehicleModel(CP)
|
VM = VehicleModel(CP)
|
||||||
|
|
||||||
controller = controller(CP.as_reader(), CP_SP.as_reader(), CI)
|
controller = controller(CP.as_reader(), CP_SP.as_reader(), CI, DT_CTRL)
|
||||||
|
|
||||||
CS = car.CarState.new_message()
|
CS = car.CarState.new_message()
|
||||||
CS.vEgo = 30
|
CS.vEgo = 30
|
||||||
@@ -42,13 +43,13 @@ class TestLatControl:
|
|||||||
|
|
||||||
# Saturate for curvature limited and controller limited
|
# Saturate for curvature limited and controller limited
|
||||||
for _ in range(1000):
|
for _ in range(1000):
|
||||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, True)
|
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, True, 0.2)
|
||||||
assert lac_log.saturated
|
assert lac_log.saturated
|
||||||
|
|
||||||
for _ in range(1000):
|
for _ in range(1000):
|
||||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, False)
|
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, False, 0.2)
|
||||||
assert not lac_log.saturated
|
assert not lac_log.saturated
|
||||||
|
|
||||||
for _ in range(1000):
|
for _ in range(1000):
|
||||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, pose, False)
|
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, pose, False, 0.2)
|
||||||
assert lac_log.saturated
|
assert lac_log.saturated
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from collections import defaultdict
|
|||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
from cereal.services import SERVICE_LIST
|
from cereal.services import SERVICE_LIST
|
||||||
from openpilot.common.file_helpers import LOG_COMPRESSION_LEVEL
|
from openpilot.common.utils import LOG_COMPRESSION_LEVEL
|
||||||
from openpilot.tools.lib.logreader import LogReader
|
from openpilot.tools.lib.logreader import LogReader
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from collections import deque, defaultdict
|
from collections import deque, defaultdict
|
||||||
|
|
||||||
@@ -250,6 +251,8 @@ class TorqueEstimator(ParameterEstimator, TorqueEstimatorExt):
|
|||||||
def main(demo=False):
|
def main(demo=False):
|
||||||
config_realtime_process([0, 1, 2, 3], 5)
|
config_realtime_process([0, 1, 2, 3], 5)
|
||||||
|
|
||||||
|
DEBUG = bool(int(os.getenv("DEBUG", "0")))
|
||||||
|
|
||||||
pm = messaging.PubMaster(['liveTorqueParameters'])
|
pm = messaging.PubMaster(['liveTorqueParameters'])
|
||||||
sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveCalibration', 'livePose', 'liveDelay'], poll='livePose')
|
sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveCalibration', 'livePose', 'liveDelay'], poll='livePose')
|
||||||
|
|
||||||
@@ -268,7 +271,7 @@ def main(demo=False):
|
|||||||
|
|
||||||
# 4Hz driven by livePose
|
# 4Hz driven by livePose
|
||||||
if sm.frame % 5 == 0:
|
if sm.frame % 5 == 0:
|
||||||
pm.send('liveTorqueParameters', estimator.get_msg(valid=sm.all_checks()))
|
pm.send('liveTorqueParameters', estimator.get_msg(valid=sm.all_checks(), with_points=DEBUG))
|
||||||
|
|
||||||
# Cache points every 60 seconds while onroad
|
# Cache points every 60 seconds while onroad
|
||||||
if sm.frame % 240 == 0:
|
if sm.frame % 240 == 0:
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
|
|
||||||
Import('env', 'envCython', 'arch', 'cereal', 'messaging', 'common', 'gpucommon', 'visionipc', 'transformations')
|
Import('env', 'envCython', 'arch', 'cereal', 'messaging', 'common', 'visionipc', 'transformations')
|
||||||
lenv = env.Clone()
|
lenv = env.Clone()
|
||||||
lenvCython = envCython.Clone()
|
lenvCython = envCython.Clone()
|
||||||
|
|
||||||
libs = [cereal, messaging, visionipc, gpucommon, common, 'capnp', 'kj', 'pthread']
|
libs = [cereal, messaging, visionipc, common, 'capnp', 'kj', 'pthread']
|
||||||
frameworks = []
|
frameworks = []
|
||||||
|
|
||||||
common_src = [
|
common_src = [
|
||||||
@@ -51,8 +51,8 @@ def tg_compile(flags, model_name):
|
|||||||
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
||||||
flags = {
|
flags = {
|
||||||
'larch64': 'DEV=QCOM',
|
'larch64': 'DEV=QCOM',
|
||||||
'Darwin': 'DEV=CPU IMAGE=0',
|
'Darwin': f'DEV=CPU HOME={os.path.expanduser("~")} IMAGE=0', # tinygrad calls brew which needs a $HOME in the env
|
||||||
}.get(arch, 'DEV=LLVM IMAGE=0')
|
}.get(arch, 'DEV=CPU CPU_LLVM=1 IMAGE=0')
|
||||||
tg_compile(flags, model_name)
|
tg_compile(flags, model_name)
|
||||||
|
|
||||||
# Compile BIG model if USB GPU is available
|
# Compile BIG model if USB GPU is available
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
from openpilot.system.hardware import TICI
|
from openpilot.system.hardware import TICI
|
||||||
os.environ['DEV'] = 'QCOM' if TICI else 'LLVM'
|
os.environ['DEV'] = 'QCOM' if TICI else 'CPU'
|
||||||
from tinygrad.tensor import Tensor
|
from tinygrad.tensor import Tensor
|
||||||
from tinygrad.dtype import dtypes
|
from tinygrad.dtype import dtypes
|
||||||
import math
|
import math
|
||||||
@@ -25,13 +25,13 @@ from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from
|
|||||||
MODEL_WIDTH, MODEL_HEIGHT = DM_INPUT_SIZE
|
MODEL_WIDTH, MODEL_HEIGHT = DM_INPUT_SIZE
|
||||||
CALIB_LEN = 3
|
CALIB_LEN = 3
|
||||||
FEATURE_LEN = 512
|
FEATURE_LEN = 512
|
||||||
OUTPUT_SIZE = 84 + FEATURE_LEN
|
OUTPUT_SIZE = 83 + FEATURE_LEN
|
||||||
|
|
||||||
PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld"
|
PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld"
|
||||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||||
MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl'
|
MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl'
|
||||||
|
|
||||||
|
# TODO: slice from meta
|
||||||
class DriverStateResult(ctypes.Structure):
|
class DriverStateResult(ctypes.Structure):
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
("face_orientation", ctypes.c_float*3),
|
("face_orientation", ctypes.c_float*3),
|
||||||
@@ -46,8 +46,8 @@ class DriverStateResult(ctypes.Structure):
|
|||||||
("left_blink_prob", ctypes.c_float),
|
("left_blink_prob", ctypes.c_float),
|
||||||
("right_blink_prob", ctypes.c_float),
|
("right_blink_prob", ctypes.c_float),
|
||||||
("sunglasses_prob", ctypes.c_float),
|
("sunglasses_prob", ctypes.c_float),
|
||||||
("occluded_prob", ctypes.c_float),
|
("_unused_c", ctypes.c_float),
|
||||||
("ready_prob", ctypes.c_float*4),
|
("_unused_d", ctypes.c_float*4),
|
||||||
("not_ready_prob", ctypes.c_float*2)]
|
("not_ready_prob", ctypes.c_float*2)]
|
||||||
|
|
||||||
|
|
||||||
@@ -55,7 +55,6 @@ class DMonitoringModelResult(ctypes.Structure):
|
|||||||
_fields_ = [
|
_fields_ = [
|
||||||
("driver_state_lhd", DriverStateResult),
|
("driver_state_lhd", DriverStateResult),
|
||||||
("driver_state_rhd", DriverStateResult),
|
("driver_state_rhd", DriverStateResult),
|
||||||
("poor_vision_prob", ctypes.c_float),
|
|
||||||
("wheel_on_right_prob", ctypes.c_float),
|
("wheel_on_right_prob", ctypes.c_float),
|
||||||
("features", ctypes.c_float*FEATURE_LEN)]
|
("features", ctypes.c_float*FEATURE_LEN)]
|
||||||
|
|
||||||
@@ -107,8 +106,6 @@ def fill_driver_state(msg, ds_result: DriverStateResult):
|
|||||||
msg.leftBlinkProb = float(sigmoid(ds_result.left_blink_prob))
|
msg.leftBlinkProb = float(sigmoid(ds_result.left_blink_prob))
|
||||||
msg.rightBlinkProb = float(sigmoid(ds_result.right_blink_prob))
|
msg.rightBlinkProb = float(sigmoid(ds_result.right_blink_prob))
|
||||||
msg.sunglassesProb = float(sigmoid(ds_result.sunglasses_prob))
|
msg.sunglassesProb = float(sigmoid(ds_result.sunglasses_prob))
|
||||||
msg.occludedProb = float(sigmoid(ds_result.occluded_prob))
|
|
||||||
msg.readyProb = [float(sigmoid(x)) for x in ds_result.ready_prob]
|
|
||||||
msg.notReadyProb = [float(sigmoid(x)) for x in ds_result.not_ready_prob]
|
msg.notReadyProb = [float(sigmoid(x)) for x in ds_result.not_ready_prob]
|
||||||
|
|
||||||
|
|
||||||
@@ -119,7 +116,6 @@ def get_driverstate_packet(model_output: np.ndarray, frame_id: int, location_ts:
|
|||||||
ds.frameId = frame_id
|
ds.frameId = frame_id
|
||||||
ds.modelExecutionTime = execution_time
|
ds.modelExecutionTime = execution_time
|
||||||
ds.gpuExecutionTime = gpu_execution_time
|
ds.gpuExecutionTime = gpu_execution_time
|
||||||
ds.poorVisionProb = float(sigmoid(model_result.poor_vision_prob))
|
|
||||||
ds.wheelOnRightProb = float(sigmoid(model_result.wheel_on_right_prob))
|
ds.wheelOnRightProb = float(sigmoid(model_result.wheel_on_right_prob))
|
||||||
ds.rawPredictions = model_output.tobytes() if SEND_RAW_PRED else b''
|
ds.rawPredictions = model_output.tobytes() if SEND_RAW_PRED else b''
|
||||||
fill_driver_state(ds.leftDriverData, model_result.driver_state_lhd)
|
fill_driver_state(ds.leftDriverData, model_result.driver_state_lhd)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
from openpilot.system.hardware import TICI
|
from openpilot.system.hardware import TICI
|
||||||
os.environ['DEV'] = 'QCOM' if TICI else 'LLVM'
|
os.environ['DEV'] = 'QCOM' if TICI else 'CPU'
|
||||||
USBGPU = "USBGPU" in os.environ
|
USBGPU = "USBGPU" in os.environ
|
||||||
if USBGPU:
|
if USBGPU:
|
||||||
os.environ['DEV'] = 'AMD'
|
os.environ['DEV'] = 'AMD'
|
||||||
|
|||||||
@@ -62,6 +62,5 @@ Refer to **slice_outputs** and **parse_vision_outputs/parse_policy_outputs** in
|
|||||||
* (deprecated) distracted probabilities: 2
|
* (deprecated) distracted probabilities: 2
|
||||||
* using phone probability: 1
|
* using phone probability: 1
|
||||||
* distracted probability: 1
|
* distracted probability: 1
|
||||||
* common outputs 2
|
* common outputs 1
|
||||||
* poor camera vision probability: 1
|
|
||||||
* left hand drive probability: 1
|
* left hand drive probability: 1
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
fa69be01-b430-4504-9d72-7dcb058eb6dd
|
|
||||||
d9fb22d1c4fa3ca3d201dbc8edf1d0f0918e53e6
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:50efe6451a3fb3fa04b6bb0e846544533329bd46ecefe9e657e91214dee2aaeb
|
oid sha256:3a53626ab84757813fb16a1441704f2ae7192bef88c331bdc2415be6981d204f
|
||||||
size 7196502
|
size 7191776
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ebb38a934d6472c061cc6010f46d9720ca132d631a47e585a893bdd41ade2419
|
oid sha256:c5a1f0655ddf266ed42ad1980389d96f47cc5e756da1fa3ca1477a920bb9b157
|
||||||
size 12343535
|
size 13926324
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:befac016a247b7ad5dc5b55d339d127774ed7bd2b848f1583f72aa4caee37781
|
oid sha256:8f16d548ea4eb5d01518a9e90d4527cd97c31a84bcaf6f695dead8f0015fecc4
|
||||||
size 46271991
|
size 46271942
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import numpy as np
|
|||||||
from cereal import car, log
|
from cereal import car, log
|
||||||
import cereal.messaging as messaging
|
import cereal.messaging as messaging
|
||||||
from openpilot.selfdrive.selfdrived.events import Events
|
from openpilot.selfdrive.selfdrived.events import Events
|
||||||
|
from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
|
||||||
from openpilot.common.realtime import DT_DMON
|
from openpilot.common.realtime import DT_DMON
|
||||||
from openpilot.common.filter_simple import FirstOrderFilter
|
from openpilot.common.filter_simple import FirstOrderFilter
|
||||||
from openpilot.common.params import Params
|
from openpilot.common.params import Params
|
||||||
from openpilot.common.stat_live import RunningStatFilter
|
from openpilot.common.stat_live import RunningStatFilter
|
||||||
from openpilot.common.transformations.camera import DEVICE_CAMERAS
|
from openpilot.common.transformations.camera import DEVICE_CAMERAS
|
||||||
|
from openpilot.system.hardware import HARDWARE
|
||||||
|
|
||||||
EventName = log.OnroadEvent.EventName
|
EventName = log.OnroadEvent.EventName
|
||||||
|
|
||||||
@@ -34,12 +36,13 @@ class DRIVER_MONITOR_SETTINGS:
|
|||||||
self._SG_THRESHOLD = 0.9
|
self._SG_THRESHOLD = 0.9
|
||||||
self._BLINK_THRESHOLD = 0.865
|
self._BLINK_THRESHOLD = 0.865
|
||||||
|
|
||||||
self._EE_THRESH11 = 0.4
|
if HARDWARE.get_device_type() == 'mici':
|
||||||
|
self._EE_THRESH11 = 0.75
|
||||||
|
else:
|
||||||
|
self._EE_THRESH11 = 0.4
|
||||||
self._EE_THRESH12 = 15.0
|
self._EE_THRESH12 = 15.0
|
||||||
self._EE_MAX_OFFSET1 = 0.06
|
self._EE_MAX_OFFSET1 = 0.06
|
||||||
self._EE_MIN_OFFSET1 = 0.025
|
self._EE_MIN_OFFSET1 = 0.025
|
||||||
self._EE_THRESH21 = 0.01
|
|
||||||
self._EE_THRESH22 = 0.35
|
|
||||||
|
|
||||||
self._POSE_PITCH_THRESHOLD = 0.3133
|
self._POSE_PITCH_THRESHOLD = 0.3133
|
||||||
self._POSE_PITCH_THRESHOLD_SLACK = 0.3237
|
self._POSE_PITCH_THRESHOLD_SLACK = 0.3237
|
||||||
@@ -55,6 +58,9 @@ class DRIVER_MONITOR_SETTINGS:
|
|||||||
self._YAW_MAX_OFFSET = 0.289
|
self._YAW_MAX_OFFSET = 0.289
|
||||||
self._YAW_MIN_OFFSET = -0.0246
|
self._YAW_MIN_OFFSET = -0.0246
|
||||||
|
|
||||||
|
self._DCAM_UNCERTAIN_ALERT_THRESHOLD = 0.1
|
||||||
|
self._DCAM_UNCERTAIN_ALERT_COUNT = int(60 / self._DT_DMON)
|
||||||
|
self._DCAM_UNCERTAIN_RESET_COUNT = int(20 / self._DT_DMON)
|
||||||
self._POSESTD_THRESHOLD = 0.3
|
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._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._DISTRACTED_FILTER_TS = 0.25 # 0.6Hz
|
||||||
@@ -137,11 +143,8 @@ class DriverMonitoring:
|
|||||||
self.pose = DriverPose(self.settings._POSE_OFFSET_MAX_COUNT)
|
self.pose = DriverPose(self.settings._POSE_OFFSET_MAX_COUNT)
|
||||||
self.blink = DriverBlink()
|
self.blink = DriverBlink()
|
||||||
self.eev1 = 0.
|
self.eev1 = 0.
|
||||||
self.eev2 = 1.
|
|
||||||
self.ee1_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT)
|
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.ee1_calibrated = False
|
||||||
self.ee2_calibrated = False
|
|
||||||
|
|
||||||
self.always_on = always_on
|
self.always_on = always_on
|
||||||
self.distracted_types = []
|
self.distracted_types = []
|
||||||
@@ -159,6 +162,9 @@ class DriverMonitoring:
|
|||||||
self.hi_stds = 0
|
self.hi_stds = 0
|
||||||
self.threshold_pre = self.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL / self.settings._DISTRACTED_TIME
|
self.threshold_pre = self.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL / self.settings._DISTRACTED_TIME
|
||||||
self.threshold_prompt = self.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL / self.settings._DISTRACTED_TIME
|
self.threshold_prompt = self.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL / self.settings._DISTRACTED_TIME
|
||||||
|
self.dcam_uncertain_cnt = 0
|
||||||
|
self.dcam_uncertain_alerted = False # once per drive
|
||||||
|
self.dcam_reset_cnt = 0
|
||||||
|
|
||||||
self.params = Params()
|
self.params = Params()
|
||||||
self.too_distracted = self.params.get_bool("DriverTooDistracted")
|
self.too_distracted = self.params.get_bool("DriverTooDistracted")
|
||||||
@@ -246,7 +252,7 @@ class DriverMonitoring:
|
|||||||
|
|
||||||
return distracted_types
|
return distracted_types
|
||||||
|
|
||||||
def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged):
|
def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged, standstill):
|
||||||
rhd_pred = driver_state.wheelOnRightProb
|
rhd_pred = driver_state.wheelOnRightProb
|
||||||
# calibrates only when there's movement and either face detected
|
# calibrates only when there's movement and either face detected
|
||||||
if car_speed > self.settings._WHEELPOS_CALIB_MIN_SPEED and (driver_state.leftDriverData.faceProb > self.settings._FACE_THRESHOLD or
|
if car_speed > self.settings._WHEELPOS_CALIB_MIN_SPEED and (driver_state.leftDriverData.faceProb > self.settings._FACE_THRESHOLD or
|
||||||
@@ -262,7 +268,7 @@ class DriverMonitoring:
|
|||||||
driver_data = driver_state.rightDriverData if self.wheel_on_right else driver_state.leftDriverData
|
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,
|
if not all(len(x) > 0 for x in (driver_data.faceOrientation, driver_data.facePosition,
|
||||||
driver_data.faceOrientationStd, driver_data.facePositionStd,
|
driver_data.faceOrientationStd, driver_data.facePositionStd,
|
||||||
driver_data.readyProb, driver_data.notReadyProb)):
|
driver_data.notReadyProb)):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.face_detected = driver_data.faceProb > self.settings._FACE_THRESHOLD
|
self.face_detected = driver_data.faceProb > self.settings._FACE_THRESHOLD
|
||||||
@@ -279,7 +285,6 @@ class DriverMonitoring:
|
|||||||
self.blink.right = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) \
|
self.blink.right = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) \
|
||||||
* (driver_data.sunglassesProb < self.settings._SG_THRESHOLD)
|
* (driver_data.sunglassesProb < self.settings._SG_THRESHOLD)
|
||||||
self.eev1 = driver_data.notReadyProb[0]
|
self.eev1 = driver_data.notReadyProb[0]
|
||||||
self.eev2 = driver_data.readyProb[0]
|
|
||||||
|
|
||||||
self.distracted_types = self._get_distracted_types()
|
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
|
self.driver_distracted = (DistractedType.DISTRACTED_E2E in self.distracted_types or DistractedType.DISTRACTED_POSE in self.distracted_types
|
||||||
@@ -293,12 +298,20 @@ class DriverMonitoring:
|
|||||||
self.pose.pitch_offseter.push_and_update(self.pose.pitch)
|
self.pose.pitch_offseter.push_and_update(self.pose.pitch)
|
||||||
self.pose.yaw_offseter.push_and_update(self.pose.yaw)
|
self.pose.yaw_offseter.push_and_update(self.pose.yaw)
|
||||||
self.ee1_offseter.push_and_update(self.eev1)
|
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.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.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.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
|
|
||||||
|
if self.face_detected and not self.driver_distracted:
|
||||||
|
if model_std_max > self.settings._DCAM_UNCERTAIN_ALERT_THRESHOLD:
|
||||||
|
if not standstill:
|
||||||
|
self.dcam_uncertain_cnt += 1
|
||||||
|
self.dcam_reset_cnt = 0
|
||||||
|
else:
|
||||||
|
self.dcam_reset_cnt += 1
|
||||||
|
if self.dcam_reset_cnt > self.settings._DCAM_UNCERTAIN_RESET_COUNT:
|
||||||
|
self.dcam_uncertain_cnt = 0
|
||||||
|
|
||||||
self.is_model_uncertain = self.hi_stds > self.settings._HI_STD_FALLBACK_TIME
|
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)
|
self._set_timers(self.face_detected and not self.is_model_uncertain)
|
||||||
@@ -376,6 +389,10 @@ class DriverMonitoring:
|
|||||||
if alert is not None:
|
if alert is not None:
|
||||||
self.current_events.add(alert)
|
self.current_events.add(alert)
|
||||||
|
|
||||||
|
if self.dcam_uncertain_cnt > self.settings._DCAM_UNCERTAIN_ALERT_COUNT and not self.dcam_uncertain_alerted:
|
||||||
|
set_offroad_alert("Offroad_DriverMonitoringUncertain", True)
|
||||||
|
self.dcam_uncertain_alerted = True
|
||||||
|
|
||||||
|
|
||||||
def get_state_packet(self, valid=True):
|
def get_state_packet(self, valid=True):
|
||||||
# build driverMonitoringState packet
|
# build driverMonitoringState packet
|
||||||
@@ -397,6 +414,7 @@ class DriverMonitoring:
|
|||||||
"hiStdCount": self.hi_stds,
|
"hiStdCount": self.hi_stds,
|
||||||
"isActiveMode": self.active_monitoring_mode,
|
"isActiveMode": self.active_monitoring_mode,
|
||||||
"isRHD": self.wheel_on_right,
|
"isRHD": self.wheel_on_right,
|
||||||
|
"uncertainCount": self.dcam_uncertain_cnt,
|
||||||
}
|
}
|
||||||
return dat
|
return dat
|
||||||
|
|
||||||
@@ -412,7 +430,8 @@ class DriverMonitoring:
|
|||||||
driver_state=sm['driverStateV2'],
|
driver_state=sm['driverStateV2'],
|
||||||
cal_rpy=sm['liveCalibration'].rpyCalib,
|
cal_rpy=sm['liveCalibration'].rpyCalib,
|
||||||
car_speed=sm['carState'].vEgo,
|
car_speed=sm['carState'].vEgo,
|
||||||
op_engaged=sm['selfdriveState'].enabled or sm['carControl'].latActive
|
op_engaged=sm['selfdriveState'].enabled or sm['carControl'].latActive,
|
||||||
|
standstill=sm['carState'].standstill,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update distraction events
|
# Update distraction events
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ def make_msg(face_detected, distracted=False, model_uncertain=False):
|
|||||||
ds.leftDriverData.faceOrientationStd = [1.*model_uncertain, 1.*model_uncertain, 1.*model_uncertain]
|
ds.leftDriverData.faceOrientationStd = [1.*model_uncertain, 1.*model_uncertain, 1.*model_uncertain]
|
||||||
ds.leftDriverData.facePositionStd = [1.*model_uncertain, 1.*model_uncertain]
|
ds.leftDriverData.facePositionStd = [1.*model_uncertain, 1.*model_uncertain]
|
||||||
# TODO: test both separately when e2e is used
|
# TODO: test both separately when e2e is used
|
||||||
ds.leftDriverData.readyProb = [0., 0., 0., 0.]
|
|
||||||
ds.leftDriverData.notReadyProb = [0., 0.]
|
ds.leftDriverData.notReadyProb = [0., 0.]
|
||||||
return ds
|
return ds
|
||||||
|
|
||||||
@@ -54,7 +53,7 @@ class TestMonitoring:
|
|||||||
DM = DriverMonitoring()
|
DM = DriverMonitoring()
|
||||||
events = []
|
events = []
|
||||||
for idx in range(len(msgs)):
|
for idx in range(len(msgs)):
|
||||||
DM._update_states(msgs[idx], [0, 0, 0], 0, engaged[idx])
|
DM._update_states(msgs[idx], [0, 0, 0], 0, engaged[idx], standstill[idx])
|
||||||
# cal_rpy and car_speed don't matter here
|
# cal_rpy and car_speed don't matter here
|
||||||
|
|
||||||
# evaluate events at 10Hz for tests
|
# evaluate events at 10Hz for tests
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ Panda *connect(std::string serial="", uint32_t index=0) {
|
|||||||
}
|
}
|
||||||
//panda->enable_deepsleep();
|
//panda->enable_deepsleep();
|
||||||
|
|
||||||
for (int i = 0; i < PANDA_BUS_CNT; i++) {
|
for (int i = 0; i < PANDA_CAN_CNT; i++) {
|
||||||
panda->set_can_fd_auto(i, true);
|
panda->set_can_fd_auto(i, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import time
|
|||||||
import cereal.messaging as messaging
|
import cereal.messaging as messaging
|
||||||
from cereal import log
|
from cereal import log
|
||||||
from openpilot.common.gpio import gpio_set, gpio_init
|
from openpilot.common.gpio import gpio_set, gpio_init
|
||||||
from panda import Panda, PandaDFU, PandaProtocolMismatch
|
from panda import Panda, PandaDFU
|
||||||
from openpilot.common.retry import retry
|
|
||||||
from openpilot.system.manager.process_config import managed_processes
|
from openpilot.system.manager.process_config import managed_processes
|
||||||
from openpilot.system.hardware import HARDWARE
|
from openpilot.system.hardware import HARDWARE
|
||||||
from openpilot.system.hardware.tici.pins import GPIO
|
from openpilot.system.hardware.tici.pins import GPIO
|
||||||
@@ -50,8 +49,7 @@ class TestPandad:
|
|||||||
assert not Panda.wait_for_dfu(None, 3)
|
assert not Panda.wait_for_dfu(None, 3)
|
||||||
assert not Panda.wait_for_panda(None, 3)
|
assert not Panda.wait_for_panda(None, 3)
|
||||||
|
|
||||||
@retry(attempts=3)
|
def _flash_bootstub(self, fn):
|
||||||
def _flash_bootstub_and_test(self, fn, expect_mismatch=False):
|
|
||||||
self._go_to_dfu()
|
self._go_to_dfu()
|
||||||
pd = PandaDFU(None)
|
pd = PandaDFU(None)
|
||||||
if fn is None:
|
if fn is None:
|
||||||
@@ -61,16 +59,6 @@ class TestPandad:
|
|||||||
pd.reset()
|
pd.reset()
|
||||||
HARDWARE.reset_internal_panda()
|
HARDWARE.reset_internal_panda()
|
||||||
|
|
||||||
assert Panda.wait_for_panda(None, 10)
|
|
||||||
if expect_mismatch:
|
|
||||||
with pytest.raises(PandaProtocolMismatch):
|
|
||||||
Panda()
|
|
||||||
else:
|
|
||||||
with Panda() as p:
|
|
||||||
assert p.bootstub
|
|
||||||
|
|
||||||
self._run_test(45)
|
|
||||||
|
|
||||||
def test_in_dfu(self):
|
def test_in_dfu(self):
|
||||||
HARDWARE.recover_internal_panda()
|
HARDWARE.recover_internal_panda()
|
||||||
self._run_test(60)
|
self._run_test(60)
|
||||||
@@ -106,13 +94,14 @@ class TestPandad:
|
|||||||
print("startup times", ts, sum(ts) / len(ts))
|
print("startup times", ts, sum(ts) / len(ts))
|
||||||
assert 0.1 < (sum(ts)/len(ts)) < 0.7
|
assert 0.1 < (sum(ts)/len(ts)) < 0.7
|
||||||
|
|
||||||
def test_protocol_version_check(self):
|
def test_old_spi_protocol(self):
|
||||||
# flash old fw
|
# flash firmware with old SPI protocol
|
||||||
fn = os.path.join(HERE, "bootstub.panda_h7_spiv0.bin")
|
self._flash_bootstub(os.path.join(HERE, "bootstub.panda_h7_spiv0.bin"))
|
||||||
self._flash_bootstub_and_test(fn, expect_mismatch=True)
|
self._run_test(45)
|
||||||
|
|
||||||
def test_release_to_devel_bootstub(self):
|
def test_release_to_devel_bootstub(self):
|
||||||
self._flash_bootstub_and_test(None)
|
self._flash_bootstub(None)
|
||||||
|
self._run_test(45)
|
||||||
|
|
||||||
def test_recover_from_bad_bootstub(self):
|
def test_recover_from_bad_bootstub(self):
|
||||||
self._go_to_dfu()
|
self._go_to_dfu()
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from pprint import pprint
|
|||||||
import cereal.messaging as messaging
|
import cereal.messaging as messaging
|
||||||
from cereal import car, log
|
from cereal import car, log
|
||||||
from opendbc.car.can_definitions import CanData
|
from opendbc.car.can_definitions import CanData
|
||||||
from openpilot.common.retry import retry
|
from openpilot.common.utils import retry
|
||||||
from openpilot.common.params import Params
|
from openpilot.common.params import Params
|
||||||
from openpilot.common.timeout import Timeout
|
from openpilot.common.timeout import Timeout
|
||||||
from openpilot.selfdrive.pandad import can_list_to_can_capnp
|
from openpilot.selfdrive.pandad import can_list_to_can_capnp
|
||||||
|
|||||||
@@ -41,6 +41,10 @@
|
|||||||
"text": "OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display.\n\n%1",
|
"text": "OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display.\n\n%1",
|
||||||
"severity": 0
|
"severity": 0
|
||||||
},
|
},
|
||||||
|
"Offroad_DriverMonitoringUncertain": {
|
||||||
|
"text": "openpilot detected poor visibility for driver monitoring. Ensure the device has a clear view of the driver. This can be checked using Settings -> Device -> Driver Camera Preview. Extreme lighting conditions and/or unconventional mounting positions may also trigger this alert.",
|
||||||
|
"severity": 0
|
||||||
|
},
|
||||||
"Offroad_ExcessiveActuation": {
|
"Offroad_ExcessiveActuation": {
|
||||||
"text": "openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting.",
|
"text": "openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting.",
|
||||||
"severity": 1,
|
"severity": 1,
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.
|
|||||||
|
|
||||||
def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
|
def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
|
||||||
return Alert(
|
return Alert(
|
||||||
f"Steer Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}",
|
f"Steer Assist Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}",
|
||||||
"",
|
"",
|
||||||
AlertStatus.userPrompt, AlertSize.small,
|
AlertStatus.userPrompt, AlertSize.small,
|
||||||
Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 0.4)
|
Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 0.4)
|
||||||
@@ -322,7 +322,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
|||||||
|
|
||||||
EventName.steerTempUnavailableSilent: {
|
EventName.steerTempUnavailableSilent: {
|
||||||
ET.WARNING: Alert(
|
ET.WARNING: Alert(
|
||||||
"Steering Temporarily Unavailable",
|
"Steering Assist Temporarily Unavailable",
|
||||||
"",
|
"",
|
||||||
AlertStatus.userPrompt, AlertSize.small,
|
AlertStatus.userPrompt, AlertSize.small,
|
||||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 1.8),
|
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 1.8),
|
||||||
@@ -568,7 +568,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
EventName.steerTempUnavailable: {
|
EventName.steerTempUnavailable: {
|
||||||
ET.SOFT_DISABLE: soft_disable_alert("Steering Temporarily Unavailable"),
|
ET.SOFT_DISABLE: soft_disable_alert("Steering Assist Temporarily Unavailable"),
|
||||||
ET.NO_ENTRY: NoEntryAlert("Steering Temporarily Unavailable"),
|
ET.NO_ENTRY: NoEntryAlert("Steering Temporarily Unavailable"),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ class Plant:
|
|||||||
from opendbc.car.honda.values import CAR
|
from opendbc.car.honda.values import CAR
|
||||||
from opendbc.car.honda.interface import CarInterface
|
from opendbc.car.honda.interface import CarInterface
|
||||||
|
|
||||||
self.planner = LongitudinalPlanner(CarInterface.get_non_essential_params(CAR.HONDA_CIVIC), init_v=self.speed)
|
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
||||||
|
CP_SP = CarInterface.get_non_essential_params_sp(CP, CAR.HONDA_CIVIC)
|
||||||
|
self.planner = LongitudinalPlanner(CP, CP_SP, init_v=self.speed)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_time(self):
|
def current_time(self):
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
afcab1abb62b9d5678342956cced4712f44e909e
|
b508f43fb0481bce0859c9b6ab4f45ee690b8dab
|
||||||
@@ -42,6 +42,7 @@ sudo systemctl restart NetworkManager
|
|||||||
sudo systemctl disable ssh-param-watcher.path
|
sudo systemctl disable ssh-param-watcher.path
|
||||||
sudo systemctl disable ssh-param-watcher.service
|
sudo systemctl disable ssh-param-watcher.service
|
||||||
sudo mount -o ro,remount /
|
sudo mount -o ro,remount /
|
||||||
|
sudo systemctl stop power_monitor
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
if ! sudo systemctl is-active -q ssh; then
|
if ! sudo systemctl is-active -q ssh; then
|
||||||
@@ -54,7 +55,6 @@ while true; do
|
|||||||
# /data/ciui.py &
|
# /data/ciui.py &
|
||||||
#fi
|
#fi
|
||||||
|
|
||||||
awk '{print \$1}' /proc/uptime > /var/tmp/power_watchdog
|
|
||||||
sleep 5s
|
sleep 5s
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ CPU usage budget
|
|||||||
TEST_DURATION = 25
|
TEST_DURATION = 25
|
||||||
LOG_OFFSET = 8
|
LOG_OFFSET = 8
|
||||||
|
|
||||||
MAX_TOTAL_CPU = 300. # total for all 8 cores
|
MAX_TOTAL_CPU = 350. # total for all 8 cores
|
||||||
PROCS = {
|
PROCS = {
|
||||||
# Baseline CPU usage by process
|
# Baseline CPU usage by process
|
||||||
"selfdrive.controls.controlsd": 16.0,
|
"selfdrive.controls.controlsd": 16.0,
|
||||||
@@ -42,7 +42,7 @@ PROCS = {
|
|||||||
"./encoderd": 13.0,
|
"./encoderd": 13.0,
|
||||||
"./camerad": 10.0,
|
"./camerad": 10.0,
|
||||||
"selfdrive.controls.plannerd": 8.0,
|
"selfdrive.controls.plannerd": 8.0,
|
||||||
"./ui": 18.0,
|
"selfdrive.ui.ui": 40.0,
|
||||||
"system.sensord.sensord": 13.0,
|
"system.sensord.sensord": 13.0,
|
||||||
"selfdrive.controls.radard": 2.0,
|
"selfdrive.controls.radard": 2.0,
|
||||||
"selfdrive.modeld.modeld": 22.0,
|
"selfdrive.modeld.modeld": 22.0,
|
||||||
@@ -206,7 +206,8 @@ class TestOnroad:
|
|||||||
result += "-------------- UI Draw Timing ------------------\n"
|
result += "-------------- UI Draw Timing ------------------\n"
|
||||||
result += "------------------------------------------------\n"
|
result += "------------------------------------------------\n"
|
||||||
|
|
||||||
ts = self.ts['uiDebug']['drawTimeMillis']
|
# skip first few frames -- connecting to vipc
|
||||||
|
ts = self.ts['uiDebug']['drawTimeMillis'][15:]
|
||||||
result += f"min {min(ts):.2f}ms\n"
|
result += f"min {min(ts):.2f}ms\n"
|
||||||
result += f"max {max(ts):.2f}ms\n"
|
result += f"max {max(ts):.2f}ms\n"
|
||||||
result += f"std {np.std(ts):.2f}ms\n"
|
result += f"std {np.std(ts):.2f}ms\n"
|
||||||
@@ -215,7 +216,7 @@ class TestOnroad:
|
|||||||
print(result)
|
print(result)
|
||||||
|
|
||||||
assert max(ts) < 250.
|
assert max(ts) < 250.
|
||||||
assert np.mean(ts) < 10.
|
assert np.mean(ts) < 20. # TODO: ~6-11ms, increase consistency
|
||||||
#self.assertLess(np.std(ts), 5.)
|
#self.assertLess(np.std(ts), 5.)
|
||||||
|
|
||||||
# some slow frames are expected since camerad/modeld can preempt ui
|
# some slow frames are expected since camerad/modeld can preempt ui
|
||||||
@@ -285,7 +286,7 @@ class TestOnroad:
|
|||||||
|
|
||||||
# check for big leaks. note that memory usage is
|
# check for big leaks. note that memory usage is
|
||||||
# expected to go up while the MSGQ buffers fill up
|
# expected to go up while the MSGQ buffers fill up
|
||||||
assert np.average(mems) <= 65, "Average memory usage above 65%"
|
assert np.average(mems) <= 85, "Average memory usage above 85%"
|
||||||
assert np.max(np.diff(mems)) <= 4, "Max memory increase too high"
|
assert np.max(np.diff(mems)) <= 4, "Max memory increase too high"
|
||||||
assert np.average(np.diff(mems)) <= 1, "Average memory increase too high"
|
assert np.average(np.diff(mems)) <= 1, "Average memory increase too high"
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1 @@
|
|||||||
moc_*
|
|
||||||
*.moc
|
|
||||||
|
|
||||||
translations/main_test_en.*
|
|
||||||
|
|
||||||
ui
|
|
||||||
mui
|
|
||||||
watch3
|
|
||||||
installer/installers/*
|
installer/installers/*
|
||||||
qt/setup/setup
|
|
||||||
qt/setup/reset
|
|
||||||
qt/setup/wifi
|
|
||||||
qt/setup/updater
|
|
||||||
translations/alerts_generated.h
|
|
||||||
|
|||||||
+27
-65
@@ -1,75 +1,37 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
import json
|
import json
|
||||||
Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations')
|
from pathlib import Path
|
||||||
|
Import('env', 'arch', 'common')
|
||||||
|
|
||||||
base_libs = [common, messaging, visionipc, transformations,
|
# build the fonts
|
||||||
'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"]
|
generator = File("#selfdrive/assets/fonts/process.py")
|
||||||
|
source_files = Glob("#selfdrive/assets/fonts/*.ttf") + Glob("#selfdrive/assets/fonts/*.otf")
|
||||||
|
output_files = [
|
||||||
|
(f.abspath.split('.')[0] + ".fnt", f.abspath.split('.')[0] + ".png")
|
||||||
|
for f in source_files
|
||||||
|
if "NotoColor" not in f.name
|
||||||
|
]
|
||||||
|
env.Command(
|
||||||
|
target=output_files,
|
||||||
|
source=[generator, source_files],
|
||||||
|
action=f"python3 {generator}",
|
||||||
|
)
|
||||||
|
|
||||||
if arch == 'larch64':
|
# compile gettext .po -> .mo translations
|
||||||
base_libs.append('EGL')
|
|
||||||
|
|
||||||
if arch == "Darwin":
|
|
||||||
del base_libs[base_libs.index('OpenCL')]
|
|
||||||
qt_env['FRAMEWORKS'] += ['OpenCL']
|
|
||||||
|
|
||||||
sp_widgets_src = []
|
|
||||||
sp_qt_src = []
|
|
||||||
sp_qt_util = []
|
|
||||||
if not GetOption('stock_ui'):
|
|
||||||
SConscript(['sunnypilot/SConscript'])
|
|
||||||
Import('sp_widgets_src', 'sp_qt_src', 'sp_qt_util')
|
|
||||||
|
|
||||||
# FIXME: remove this once we're on 5.15 (24.04)
|
|
||||||
qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"]
|
|
||||||
|
|
||||||
qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"] + sp_qt_util, LIBS=base_libs)
|
|
||||||
widgets_src = ["qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/prime_state.cc",
|
|
||||||
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
|
|
||||||
"qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc",
|
|
||||||
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc",
|
|
||||||
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + sp_widgets_src
|
|
||||||
|
|
||||||
widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs)
|
|
||||||
Export('widgets')
|
|
||||||
qt_libs = [widgets, qt_util] + base_libs
|
|
||||||
|
|
||||||
qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc",
|
|
||||||
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", "qt/offroad/offroad_home.cc",
|
|
||||||
"qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc",
|
|
||||||
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/offroad/firehose.cc",
|
|
||||||
"qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc",
|
|
||||||
"qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] + sp_qt_src
|
|
||||||
|
|
||||||
# build translation files
|
|
||||||
with open(File("translations/languages.json").abspath) as f:
|
with open(File("translations/languages.json").abspath) as f:
|
||||||
languages = json.loads(f.read())
|
languages = json.loads(f.read())
|
||||||
translation_sources = [f"#selfdrive/ui/translations/{l}.ts" for l in languages.values()]
|
|
||||||
translation_targets = [src.replace(".ts", ".qm") for src in translation_sources]
|
|
||||||
lrelease_bin = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease'
|
|
||||||
|
|
||||||
lrelease = qt_env.Command(translation_targets, translation_sources, f"{lrelease_bin} $SOURCES")
|
po_sources = [f"#selfdrive/ui/translations/app_{l}.po" for l in languages.values()]
|
||||||
qt_env.NoClean(translation_sources)
|
po_sources = [src for src in po_sources if os.path.exists(File(src).abspath)]
|
||||||
qt_env.Precious(translation_sources)
|
mo_targets = [src.replace(".po", ".mo") for src in po_sources]
|
||||||
|
mo_build = []
|
||||||
|
for src, tgt in zip(po_sources, mo_targets):
|
||||||
|
mo_build.append(env.Command(tgt, src, "msgfmt -o $TARGET $SOURCE"))
|
||||||
|
mo_alias = env.Alias('mo', mo_build)
|
||||||
|
env.AlwaysBuild(mo_alias)
|
||||||
|
|
||||||
# create qrc file for compiled translations to include with assets
|
|
||||||
translations_assets_src = "#selfdrive/assets/translations_assets.qrc"
|
|
||||||
with open(File(translations_assets_src).abspath, 'w') as f:
|
|
||||||
f.write('<!DOCTYPE RCC><RCC version="1.0">\n<qresource>\n')
|
|
||||||
f.write('\n'.join([f'<file alias="{l}">../ui/translations/{l}.qm</file>' for l in languages.values()]))
|
|
||||||
f.write('\n</qresource>\n</RCC>')
|
|
||||||
|
|
||||||
# build assets
|
|
||||||
assets = "#selfdrive/assets/assets.cc"
|
|
||||||
assets_src = "#selfdrive/assets/assets.qrc"
|
|
||||||
qt_env.Command(assets, [assets_src, translations_assets_src], f"rcc $SOURCES -o $TARGET")
|
|
||||||
qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, translations_assets_src, "#selfdrive/assets/assets.o"]) + [lrelease])
|
|
||||||
asset_obj = qt_env.Object("assets", assets)
|
|
||||||
|
|
||||||
# build main UI
|
|
||||||
qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs)
|
|
||||||
if GetOption('extras'):
|
if GetOption('extras'):
|
||||||
qt_src.remove("main.cc") # replaced by test_runner
|
|
||||||
qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs)
|
|
||||||
|
|
||||||
# build installers
|
# build installers
|
||||||
if arch != "Darwin":
|
if arch != "Darwin":
|
||||||
raylib_env = env.Clone()
|
raylib_env = env.Clone()
|
||||||
@@ -78,7 +40,7 @@ if GetOption('extras'):
|
|||||||
|
|
||||||
raylib_libs = common + ["raylib"]
|
raylib_libs = common + ["raylib"]
|
||||||
if arch == "larch64":
|
if arch == "larch64":
|
||||||
raylib_libs += ["GLESv2", "wayland-client", "wayland-egl", "EGL"]
|
raylib_libs += ["GLESv2", "EGL", "gbm", "drm"]
|
||||||
else:
|
else:
|
||||||
raylib_libs += ["GL"]
|
raylib_libs += ["GL"]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
UI_BORDER_SIZE = 30
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "common/swaglog.h"
|
#include "common/swaglog.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
#include "system/hardware/hw.h"
|
||||||
#include "third_party/raylib/include/raylib.h"
|
#include "third_party/raylib/include/raylib.h"
|
||||||
|
|
||||||
int freshClone();
|
int freshClone();
|
||||||
@@ -38,6 +39,27 @@ extern const uint8_t inter_ttf_end[] asm("_binary_selfdrive_ui_installer_inter_a
|
|||||||
|
|
||||||
Font font;
|
Font font;
|
||||||
|
|
||||||
|
std::vector<std::string> tici_prebuilt_branches = {"release3", "release-tizi", "release3-staging", "nightly", "nightly-dev"};
|
||||||
|
std::string migrated_branch;
|
||||||
|
|
||||||
|
void branchMigration() {
|
||||||
|
migrated_branch = BRANCH_STR;
|
||||||
|
cereal::InitData::DeviceType device_type = Hardware::get_device_type();
|
||||||
|
if (device_type == cereal::InitData::DeviceType::TICI) {
|
||||||
|
if (std::find(tici_prebuilt_branches.begin(), tici_prebuilt_branches.end(), BRANCH_STR) != tici_prebuilt_branches.end()) {
|
||||||
|
migrated_branch = "release-tici";
|
||||||
|
} else if (BRANCH_STR == "master") {
|
||||||
|
migrated_branch = "master-tici";
|
||||||
|
}
|
||||||
|
} else if (device_type == cereal::InitData::DeviceType::TIZI) {
|
||||||
|
if (BRANCH_STR == "release3") {
|
||||||
|
migrated_branch = "release-tizi";
|
||||||
|
} else if (BRANCH_STR == "release3-staging") {
|
||||||
|
migrated_branch = "release-tizi-staging";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void run(const char* cmd) {
|
void run(const char* cmd) {
|
||||||
int err = std::system(cmd);
|
int err = std::system(cmd);
|
||||||
assert(err == 0);
|
assert(err == 0);
|
||||||
@@ -87,7 +109,7 @@ int doInstall() {
|
|||||||
int freshClone() {
|
int freshClone() {
|
||||||
LOGD("Doing fresh clone");
|
LOGD("Doing fresh clone");
|
||||||
std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1",
|
std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1",
|
||||||
GIT_URL.c_str(), BRANCH_STR.c_str(), TMP_INSTALL_PATH);
|
GIT_URL.c_str(), migrated_branch.c_str(), TMP_INSTALL_PATH);
|
||||||
return executeGitCommand(cmd);
|
return executeGitCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,11 +117,11 @@ int cachedFetch(const std::string &cache) {
|
|||||||
LOGD("Fetching with cache: %s", cache.c_str());
|
LOGD("Fetching with cache: %s", cache.c_str());
|
||||||
|
|
||||||
run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str());
|
run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str());
|
||||||
run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, BRANCH_STR.c_str()).c_str());
|
run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, migrated_branch.c_str()).c_str());
|
||||||
|
|
||||||
renderProgress(10);
|
renderProgress(10);
|
||||||
|
|
||||||
return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, BRANCH_STR.c_str()));
|
return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, migrated_branch.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
int executeGitCommand(const std::string &cmd) {
|
int executeGitCommand(const std::string &cmd) {
|
||||||
@@ -142,8 +164,8 @@ void cloneFinished(int exitCode) {
|
|||||||
// ensure correct branch is checked out
|
// ensure correct branch is checked out
|
||||||
int err = chdir(TMP_INSTALL_PATH);
|
int err = chdir(TMP_INSTALL_PATH);
|
||||||
assert(err == 0);
|
assert(err == 0);
|
||||||
run(("git checkout " + BRANCH_STR).c_str());
|
run(("git checkout " + migrated_branch).c_str());
|
||||||
run(("git reset --hard origin/" + BRANCH_STR).c_str());
|
run(("git reset --hard origin/" + migrated_branch).c_str());
|
||||||
run("git submodule update --init");
|
run("git submodule update --init");
|
||||||
|
|
||||||
// move into place
|
// move into place
|
||||||
@@ -193,6 +215,8 @@ int main(int argc, char *argv[]) {
|
|||||||
font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, FONT_SIZE, NULL, 0);
|
font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, FONT_SIZE, NULL, 0);
|
||||||
SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR);
|
SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR);
|
||||||
|
|
||||||
|
branchMigration();
|
||||||
|
|
||||||
if (util::file_exists(CONTINUE_PATH)) {
|
if (util::file_exists(CONTINUE_PATH)) {
|
||||||
finishInstall();
|
finishInstall();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user