pull/30209/head
Yassine 2 years ago
commit f2707ee02f
  1. 10
      .github/pull_request_template.md
  2. 2
      .github/workflows/badges.yaml
  3. 62
      .github/workflows/docs.yaml
  4. 2
      .github/workflows/labeler.yaml
  5. 2
      .github/workflows/prebuilt.yaml
  6. 2
      .github/workflows/release.yaml
  7. 2
      .github/workflows/repo-maintenance.yaml
  8. 26
      .github/workflows/selfdrive_tests.yaml
  9. 24
      .github/workflows/tools_tests.yaml
  10. 1
      .gitignore
  11. 4
      .pre-commit-config.yaml
  12. 4
      Jenkinsfile
  13. 2
      cereal
  14. 4
      docs/CARS.md
  15. 42
      docs/docker/Dockerfile
  16. 15
      docs/docker/nginx.conf
  17. 2
      panda
  18. 1243
      poetry.lock
  19. 4
      pyproject.toml
  20. 18
      release/files_common
  21. 2
      selfdrive/car/chrysler/interface.py
  22. 1
      selfdrive/car/chrysler/values.py
  23. 14
      selfdrive/car/hyundai/values.py
  24. 3
      selfdrive/car/subaru/values.py
  25. 7
      selfdrive/car/toyota/carstate.py
  26. 9
      selfdrive/car/toyota/values.py
  27. 2
      selfdrive/car/volkswagen/values.py
  28. 36
      selfdrive/controls/controlsd.py
  29. 4
      selfdrive/controls/lib/drive_helpers.py
  30. 18
      selfdrive/controls/lib/events.py
  31. 6
      selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py
  32. 12
      selfdrive/controls/lib/longcontrol.py
  33. 16
      selfdrive/controls/lib/longitudinal_planner.py
  34. 21
      selfdrive/controls/plannerd.py
  35. 50
      selfdrive/locationd/helpers.py
  36. 5
      selfdrive/locationd/laikad.py
  37. 8
      selfdrive/locationd/paramsd.py
  38. 60
      selfdrive/locationd/torqued.py
  39. 12
      selfdrive/modeld/SConscript
  40. 77
      selfdrive/modeld/constants.py
  41. 186
      selfdrive/modeld/fill_model_msg.py
  42. 29
      selfdrive/modeld/get_model_metadata.py
  43. 87
      selfdrive/modeld/modeld.py
  44. 6
      selfdrive/modeld/models/README.md
  45. 358
      selfdrive/modeld/models/driving.cc
  46. 273
      selfdrive/modeld/models/driving.h
  47. 26
      selfdrive/modeld/models/driving.pxd
  48. 54
      selfdrive/modeld/models/driving_pyx.pyx
  49. 8
      selfdrive/modeld/navmodeld.py
  50. 101
      selfdrive/modeld/parse_model_outputs.py
  51. 32
      selfdrive/modeld/thneed/lib.py
  52. 13
      selfdrive/monitoring/dmonitoringd.py
  53. 8
      selfdrive/navd/navd.py
  54. 3
      selfdrive/test/docker_common.sh
  55. 8
      selfdrive/test/longitudinal_maneuvers/plant.py
  56. 4
      selfdrive/test/process_replay/README.md
  57. 70
      selfdrive/test/process_replay/compare_logs.py
  58. 8
      selfdrive/test/process_replay/model_replay.py
  59. 2
      selfdrive/test/process_replay/model_replay_ref_commit
  60. 15
      selfdrive/test/process_replay/process_replay.py
  61. 4
      selfdrive/test/process_replay/regen.py
  62. 8
      selfdrive/test/process_replay/test_processes.py
  63. 26
      selfdrive/test/test_onroad.py
  64. 1
      selfdrive/ui/soundd/soundd
  65. 1
      selfdrive/ui/spinner
  66. 1
      selfdrive/ui/text
  67. 1
      selfdrive/ui/ui
  68. 2
      selfdrive/ui/ui.h
  69. 115
      system/hardware/tici/esim.py
  70. 2
      system/hardware/tici/tests/test_power_draw.py
  71. 3
      system/loggerd/logger.cc
  72. 11
      system/micd.py
  73. 2
      system/sensord/.gitignore
  74. 2
      system/sensord/SConscript
  75. 4
      system/sensord/sensord
  76. 2
      system/sensord/tests/test_sensord.py
  77. 2
      tinygrad_repo
  78. 1
      tools/cabana/binaryview.cc
  79. 7
      tools/cabana/chart/chartswidget.cc
  80. 1
      tools/cabana/chart/chartswidget.h
  81. 6
      tools/cabana/detailwidget.cc
  82. 16
      tools/cabana/videowidget.cc
  83. 6
      tools/cabana/videowidget.h
  84. 4
      tools/install_python_dependencies.sh
  85. 51
      tools/lib/framereader.py
  86. 7
      tools/lib/logreader.py
  87. 4
      tools/replay/ui.py
  88. 1
      tools/sim/Dockerfile.sim

@ -1,5 +1,15 @@
<!-- Please copy and paste the relevant template --> <!-- Please copy and paste the relevant template -->
<!--- ***** Template: Fingerprint *****
**Car**
Which car (make, model, year) this fingerprint is for
**Route**
A route with the fingerprint
-->
<!--- ***** Template: Car bug fix ***** <!--- ***** Template: Car bug fix *****
**Description** [](A description of the bug and the fix. Also link any relevant issues.) **Description** [](A description of the bug and the fix. Also link any relevant issues.)

@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
if: github.repository == 'commaai/openpilot' if: github.repository == 'commaai/openpilot'
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry

@ -0,0 +1,62 @@
name: docs
on:
push:
branches:
- master
pull_request:
concurrency:
group: ${{ 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
env:
BASE_IMAGE: openpilot-base
BUILD: selfdrive/test/docker_build.sh base
RUN: docker run --shm-size 1G -v $GITHUB_WORKSPACE:/tmp/openpilot -w /tmp/openpilot -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/sh -c
jobs:
docs:
name: build docs
runs-on: ubuntu-20.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
- name: Build openpilot
run: |
${{ env.RUN }} "scons -j$(nproc)"
- name: Build docs
run: |
${{ env.RUN }} "apt update && apt install -y doxygen && cd docs && make html"
- uses: actions/checkout@v4
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
with:
path: openpilot-docs
ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }}
repository: commaai/openpilot-docs
- name: Push
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
run: |
set -x
source release/identity.sh
cd openpilot-docs
git checkout --orphan tmp
git rm -rf --cached .
cp -r ../build/docs/html/ docs/
touch docs/.nojekyll
git add -f .
git commit -m "build docs"
# docs live in different repo to not bloat openpilot's full clone size
git push -f origin gh-pages

@ -9,7 +9,7 @@ jobs:
pull-requests: write pull-requests: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: false submodules: false
- uses: actions/labeler@v5.0.0-alpha.1 - uses: actions/labeler@v5.0.0-alpha.1

@ -24,7 +24,7 @@ jobs:
wait-interval: 30 wait-interval: 30
running-workflow-name: 'build prebuilt' running-workflow-name: 'build prebuilt'
check-regexp: ^((?!.*(build master-ci).*).)*$ check-regexp: ^((?!.*(build master-ci).*).)*$
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- run: git lfs pull - run: git lfs pull

@ -27,7 +27,7 @@ jobs:
wait-interval: 30 wait-interval: 30
running-workflow-name: 'build master-ci' running-workflow-name: 'build master-ci'
check-regexp: ^((?!.*(build prebuilt).*).)*$ check-regexp: ^((?!.*(build prebuilt).*).)*$
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
fetch-depth: 0 fetch-depth: 0

@ -12,7 +12,7 @@ jobs:
container: container:
image: ghcr.io/commaai/openpilot-base:latest image: ghcr.io/commaai/openpilot-base:latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: poetry lock - name: poetry lock
run: | run: |
pip install poetry pip install poetry

@ -35,7 +35,7 @@ jobs:
env: env:
STRIPPED_DIR: /tmp/releasepilot STRIPPED_DIR: /tmp/releasepilot
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- run: git lfs pull - run: git lfs pull
@ -77,7 +77,7 @@ jobs:
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && '["x86_64", "aarch64"]' || '["x86_64"]' ) }} (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && '["x86_64", "aarch64"]' || '["x86_64"]' ) }}
runs-on: ${{ (matrix.arch == 'aarch64') && 'buildjet-2vcpu-ubuntu-2204-arm' || 'ubuntu-20.04' }} runs-on: ${{ (matrix.arch == 'aarch64') && 'buildjet-2vcpu-ubuntu-2204-arm' || 'ubuntu-20.04' }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
@ -97,7 +97,7 @@ jobs:
runs-on: ${{ (matrix.arch == 'aarch64') && 'buildjet-2vcpu-ubuntu-2204-arm' || 'ubuntu-20.04' }} runs-on: ${{ (matrix.arch == 'aarch64') && 'buildjet-2vcpu-ubuntu-2204-arm' || 'ubuntu-20.04' }}
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot' if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Setup to push to repo - name: Setup to push to repo
@ -120,7 +120,7 @@ jobs:
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot' if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
needs: [docker_push] needs: [docker_push]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: false submodules: false
- name: Setup docker - name: Setup docker
@ -135,7 +135,7 @@ jobs:
name: static analysis name: static analysis
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
@ -147,7 +147,7 @@ jobs:
name: valgrind name: valgrind
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
@ -165,7 +165,7 @@ jobs:
name: unit tests name: unit tests
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
@ -190,7 +190,7 @@ jobs:
((github.event_name != 'pull_request') || ((github.event_name != 'pull_request') ||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'buildjet-8vcpu-ubuntu-2004' || 'ubuntu-20.04' }} (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'buildjet-8vcpu-ubuntu-2004' || 'ubuntu-20.04' }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
@ -232,7 +232,7 @@ jobs:
name: regen name: regen
runs-on: 'ubuntu-20.04' runs-on: 'ubuntu-20.04'
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
@ -259,7 +259,7 @@ jobs:
name: model tests name: model tests
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
@ -295,7 +295,7 @@ jobs:
matrix: matrix:
job: [0, 1, 2, 3, 4] job: [0, 1, 2, 3, 4]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
@ -323,7 +323,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
ref: ${{ github.event.pull_request.base.ref }} ref: ${{ github.event.pull_request.base.ref }}
@ -333,7 +333,7 @@ jobs:
run: | run: |
${{ env.RUN }} "scons -j$(nproc) && python selfdrive/debug/dump_car_info.py --path /tmp/openpilot_cache/base_car_info" ${{ env.RUN }} "scons -j$(nproc) && python selfdrive/debug/dump_car_info.py --path /tmp/openpilot_cache/base_car_info"
sudo chown -R $USER:$USER ${{ github.workspace }} sudo chown -R $USER:$USER ${{ github.workspace }}
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
path: current path: current

@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
timeout-minutes: 45 timeout-minutes: 45
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
@ -48,7 +48,7 @@ jobs:
if: github.repository == 'commaai/openpilot' if: github.repository == 'commaai/openpilot'
timeout-minutes: 45 timeout-minutes: 45
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
@ -63,29 +63,11 @@ jobs:
run: | run: |
selfdrive/test/docker_build.sh sim selfdrive/test/docker_build.sh sim
docs:
name: build docs
runs-on: ubuntu-20.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
- name: Setup to push to repo
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
run: |
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
$DOCKER_LOGIN
- name: Build and push docs image
run: |
selfdrive/test/docker_build.sh docs
devcontainer: devcontainer:
name: devcontainer name: devcontainer
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry

1
.gitignore vendored

@ -79,6 +79,7 @@ comma*.sh
selfdrive/modeld/thneed/compile selfdrive/modeld/thneed/compile
selfdrive/modeld/models/*.thneed selfdrive/modeld/models/*.thneed
selfdrive/modeld/models/*.pkl
*.bz2 *.bz2

@ -5,7 +5,7 @@ repos:
- id: check-hooks-apply - id: check-hooks-apply
- id: check-useless-excludes - id: check-useless-excludes
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0 rev: v4.5.0
hooks: hooks:
- id: check-ast - id: check-ast
exclude: '^(third_party)/' exclude: '^(third_party)/'
@ -41,7 +41,7 @@ repos:
args: ['--explicit-package-bases'] args: ['--explicit-package-bases']
exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)' exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)'
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.292 rev: v0.1.0
hooks: hooks:
- id: ruff - id: ruff
exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)' exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)'

4
Jenkinsfile vendored

@ -21,6 +21,8 @@ source ~/.bash_profile
if [ -f /TICI ]; then if [ -f /TICI ]; then
source /etc/profile source /etc/profile
rm -rf ~/.commacache
if ! systemctl is-active --quiet systemd-resolved; then if ! systemctl is-active --quiet systemd-resolved; then
echo "restarting resolved" echo "restarting resolved"
sudo systemctl start systemd-resolved sudo systemctl start systemd-resolved
@ -154,7 +156,7 @@ node {
["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR ./build_devel.sh"], ["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR ./build_devel.sh"],
["build openpilot", "cd selfdrive/manager && ./build.py"], ["build openpilot", "cd selfdrive/manager && ./build.py"],
["check dirty", "release/check-dirty.sh"], ["check dirty", "release/check-dirty.sh"],
["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"], ["onroad tests", "pytest selfdrive/test/test_onroad.py -s"],
["time to onroad", "cd selfdrive/test/ && pytest test_time_to_onroad.py"], ["time to onroad", "cd selfdrive/test/ && pytest test_time_to_onroad.py"],
]) ])
}, },

@ -1 +1 @@
Subproject commit bcdbfcfdeb4bf59eae484e9b2c726c8b41c87350 Subproject commit 5dcf486c57f2e75cd6e50d2adda02bf776d51c8f

@ -120,7 +120,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Kia|EV6 (without HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=EV6 (without HDA II) 2022-23">Buy Here</a></sub></details>|| |Kia|EV6 (without HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=EV6 (without HDA II) 2022-23">Buy Here</a></sub></details>||
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Forte 2019-21">Buy Here</a></sub></details>|| |Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Forte 2019-21">Buy Here</a></sub></details>||
|Kia|Forte 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Forte 2023">Buy Here</a></sub></details>|| |Kia|Forte 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Forte 2023">Buy Here</a></sub></details>||
|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=K5 2021-22">Buy Here</a></sub></details>|| |Kia|K5 2021-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=K5 2021-24">Buy Here</a></sub></details>||
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=K5 Hybrid 2020-22">Buy Here</a></sub></details>|| |Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=K5 Hybrid 2020-22">Buy Here</a></sub></details>||
|Kia|K8 Hybrid (with HDA II) 2023[<sup>6</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>|| |Kia|K8 Hybrid (with HDA II) 2023[<sup>6</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>||
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Niro EV 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Niro EV 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
@ -139,7 +139,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sorento 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sorento 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sorento 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sorento 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Sorento 2021-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sorento 2021-23">Buy Here</a></sub></details>|| |Kia|Sorento 2021-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sorento 2021-23">Buy Here</a></sub></details>||
|Kia|Sorento Hybrid 2023[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sorento Hybrid 2023">Buy Here</a></sub></details>|| |Kia|Sorento Hybrid 2021-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sorento Hybrid 2021-23">Buy Here</a></sub></details>||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>|| |Kia|Sorento Plug-in Hybrid 2022-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|Kia|Sportage 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sportage 2023">Buy Here</a></sub></details>|| |Kia|Sportage 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sportage 2023">Buy Here</a></sub></details>||
|Kia|Sportage Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sportage Hybrid 2023">Buy Here</a></sub></details>|| |Kia|Sportage Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<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.html?make=Kia&model=Sportage Hybrid 2023">Buy Here</a></sub></details>||

@ -1,42 +0,0 @@
FROM ghcr.io/commaai/openpilot-base:latest
ENV PYTHONUNBUFFERED 1
ENV OPENPILOT_PATH /tmp/openpilot
ENV PYTHONPATH ${OPENPILOT_PATH}:${PYTHONPATH}
ENV POETRY_VIRUALENVS_CREATE false
RUN mkdir -p ${OPENPILOT_PATH}
WORKDIR ${OPENPILOT_PATH}
COPY SConstruct ${OPENPILOT_PATH}
COPY ./openpilot ${OPENPILOT_PATH}/openpilot
COPY ./body ${OPENPILOT_PATH}/body
COPY ./third_party ${OPENPILOT_PATH}/third_party
COPY ./site_scons ${OPENPILOT_PATH}/site_scons
COPY ./laika_repo ${OPENPILOT_PATH}/laika_repo
RUN ln -s ${OPENPILOT_PATH}/laika_repo/laika/ ${OPENPILOT_PATH}/laika
COPY ./rednose ${OPENPILOT_PATH}/rednose
COPY ./rednose_repo ${OPENPILOT_PATH}/rednose_repo
COPY ./tools ${OPENPILOT_PATH}/tools
COPY ./release ${OPENPILOT_PATH}/release
COPY ./common ${OPENPILOT_PATH}/common
COPY ./opendbc ${OPENPILOT_PATH}/opendbc
COPY ./cereal ${OPENPILOT_PATH}/cereal
COPY ./panda ${OPENPILOT_PATH}/panda
COPY ./selfdrive ${OPENPILOT_PATH}/selfdrive
COPY ./system ${OPENPILOT_PATH}/system
COPY ./*.md ${OPENPILOT_PATH}/
RUN --mount=type=bind,source=.ci_cache/scons_cache,target=/tmp/scons_cache,rw scons -j$(nproc) --cache-readonly
RUN apt update && apt install doxygen -y
COPY ./docs ${OPENPILOT_PATH}/docs
RUN git init .
WORKDIR ${OPENPILOT_PATH}/docs
RUN make html
FROM nginx:1.21
COPY --from=0 /tmp/openpilot/build/docs/html /usr/share/nginx/html
COPY ./docs/docker/nginx.conf /etc/nginx/conf.d/default.conf

@ -1,15 +0,0 @@
server {
listen 80;
listen [::]:80;
server_name localhost;
gzip on;
gzip_types text/html text/plain text/css text/xml text/javascript application/javascript application/x-javascript;
gzip_min_length 1024;
gzip_vary on;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ /index.html;
}
}

@ -1 +1 @@
Subproject commit 6bf6ba773ea4174c702ac9c4af4e8a6fd3cef655 Subproject commit 549fa32fc7b0354ebbb48bae846bff380eab9446

1243
poetry.lock generated

File diff suppressed because it is too large Load Diff

@ -162,7 +162,9 @@ types-pycurl = "*"
types-PyYAML = "*" types-PyYAML = "*"
types-requests = "*" types-requests = "*"
types-tabulate = "*" types-tabulate = "*"
pyqt5 = { version = "*", markers = "platform_machine == 'x86_64'" } # no aarch64 wheels for macOS/linux
# this is only pinned since 5.15.11 is broken
pyqt5 = { version = "==5.15.2", markers = "platform_machine == 'x86_64'" } # no aarch64 wheels for macOS/linux
[tool.poetry.group.carla] [tool.poetry.group.carla]
optional = true optional = true

@ -246,6 +246,7 @@ selfdrive/locationd/models/gnss_helpers.py
selfdrive/locationd/torqued.py selfdrive/locationd/torqued.py
selfdrive/locationd/calibrationd.py selfdrive/locationd/calibrationd.py
selfdrive/locationd/helpers.py
system/logcatd/.gitignore system/logcatd/.gitignore
system/logcatd/SConscript system/logcatd/SConscript
@ -284,7 +285,6 @@ system/sensord/SConscript
system/sensord/sensors_qcom2.cc system/sensord/sensors_qcom2.cc
system/sensord/sensors/*.cc system/sensord/sensors/*.cc
system/sensord/sensors/*.h system/sensord/sensors/*.h
system/sensord/sensord
system/sensord/pigeond.py system/sensord/pigeond.py
selfdrive/thermald/thermald.py selfdrive/thermald/thermald.py
@ -358,6 +358,9 @@ selfdrive/modeld/.gitignore
selfdrive/modeld/__init__.py selfdrive/modeld/__init__.py
selfdrive/modeld/SConscript selfdrive/modeld/SConscript
selfdrive/modeld/modeld.py selfdrive/modeld/modeld.py
selfdrive/modeld/parse_model_outputs.py
selfdrive/modeld/fill_model_msg.py
selfdrive/modeld/get_model_metadata.py
selfdrive/modeld/navmodeld.py selfdrive/modeld/navmodeld.py
selfdrive/modeld/dmonitoringmodeld.py selfdrive/modeld/dmonitoringmodeld.py
selfdrive/modeld/constants.py selfdrive/modeld/constants.py
@ -370,8 +373,6 @@ selfdrive/modeld/models/*.pyx
selfdrive/modeld/models/commonmodel.cc selfdrive/modeld/models/commonmodel.cc
selfdrive/modeld/models/commonmodel.h selfdrive/modeld/models/commonmodel.h
selfdrive/modeld/models/driving.cc
selfdrive/modeld/models/driving.h
selfdrive/modeld/models/supercombo.onnx selfdrive/modeld/models/supercombo.onnx
selfdrive/modeld/models/dmonitoring_model_q.dlc selfdrive/modeld/models/dmonitoring_model_q.dlc
@ -591,9 +592,16 @@ tinygrad_repo/extra/onnx.py
tinygrad_repo/extra/onnx_ops.py tinygrad_repo/extra/onnx_ops.py
tinygrad_repo/extra/thneed.py tinygrad_repo/extra/thneed.py
tinygrad_repo/extra/utils.py tinygrad_repo/extra/utils.py
tinygrad_repo/tinygrad/codegen/ast.py tinygrad_repo/tinygrad/codegen/kernel.py
tinygrad_repo/tinygrad/codegen/gpu.py tinygrad_repo/tinygrad/codegen/linearizer.py
tinygrad_repo/tinygrad/codegen/optimizer.py
tinygrad_repo/tinygrad/features/image.py
tinygrad_repo/tinygrad/nn/* tinygrad_repo/tinygrad/nn/*
tinygrad_repo/tinygrad/renderer/cstyle.py
tinygrad_repo/tinygrad/renderer/opencl.py
tinygrad_repo/tinygrad/runtime/lib.py
tinygrad_repo/tinygrad/runtime/ops_cpu.py
tinygrad_repo/tinygrad/runtime/ops_disk.py
tinygrad_repo/tinygrad/runtime/ops_gpu.py tinygrad_repo/tinygrad/runtime/ops_gpu.py
tinygrad_repo/tinygrad/shape/* tinygrad_repo/tinygrad/shape/*
tinygrad_repo/tinygrad/*.py tinygrad_repo/tinygrad/*.py

@ -64,7 +64,7 @@ class CarInterface(CarInterfaceBase):
ret.mass = 2493. ret.mass = 2493.
ret.minSteerSpeed = 14.5 ret.minSteerSpeed = 14.5
# Older EPS FW allow steer to zero # Older EPS FW allow steer to zero
if any(fw.ecu == 'eps' and fw.fwVersion[:4] <= b"6831" for fw in car_fw): if any(fw.ecu == 'eps' and b"68" < fw.fwVersion[:4] <= b"6831" for fw in car_fw):
ret.minSteerSpeed = 0. ret.minSteerSpeed = 0.
elif candidate == CAR.RAM_HD: elif candidate == CAR.RAM_HD:

@ -272,6 +272,7 @@ FW_VERSIONS = {
], ],
(Ecu.eps, 0x75A, None): [ (Ecu.eps, 0x75A, None): [
b'21590101AA', b'21590101AA',
b'21590101AB',
b'68273275AF', b'68273275AF',
b'68273275AG', b'68273275AG',
b'68273275AH', b'68273275AH',

@ -225,7 +225,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
HyundaiCarInfo("Kia Forte 2019-21", car_parts=CarParts.common([CarHarness.hyundai_g])), HyundaiCarInfo("Kia Forte 2019-21", car_parts=CarParts.common([CarHarness.hyundai_g])),
HyundaiCarInfo("Kia Forte 2023", car_parts=CarParts.common([CarHarness.hyundai_e])), HyundaiCarInfo("Kia Forte 2023", car_parts=CarParts.common([CarHarness.hyundai_e])),
], ],
CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-22", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-24", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_K5_HEV_2020: HyundaiCarInfo("Kia K5 Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_K5_HEV_2020: HyundaiCarInfo("Kia K5 Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_K8_HEV_1ST_GEN: HyundaiCarInfo("Kia K8 Hybrid (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])), CAR.KIA_K8_HEV_1ST_GEN: HyundaiCarInfo("Kia K8 Hybrid (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
CAR.KIA_NIRO_EV: [ CAR.KIA_NIRO_EV: [
@ -257,7 +257,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])), HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])),
], ],
CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2021-23", car_parts=CarParts.common([CarHarness.hyundai_k])), CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2021-23", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.KIA_SORENTO_HEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Hybrid 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_SORENTO_HEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Hybrid 2021-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])), CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])),
CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0",
@ -1407,6 +1407,7 @@ FW_VERSIONS = {
b'\xf1\x8799110L2100\xf1\x00DL3_ SCC FHCUP 1.00 1.03 99110-L2100 ', b'\xf1\x8799110L2100\xf1\x00DL3_ SCC FHCUP 1.00 1.03 99110-L2100 ',
b'\xf1\x00DL3_ SCC F-CUP 1.00 1.03 99110-L2100 ', b'\xf1\x00DL3_ SCC F-CUP 1.00 1.03 99110-L2100 ',
b'\xf1\x00DL3_ SCC FHCUP 1.00 1.03 99110-L2100 ', b'\xf1\x00DL3_ SCC FHCUP 1.00 1.03 99110-L2100 ',
b'\xf1\x00DL3_ SCC FHCUP 1.00 1.04 99110-L2100 ',
], ],
(Ecu.eps, 0x7D4, None): [ (Ecu.eps, 0x7D4, None): [
b'\xf1\x8756310-L3110\xf1\000DL3 MDPS C 1.00 1.01 56310-L3110 4DLAC101', b'\xf1\x8756310-L3110\xf1\000DL3 MDPS C 1.00 1.01 56310-L3110 4DLAC101',
@ -1414,11 +1415,13 @@ FW_VERSIONS = {
b'\xf1\x8757700-L3000\xf1\x00DL3 MDPS R 1.00 1.02 57700-L3000 4DLAP102', b'\xf1\x8757700-L3000\xf1\x00DL3 MDPS R 1.00 1.02 57700-L3000 4DLAP102',
b'\xf1\x00DL3 MDPS C 1.00 1.01 56310-L3220 4DLAC101', b'\xf1\x00DL3 MDPS C 1.00 1.01 56310-L3220 4DLAC101',
b'\xf1\x00DL3 MDPS C 1.00 1.02 56310-L2220 4DLDC102', b'\xf1\x00DL3 MDPS C 1.00 1.02 56310-L2220 4DLDC102',
b'\xf1\x00DL3 MDPS C 1.00 1.02 56310L3220\x00 4DLAC102',
], ],
(Ecu.fwdCamera, 0x7C4, None): [ (Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00DL3 MFC AT USA LHD 1.00 1.03 99210-L3000 200915', b'\xf1\x00DL3 MFC AT USA LHD 1.00 1.03 99210-L3000 200915',
b'\xf1\x00DL3 MFC AT USA LHD 1.00 1.04 99210-L3000 210208', b'\xf1\x00DL3 MFC AT USA LHD 1.00 1.04 99210-L3000 210208',
b'\xf1\x00DL3 MFC AT KOR LHD 1.00 1.04 99210-L2000 210527', b'\xf1\x00DL3 MFC AT KOR LHD 1.00 1.04 99210-L2000 210527',
b'\xf1\x00DL3 MFC AT USA LHD 1.00 1.05 99210-L3000 211222',
], ],
(Ecu.abs, 0x7D1, None): [ (Ecu.abs, 0x7D1, None): [
b'\xf1\000DL ESC \006 101 \004\002 58910-L3200', b'\xf1\000DL ESC \006 101 \004\002 58910-L3200',
@ -1427,12 +1430,14 @@ FW_VERSIONS = {
b'\xf1\x8758910-L3600\xf1\x00DL ESC \x03 100 \x08\x02 58910-L3600', b'\xf1\x8758910-L3600\xf1\x00DL ESC \x03 100 \x08\x02 58910-L3600',
b'\xf1\x00DL ESC \t 100 \x06\x02 58910-L3800', b'\xf1\x00DL ESC \t 100 \x06\x02 58910-L3800',
b'\xf1\x00DL ESC \x01 104 \x07\x12 58910-L2200', b'\xf1\x00DL ESC \x01 104 \x07\x12 58910-L2200',
b'\xf1\x00DL ESC \x06 103"\x08\x06 58910-L3200',
], ],
(Ecu.engine, 0x7E0, None): [ (Ecu.engine, 0x7E0, None): [
b'\xf1\x87391212MKT0', b'\xf1\x87391212MKT0',
b'\xf1\x87391212MKV0', b'\xf1\x87391212MKV0',
b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x82DLDWN5TMDCXXXJ1B', b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x82DLDWN5TMDCXXXJ1B',
b'\xf1\x81HM6M2_0a0_DQ0', b'\xf1\x81HM6M2_0a0_DQ0',
b'\xf1\x87391212MKT3',
], ],
(Ecu.transmission, 0x7E1, None): [ (Ecu.transmission, 0x7E1, None): [
b'\xf1\000bcsh8p54 U913\000\000\000\000\000\000TDL2T16NB1ia\v\xb8', b'\xf1\000bcsh8p54 U913\000\000\000\000\000\000TDL2T16NB1ia\v\xb8',
@ -1442,6 +1447,7 @@ FW_VERSIONS = {
b'\xf1\x87954A02N300\x00\x00\x00\x00\x00\xf1\x81T02730A1 \xf1\x00T02601BL T02730A1 WDL3T25XXX730NS2b\x1f\xb8%', b'\xf1\x87954A02N300\x00\x00\x00\x00\x00\xf1\x81T02730A1 \xf1\x00T02601BL T02730A1 WDL3T25XXX730NS2b\x1f\xb8%',
b'\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00TDL4T16NB05\x94t\x18', b'\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00TDL4T16NB05\x94t\x18',
b'\xf1\x00HT6TA261BLHT6TAB00A1SDL0C20KS0\x00\x00\x00\x00\x00\x00\\\x9f\xa5\x15', b'\xf1\x00HT6TA261BLHT6TAB00A1SDL0C20KS0\x00\x00\x00\x00\x00\x00\\\x9f\xa5\x15',
b'\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00TDL2T16NB2.\x13\xf6\xed',
], ],
}, },
CAR.KIA_K5_HEV_2020: { CAR.KIA_K5_HEV_2020: {
@ -1498,6 +1504,7 @@ FW_VERSIONS = {
b'\xf1\x8758520-K4010\xf1\x00OS IEB \x03 101 \x11\x13 58520-K4010', b'\xf1\x8758520-K4010\xf1\x00OS IEB \x03 101 \x11\x13 58520-K4010',
b'\xf1\x00OS IEB \r 102"\x05\x16 58520-K4010', b'\xf1\x00OS IEB \r 102"\x05\x16 58520-K4010',
b'\xf1\x00OS IEB \x02 102"\x05\x16 58520-K4010', b'\xf1\x00OS IEB \x02 102"\x05\x16 58520-K4010',
b'\xf1\x00OS IEB \x03 102"\x05\x16 58520-K4010',
], ],
(Ecu.fwdCamera, 0x7C4, None): [ (Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00OSP LKA AT CND LHD 1.00 1.02 99211-J9110 802', b'\xf1\x00OSP LKA AT CND LHD 1.00 1.02 99211-J9110 802',
@ -1511,6 +1518,7 @@ FW_VERSIONS = {
b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4260\x00 4OEPC102', b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4260\x00 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310/K4970 4OEPC102', b'\xf1\x00OSP MDPS C 1.00 1.02 56310/K4970 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310/K4271 4OEPC102', b'\xf1\x00OSP MDPS C 1.00 1.02 56310/K4271 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310-K4271 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4971\x00 4OEPC102', b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4971\x00 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4261\x00 4OEPC102', b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4261\x00 4OEPC102',
], ],
@ -2031,9 +2039,11 @@ FW_VERSIONS = {
(Ecu.fwdCamera, 0x7c4, None): [ (Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MQ4HMFC AT KOR LHD 1.00 1.12 99210-P2000 230331', b'\xf1\x00MQ4HMFC AT KOR LHD 1.00 1.12 99210-P2000 230331',
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.11 99210-P2000 211217', b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.11 99210-P2000 211217',
b'\xf1\x00MQ4HMFC AT KOR LHD 1.00 1.04 99210-P2000 200330',
], ],
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQhe SCC FHCUP 1.00 1.07 99110-P4000 ', b'\xf1\x00MQhe SCC FHCUP 1.00 1.07 99110-P4000 ',
b'\xf1\x00MQhe SCC FHCUP 1.00 1.04 99110-P4000 ',
], ],
}, },
CAR.KIA_K8_HEV_1ST_GEN: { CAR.KIA_K8_HEV_1ST_GEN: {

@ -324,6 +324,7 @@ FW_VERSIONS = {
b'\x00\x00eq\x1f@ "', b'\x00\x00eq\x1f@ "',
b'\x00\x00eq\x00\x00\x00\x00', b'\x00\x00eq\x00\x00\x00\x00',
b'\x00\x00e\x8f\x00\x00\x00\x00', b'\x00\x00e\x8f\x00\x00\x00\x00',
b'\x00\x00e\x92\x00\x00\x00\x00',
b'\x00\x00e\xa4\x00\x00\x00\x00', b'\x00\x00e\xa4\x00\x00\x00\x00',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
@ -332,6 +333,7 @@ FW_VERSIONS = {
b'\xca!`0\a', b'\xca!`0\a',
b'\xcc\"f0\a', b'\xcc\"f0\a',
b'\xcc!fp\a', b'\xcc!fp\a',
b'\xcc!`p\x07',
b'\xca!f@\x07', b'\xca!f@\x07',
b'\xca!fp\x07', b'\xca!fp\x07',
b'\xf3"f@\x07', b'\xf3"f@\x07',
@ -344,6 +346,7 @@ FW_VERSIONS = {
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xe6\xf5\004\000\000', b'\xe6\xf5\004\000\000',
b'\xe6\xf5$\000\000', b'\xe6\xf5$\000\000',
b'\xe7\xf5\x04\x00\x00',
b'\xe7\xf6B0\000', b'\xe7\xf6B0\000',
b'\xe7\xf5D0\000', b'\xe7\xf5D0\000',
b'\xf1\x00\xd7\x10@', b'\xf1\x00\xd7\x10@',

@ -74,6 +74,7 @@ class CarState(CarStateBase):
ret.standstill = ret.vEgoRaw == 0 ret.standstill = ret.vEgoRaw == 0
ret.steeringAngleDeg = cp.vl["STEER_ANGLE_SENSOR"]["STEER_ANGLE"] + cp.vl["STEER_ANGLE_SENSOR"]["STEER_FRACTION"] ret.steeringAngleDeg = cp.vl["STEER_ANGLE_SENSOR"]["STEER_ANGLE"] + cp.vl["STEER_ANGLE_SENSOR"]["STEER_FRACTION"]
ret.steeringRateDeg = cp.vl["STEER_ANGLE_SENSOR"]["STEER_RATE"]
torque_sensor_angle_deg = cp.vl["STEER_TORQUE_SENSOR"]["STEER_ANGLE"] torque_sensor_angle_deg = cp.vl["STEER_TORQUE_SENSOR"]["STEER_ANGLE"]
# On some cars, the angle measurement is non-zero while initializing # On some cars, the angle measurement is non-zero while initializing
@ -81,16 +82,14 @@ class CarState(CarStateBase):
self.accurate_steer_angle_seen = True self.accurate_steer_angle_seen = True
if self.accurate_steer_angle_seen: if self.accurate_steer_angle_seen:
# Offset seems to be invalid for large steering angles # Offset seems to be invalid for large steering angles and high angle rates
if abs(ret.steeringAngleDeg) < 90 and cp.can_valid: if abs(ret.steeringAngleDeg) < 90 and abs(ret.steeringRateDeg) < 100 and cp.can_valid:
self.angle_offset.update(torque_sensor_angle_deg - ret.steeringAngleDeg) self.angle_offset.update(torque_sensor_angle_deg - ret.steeringAngleDeg)
if self.angle_offset.initialized: if self.angle_offset.initialized:
ret.steeringAngleOffsetDeg = self.angle_offset.x ret.steeringAngleOffsetDeg = self.angle_offset.x
ret.steeringAngleDeg = torque_sensor_angle_deg - self.angle_offset.x ret.steeringAngleDeg = torque_sensor_angle_deg - self.angle_offset.x
ret.steeringRateDeg = cp.vl["STEER_ANGLE_SENSOR"]["STEER_RATE"]
can_gear = int(cp.vl["GEAR_PACKET"]["GEAR"]) can_gear = int(cp.vl["GEAR_PACKET"]["GEAR"])
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None)) ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None))
ret.leftBlinker = cp.vl["BLINKERS_STATE"]["TURN_SIGNALS"] == 1 ret.leftBlinker = cp.vl["BLINKERS_STATE"]["TURN_SIGNALS"] == 1

@ -346,7 +346,7 @@ FW_CHUNK_LEN = 16
PLATFORM_CODE_ECUS = [Ecu.fwdCamera, Ecu.abs, Ecu.eps] PLATFORM_CODE_ECUS = [Ecu.fwdCamera, Ecu.abs, Ecu.eps]
# These platforms have at least one platform code for all ECUs shared with another platform. # These platforms have at least one platform code for all ECUs shared with another platform.
FUZZY_EXCLUDED_PLATFORMS = {CAR.LEXUS_ES_TSS2, CAR.LEXUS_RX_TSS2} FUZZY_EXCLUDED_PLATFORMS: set[CAR] = set()
# Some ECUs that use KWP2000 have their FW versions on non-standard data identifiers. # Some ECUs that use KWP2000 have their FW versions on non-standard data identifiers.
# Toyota diagnostic software first gets the supported data ids, then queries them one by one. # Toyota diagnostic software first gets the supported data ids, then queries them one by one.
@ -1005,6 +1005,7 @@ FW_VERSIONS = {
b'\x0230A10000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0230A10000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x0230A11000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0230A11000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x0230ZN4000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0230ZN4000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x0230ZN5000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x03312K7000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00', b'\x03312K7000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00',
b'\x03312M3000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00', b'\x03312M3000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00',
b'\x03312N6000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00', b'\x03312N6000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00',
@ -1060,6 +1061,7 @@ FW_VERSIONS = {
b'\x01F152612B91\x00\x00\x00\x00\x00\x00', b'\x01F152612B91\x00\x00\x00\x00\x00\x00',
b'\x01F15260A070\x00\x00\x00\x00\x00\x00', b'\x01F15260A070\x00\x00\x00\x00\x00\x00',
b'\x01F152676250\x00\x00\x00\x00\x00\x00', b'\x01F152676250\x00\x00\x00\x00\x00\x00',
b'\x01F152602470\x00\x00\x00\x00\x00\x00',
b'F152612590\x00\x00\x00\x00\x00\x00', b'F152612590\x00\x00\x00\x00\x00\x00',
b'F152612691\x00\x00\x00\x00\x00\x00', b'F152612691\x00\x00\x00\x00\x00\x00',
b'F152612692\x00\x00\x00\x00\x00\x00', b'F152612692\x00\x00\x00\x00\x00\x00',
@ -1782,7 +1784,6 @@ FW_VERSIONS = {
CAR.LEXUS_ES_TSS2: { CAR.LEXUS_ES_TSS2: {
(Ecu.engine, 0x700, None): [ (Ecu.engine, 0x700, None): [
b'\x018966306U6000\x00\x00\x00\x00', b'\x018966306U6000\x00\x00\x00\x00',
b'\x01896630EC9100\x00\x00\x00\x00',
b'\x018966333T5000\x00\x00\x00\x00', b'\x018966333T5000\x00\x00\x00\x00',
b'\x018966333T5100\x00\x00\x00\x00', b'\x018966333T5100\x00\x00\x00\x00',
b'\x018966333X6000\x00\x00\x00\x00', b'\x018966333X6000\x00\x00\x00\x00',
@ -1800,7 +1801,6 @@ FW_VERSIONS = {
b'\x01F152606281\x00\x00\x00\x00\x00\x00', b'\x01F152606281\x00\x00\x00\x00\x00\x00',
b'\x01F152606340\x00\x00\x00\x00\x00\x00', b'\x01F152606340\x00\x00\x00\x00\x00\x00',
b'\x01F152606461\x00\x00\x00\x00\x00\x00', b'\x01F152606461\x00\x00\x00\x00\x00\x00',
b'\x01F15260E031\x00\x00\x00\x00\x00\x00',
b'\x01F15260646200\x00\x00\x00\x00', b'\x01F15260646200\x00\x00\x00\x00',
b'F152633423\x00\x00\x00\x00\x00\x00', b'F152633423\x00\x00\x00\x00\x00\x00',
b'F152633680\x00\x00\x00\x00\x00\x00', b'F152633680\x00\x00\x00\x00\x00\x00',
@ -1813,7 +1813,6 @@ FW_VERSIONS = {
b'8965B33590\x00\x00\x00\x00\x00\x00', b'8965B33590\x00\x00\x00\x00\x00\x00',
b'8965B33690\x00\x00\x00\x00\x00\x00', b'8965B33690\x00\x00\x00\x00\x00\x00',
b'8965B33721\x00\x00\x00\x00\x00\x00', b'8965B33721\x00\x00\x00\x00\x00\x00',
b'8965B48271\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.fwdRadar, 0x750, 0xf): [ (Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301100\x00\x00\x00\x00', b'\x018821F3301100\x00\x00\x00\x00',
@ -1829,7 +1828,6 @@ FW_VERSIONS = {
b'\x028646F3304100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F3304100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00',
b'\x028646F3304300\x00\x00\x00\x008646G2601500\x00\x00\x00\x00', b'\x028646F3304300\x00\x00\x00\x008646G2601500\x00\x00\x00\x00',
b'\x028646F3309100\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', b'\x028646F3309100\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
b'\x028646F4810200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
b'\x028646F0610000\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', b'\x028646F0610000\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
b'\x028646F3303100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F3303100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F3304200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', b'\x028646F3304200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
@ -2084,6 +2082,7 @@ FW_VERSIONS = {
b'\x01896630EA9000\x00\x00\x00\x00', b'\x01896630EA9000\x00\x00\x00\x00',
b'\x01896630EB0000\x00\x00\x00\x00', b'\x01896630EB0000\x00\x00\x00\x00',
b'\x01896630EC9000\x00\x00\x00\x00', b'\x01896630EC9000\x00\x00\x00\x00',
b'\x01896630EC9100\x00\x00\x00\x00',
b'\x01896630ED0000\x00\x00\x00\x00', b'\x01896630ED0000\x00\x00\x00\x00',
b'\x01896630ED0100\x00\x00\x00\x00', b'\x01896630ED0100\x00\x00\x00\x00',
b'\x01896630ED6000\x00\x00\x00\x00', b'\x01896630ED6000\x00\x00\x00\x00',

@ -717,12 +717,14 @@ FW_VERSIONS = {
CAR.PASSAT_NMS: { CAR.PASSAT_NMS: {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x8706K906016C \xf1\x899609', b'\xf1\x8706K906016C \xf1\x899609',
b'\xf1\x8706K906016E \xf1\x899830',
b'\xf1\x8706K906016G \xf1\x891124', b'\xf1\x8706K906016G \xf1\x891124',
b'\xf1\x8706K906071BJ\xf1\x894891', b'\xf1\x8706K906071BJ\xf1\x894891',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158AB\xf1\x893318', b'\xf1\x8709G927158AB\xf1\x893318',
b'\xf1\x8709G927158BD\xf1\x893121', b'\xf1\x8709G927158BD\xf1\x893121',
b'\xf1\x8709G927158DK\xf1\x893594',
b'\xf1\x8709G927158FQ\xf1\x893745', b'\xf1\x8709G927158FQ\xf1\x893745',
], ],
(Ecu.srs, 0x715, None): [ (Ecu.srs, 0x715, None): [

@ -56,39 +56,33 @@ ENABLED_STATES = (State.preEnabled, *ACTIVE_STATES)
class Controls: class Controls:
def __init__(self, sm=None, pm=None, can_sock=None, CI=None): def __init__(self, CI=None):
config_realtime_process(4, Priority.CTRL_HIGH) config_realtime_process(4, Priority.CTRL_HIGH)
# Ensure the current branch is cached, otherwise the first iteration of controlsd lags # Ensure the current branch is cached, otherwise the first iteration of controlsd lags
self.branch = get_short_branch("") self.branch = get_short_branch("")
# Setup sockets # Setup sockets
self.pm = pm self.pm = messaging.PubMaster(['sendcan', 'controlsState', 'carState',
if self.pm is None: 'carControl', 'carEvents', 'carParams'])
self.pm = messaging.PubMaster(['sendcan', 'controlsState', 'carState',
'carControl', 'carEvents', 'carParams'])
self.sensor_packets = ["accelerometer", "gyroscope"] self.sensor_packets = ["accelerometer", "gyroscope"]
self.camera_packets = ["roadCameraState", "driverCameraState", "wideRoadCameraState"] self.camera_packets = ["roadCameraState", "driverCameraState", "wideRoadCameraState"]
self.can_sock = can_sock can_timeout = None if os.environ.get('NO_CAN_TIMEOUT', False) else 20
if can_sock is None: self.can_sock = messaging.sub_sock('can', timeout=can_timeout)
can_timeout = None if os.environ.get('NO_CAN_TIMEOUT', False) else 20
self.can_sock = messaging.sub_sock('can', timeout=can_timeout)
self.log_sock = messaging.sub_sock('androidLog') self.log_sock = messaging.sub_sock('androidLog')
self.params = Params() self.params = Params()
self.sm = sm ignore = self.sensor_packets + ['testJoystick']
if self.sm is None: if SIMULATION:
ignore = self.sensor_packets + ['testJoystick'] ignore += ['driverCameraState', 'managerState']
if SIMULATION: self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration',
ignore += ['driverCameraState', 'managerState'] 'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman',
self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman', 'testJoystick'] + self.camera_packets + self.sensor_packets,
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', ignore_alive=ignore, ignore_avg_freq=['radarState', 'testJoystick'])
'testJoystick'] + self.camera_packets + self.sensor_packets,
ignore_alive=ignore, ignore_avg_freq=['radarState', 'testJoystick'])
if CI is None: if CI is None:
# wait for one pandaState and one CAN packet # wait for one pandaState and one CAN packet
@ -879,8 +873,8 @@ class Controls:
self.prof.display() self.prof.display()
def main(sm=None, pm=None, logcan=None): def main():
controls = Controls(sm, pm, logcan) controls = Controls()
controls.controlsd_thread() controls.controlsd_thread()

@ -4,7 +4,7 @@ from cereal import car, log
from openpilot.common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip, interp from openpilot.common.numpy_fast import clip, interp
from openpilot.common.realtime import DT_MDL from openpilot.common.realtime import DT_MDL
from openpilot.selfdrive.modeld.constants import T_IDXS from openpilot.selfdrive.modeld.constants import ModelConstants
# WARNING: this value was determined based on the model's training distribution, # WARNING: this value was determined based on the model's training distribution,
# model predictions above this speed can be unpredictable # model predictions above this speed can be unpredictable
@ -177,7 +177,7 @@ def get_lag_adjusted_curvature(CP, v_ego, psis, curvatures, curvature_rates):
# in high delay cases some corrections never even get commanded. So just use # in high delay cases some corrections never even get commanded. So just use
# psi to calculate a simple linearization of desired curvature # psi to calculate a simple linearization of desired curvature
current_curvature_desired = curvatures[0] current_curvature_desired = curvatures[0]
psi = interp(delay, T_IDXS[:CONTROL_N], psis) psi = interp(delay, ModelConstants.T_IDXS[:CONTROL_N], psis)
average_curvature_desired = psi / (v_ego * delay) average_curvature_desired = psi / (v_ego * delay)
desired_curvature = 2 * average_curvature_desired - current_curvature_desired desired_curvature = 2 * average_curvature_desired - current_curvature_desired

@ -970,10 +970,10 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
if __name__ == '__main__': if __name__ == '__main__':
# print all alerts by type and priority # print all alerts by type and priority
from cereal.services import SERVICE_LIST from cereal.services import SERVICE_LIST
from collections import defaultdict, OrderedDict from collections import defaultdict
event_names = {v: k for k, v in EventName.schema.enumerants.items()} event_names = {v: k for k, v in EventName.schema.enumerants.items()}
alerts_by_type: Dict[str, Dict[int, List[str]]] = defaultdict(lambda: defaultdict(list)) alerts_by_type: Dict[str, Dict[Priority, List[str]]] = defaultdict(lambda: defaultdict(list))
CP = car.CarParams.new_message() CP = car.CarParams.new_message()
CS = car.CarState.new_message() CS = car.CarState.new_message()
@ -983,18 +983,14 @@ if __name__ == '__main__':
for et, alert in alerts.items(): for et, alert in alerts.items():
if callable(alert): if callable(alert):
alert = alert(CP, CS, sm, False, 1) alert = alert(CP, CS, sm, False, 1)
priority = alert.priority alerts_by_type[et][alert.priority].append(event_names[i])
alerts_by_type[et][priority].append(event_names[i])
all_alerts = {} all_alerts: Dict[str, List[tuple[Priority, List[str]]]] = {}
for et, priority_alerts in alerts_by_type.items(): for et, priority_alerts in alerts_by_type.items():
all_alerts[et] = OrderedDict([ all_alerts[et] = sorted(priority_alerts.items(), key=lambda x: x[0], reverse=True)
(str(priority), l)
for priority, l in sorted(priority_alerts.items(), key=lambda x: -int(x[0]))
])
for status, evs in sorted(all_alerts.items(), key=lambda x: x[0]): for status, evs in sorted(all_alerts.items(), key=lambda x: x[0]):
print(f"**** {status} ****") print(f"**** {status} ****")
for p, alert_list in evs.items(): for p, alert_list in evs:
print(f" {p}:") print(f" {repr(p)}:")
print(" ", ', '.join(alert_list), "\n") print(" ", ', '.join(alert_list), "\n")

@ -5,7 +5,7 @@ import numpy as np
from casadi import SX, vertcat, sin, cos from casadi import SX, vertcat, sin, cos
# WARNING: imports outside of constants will not trigger a rebuild # WARNING: imports outside of constants will not trigger a rebuild
from openpilot.selfdrive.modeld.constants import T_IDXS from openpilot.selfdrive.modeld.constants import ModelConstants
if __name__ == '__main__': # generating code if __name__ == '__main__': # generating code
from openpilot.third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver from openpilot.third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver
@ -66,7 +66,7 @@ def gen_lat_ocp():
ocp = AcadosOcp() ocp = AcadosOcp()
ocp.model = gen_lat_model() ocp.model = gen_lat_model()
Tf = np.array(T_IDXS)[N] Tf = np.array(ModelConstants.T_IDXS)[N]
# set dimensions # set dimensions
ocp.dims.N = N ocp.dims.N = N
@ -122,7 +122,7 @@ def gen_lat_ocp():
# set prediction horizon # set prediction horizon
ocp.solver_options.tf = Tf ocp.solver_options.tf = Tf
ocp.solver_options.shooting_nodes = np.array(T_IDXS)[:N+1] ocp.solver_options.shooting_nodes = np.array(ModelConstants.T_IDXS)[:N+1]
ocp.code_export_directory = EXPORT_DIR ocp.code_export_directory = EXPORT_DIR
return ocp return ocp

@ -3,7 +3,7 @@ from openpilot.common.numpy_fast import clip, interp
from openpilot.common.realtime import DT_CTRL from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, apply_deadzone from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, apply_deadzone
from openpilot.selfdrive.controls.lib.pid import PIDController from openpilot.selfdrive.controls.lib.pid import PIDController
from openpilot.selfdrive.modeld.constants import T_IDXS from openpilot.selfdrive.modeld.constants import ModelConstants
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = car.CarControl.Actuators.LongControlState
@ -70,19 +70,19 @@ class LongControl:
# Interp control trajectory # Interp control trajectory
speeds = long_plan.speeds speeds = long_plan.speeds
if len(speeds) == CONTROL_N: if len(speeds) == CONTROL_N:
v_target_now = interp(t_since_plan, T_IDXS[:CONTROL_N], speeds) v_target_now = interp(t_since_plan, ModelConstants.T_IDXS[:CONTROL_N], speeds)
a_target_now = interp(t_since_plan, T_IDXS[:CONTROL_N], long_plan.accels) a_target_now = interp(t_since_plan, ModelConstants.T_IDXS[:CONTROL_N], long_plan.accels)
v_target_lower = interp(self.CP.longitudinalActuatorDelayLowerBound + t_since_plan, T_IDXS[:CONTROL_N], speeds) v_target_lower = interp(self.CP.longitudinalActuatorDelayLowerBound + t_since_plan, ModelConstants.T_IDXS[:CONTROL_N], speeds)
a_target_lower = 2 * (v_target_lower - v_target_now) / self.CP.longitudinalActuatorDelayLowerBound - a_target_now a_target_lower = 2 * (v_target_lower - v_target_now) / self.CP.longitudinalActuatorDelayLowerBound - a_target_now
v_target_upper = interp(self.CP.longitudinalActuatorDelayUpperBound + t_since_plan, T_IDXS[:CONTROL_N], speeds) v_target_upper = interp(self.CP.longitudinalActuatorDelayUpperBound + t_since_plan, ModelConstants.T_IDXS[:CONTROL_N], speeds)
a_target_upper = 2 * (v_target_upper - v_target_now) / self.CP.longitudinalActuatorDelayUpperBound - a_target_now a_target_upper = 2 * (v_target_upper - v_target_now) / self.CP.longitudinalActuatorDelayUpperBound - a_target_now
v_target = min(v_target_lower, v_target_upper) v_target = min(v_target_lower, v_target_upper)
a_target = min(a_target_lower, a_target_upper) a_target = min(a_target_lower, a_target_upper)
v_target_1sec = interp(self.CP.longitudinalActuatorDelayUpperBound + t_since_plan + 1.0, T_IDXS[:CONTROL_N], speeds) v_target_1sec = interp(self.CP.longitudinalActuatorDelayUpperBound + t_since_plan + 1.0, ModelConstants.T_IDXS[:CONTROL_N], speeds)
else: else:
v_target = 0.0 v_target = 0.0
v_target_now = 0.0 v_target_now = 0.0

@ -9,7 +9,7 @@ import cereal.messaging as messaging
from openpilot.common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.realtime import DT_MDL from openpilot.common.realtime import DT_MDL
from openpilot.selfdrive.modeld.constants import T_IDXS from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX
from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc
@ -76,9 +76,9 @@ class LongitudinalPlanner:
if (len(model_msg.position.x) == 33 and if (len(model_msg.position.x) == 33 and
len(model_msg.velocity.x) == 33 and len(model_msg.velocity.x) == 33 and
len(model_msg.acceleration.x) == 33): len(model_msg.acceleration.x) == 33):
x = np.interp(T_IDXS_MPC, T_IDXS, model_msg.position.x) - model_error * T_IDXS_MPC x = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.position.x) - model_error * T_IDXS_MPC
v = np.interp(T_IDXS_MPC, T_IDXS, model_msg.velocity.x) - model_error v = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.velocity.x) - model_error
a = np.interp(T_IDXS_MPC, T_IDXS, model_msg.acceleration.x) a = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.acceleration.x)
j = np.zeros(len(T_IDXS_MPC)) j = np.zeros(len(T_IDXS_MPC))
else: else:
x = np.zeros(len(T_IDXS_MPC)) x = np.zeros(len(T_IDXS_MPC))
@ -135,11 +135,11 @@ class LongitudinalPlanner:
x, v, a, j = self.parse_model(sm['modelV2'], self.v_model_error) x, v, a, j = self.parse_model(sm['modelV2'], self.v_model_error)
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, personality=self.personality) self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, personality=self.personality)
self.v_desired_trajectory_full = np.interp(T_IDXS, T_IDXS_MPC, self.mpc.v_solution) self.v_desired_trajectory_full = np.interp(ModelConstants.T_IDXS, T_IDXS_MPC, self.mpc.v_solution)
self.a_desired_trajectory_full = np.interp(T_IDXS, T_IDXS_MPC, self.mpc.a_solution) self.a_desired_trajectory_full = np.interp(ModelConstants.T_IDXS, T_IDXS_MPC, self.mpc.a_solution)
self.v_desired_trajectory = self.v_desired_trajectory_full[:CONTROL_N] self.v_desired_trajectory = self.v_desired_trajectory_full[:CONTROL_N]
self.a_desired_trajectory = self.a_desired_trajectory_full[:CONTROL_N] self.a_desired_trajectory = self.a_desired_trajectory_full[:CONTROL_N]
self.j_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC[:-1], self.mpc.j_solution) self.j_desired_trajectory = np.interp(ModelConstants.T_IDXS[:CONTROL_N], T_IDXS_MPC[:-1], self.mpc.j_solution)
# TODO counter is only needed because radar is glitchy, remove once radar is gone # TODO counter is only needed because radar is glitchy, remove once radar is gone
self.fcw = self.mpc.crash_cnt > 2 and not sm['carState'].standstill self.fcw = self.mpc.crash_cnt > 2 and not sm['carState'].standstill
@ -148,7 +148,7 @@ class LongitudinalPlanner:
# Interpolate 0.05 seconds and save as starting point for next iteration # Interpolate 0.05 seconds and save as starting point for next iteration
a_prev = self.a_desired a_prev = self.a_desired
self.a_desired = float(interp(DT_MDL, T_IDXS[:CONTROL_N], self.a_desired_trajectory)) self.a_desired = float(interp(DT_MDL, ModelConstants.T_IDXS[:CONTROL_N], self.a_desired_trajectory))
self.v_desired_filter.x = self.v_desired_filter.x + DT_MDL * (self.a_desired + a_prev) / 2.0 self.v_desired_filter.x = self.v_desired_filter.x + DT_MDL * (self.a_desired + a_prev) / 2.0
def publish(self, sm, pm): def publish(self, sm, pm):

@ -5,7 +5,7 @@ from cereal import car
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
from openpilot.system.swaglog import cloudlog from openpilot.system.swaglog import cloudlog
from openpilot.selfdrive.modeld.constants import T_IDXS from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner
from openpilot.selfdrive.controls.lib.lateral_planner import LateralPlanner from openpilot.selfdrive.controls.lib.lateral_planner import LateralPlanner
import cereal.messaging as messaging import cereal.messaging as messaging
@ -14,8 +14,8 @@ def cumtrapz(x, t):
return np.concatenate([[0], np.cumsum(((x[0:-1] + x[1:])/2) * np.diff(t))]) return np.concatenate([[0], np.cumsum(((x[0:-1] + x[1:])/2) * np.diff(t))])
def publish_ui_plan(sm, pm, lateral_planner, longitudinal_planner): def publish_ui_plan(sm, pm, lateral_planner, longitudinal_planner):
plan_odo = cumtrapz(longitudinal_planner.v_desired_trajectory_full, T_IDXS) plan_odo = cumtrapz(longitudinal_planner.v_desired_trajectory_full, ModelConstants.T_IDXS)
model_odo = cumtrapz(lateral_planner.v_plan, T_IDXS) model_odo = cumtrapz(lateral_planner.v_plan, ModelConstants.T_IDXS)
ui_send = messaging.new_message('uiPlan') ui_send = messaging.new_message('uiPlan')
ui_send.valid = sm.all_checks(service_list=['carState', 'controlsState', 'modelV2']) ui_send.valid = sm.all_checks(service_list=['carState', 'controlsState', 'modelV2'])
@ -27,7 +27,7 @@ def publish_ui_plan(sm, pm, lateral_planner, longitudinal_planner):
uiPlan.accel = longitudinal_planner.a_desired_trajectory_full.tolist() uiPlan.accel = longitudinal_planner.a_desired_trajectory_full.tolist()
pm.send('uiPlan', ui_send) pm.send('uiPlan', ui_send)
def plannerd_thread(sm=None, pm=None): def plannerd_thread():
config_realtime_process(5, Priority.CTRL_LOW) config_realtime_process(5, Priority.CTRL_LOW)
cloudlog.info("plannerd is waiting for CarParams") cloudlog.info("plannerd is waiting for CarParams")
@ -41,12 +41,9 @@ def plannerd_thread(sm=None, pm=None):
longitudinal_planner = LongitudinalPlanner(CP) longitudinal_planner = LongitudinalPlanner(CP)
lateral_planner = LateralPlanner(CP, debug=debug_mode) lateral_planner = LateralPlanner(CP, debug=debug_mode)
if sm is None: pm = messaging.PubMaster(['longitudinalPlan', 'lateralPlan', 'uiPlan'])
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'radarState', 'modelV2'], sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'radarState', 'modelV2'],
poll=['radarState', 'modelV2'], ignore_avg_freq=['radarState']) poll=['radarState', 'modelV2'], ignore_avg_freq=['radarState'])
if pm is None:
pm = messaging.PubMaster(['longitudinalPlan', 'lateralPlan', 'uiPlan'])
while True: while True:
sm.update() sm.update()
@ -58,8 +55,8 @@ def plannerd_thread(sm=None, pm=None):
longitudinal_planner.publish(sm, pm) longitudinal_planner.publish(sm, pm)
publish_ui_plan(sm, pm, lateral_planner, longitudinal_planner) publish_ui_plan(sm, pm, lateral_planner, longitudinal_planner)
def main(sm=None, pm=None): def main():
plannerd_thread(sm, pm) plannerd_thread()
if __name__ == "__main__": if __name__ == "__main__":

@ -0,0 +1,50 @@
import numpy as np
from typing import List, Optional, Tuple, Any
class NPQueue:
def __init__(self, maxlen: int, rowsize: int) -> None:
self.maxlen = maxlen
self.arr = np.empty((0, rowsize))
def __len__(self) -> int:
return len(self.arr)
def append(self, pt: List[float]) -> None:
if len(self.arr) < self.maxlen:
self.arr = np.append(self.arr, [pt], axis=0)
else:
self.arr[:-1] = self.arr[1:]
self.arr[-1] = pt
class PointBuckets:
def __init__(self, x_bounds: List[Tuple[float, float]], min_points: List[float], min_points_total: int, points_per_bucket: int, rowsize: int) -> None:
self.x_bounds = x_bounds
self.buckets = {bounds: NPQueue(maxlen=points_per_bucket, rowsize=rowsize) for bounds in x_bounds}
self.buckets_min_points = dict(zip(x_bounds, min_points, strict=True))
self.min_points_total = min_points_total
def bucket_lengths(self) -> List[int]:
return [len(v) for v in self.buckets.values()]
def __len__(self) -> int:
return sum(self.bucket_lengths())
def is_valid(self) -> bool:
individual_buckets_valid = all(len(v) >= min_pts for v, min_pts in zip(self.buckets.values(), self.buckets_min_points.values(), strict=True))
total_points_valid = self.__len__() >= self.min_points_total
return individual_buckets_valid and total_points_valid
def add_point(self, x: float, y: float, bucket_val: float) -> None:
raise NotImplementedError
def get_points(self, num_points: Optional[int] = None) -> Any:
points = np.vstack([x.arr for x in self.buckets.values()])
if num_points is None:
return points
return points[np.random.choice(np.arange(len(points)), min(len(points), num_points), replace=False)]
def load_points(self, points: List[List[float]]) -> None:
for point in points:
self.add_point(*point)

@ -440,7 +440,7 @@ def clear_tmp_cache():
os.mkdir(Paths.download_cache_root()) os.mkdir(Paths.download_cache_root())
def main(sm=None, pm=None): def main():
#clear_tmp_cache() #clear_tmp_cache()
use_qcom = not Params().get_bool("UbloxAvailable") use_qcom = not Params().get_bool("UbloxAvailable")
@ -449,8 +449,7 @@ def main(sm=None, pm=None):
else: else:
raw_name = "ubloxGnss" raw_name = "ubloxGnss"
raw_gnss_sock = messaging.sub_sock(raw_name, conflate=False) raw_gnss_sock = messaging.sub_sock(raw_name, conflate=False)
if pm is None: pm = messaging.PubMaster(['gnssMeasurements'])
pm = messaging.PubMaster(['gnssMeasurements'])
# disable until set as main gps source, to better analyze startup time # disable until set as main gps source, to better analyze startup time
# TODO ensure low CPU usage before enabling # TODO ensure low CPU usage before enabling

@ -117,16 +117,14 @@ def check_valid_with_hysteresis(current_valid: bool, val: float, threshold: floa
return current_valid return current_valid
def main(sm=None, pm=None): def main():
config_realtime_process([0, 1, 2, 3], 5) config_realtime_process([0, 1, 2, 3], 5)
DEBUG = bool(int(os.getenv("DEBUG", "0"))) DEBUG = bool(int(os.getenv("DEBUG", "0")))
REPLAY = bool(int(os.getenv("REPLAY", "0"))) REPLAY = bool(int(os.getenv("REPLAY", "0")))
if sm is None: pm = messaging.PubMaster(['liveParameters'])
sm = messaging.SubMaster(['liveLocationKalman', 'carState'], poll=['liveLocationKalman']) sm = messaging.SubMaster(['liveLocationKalman', 'carState'], poll=['liveLocationKalman'])
if pm is None:
pm = messaging.PubMaster(['liveParameters'])
params_reader = Params() params_reader = Params()
# wait for stats about the car to come in from controls # wait for stats about the car to come in from controls

@ -12,6 +12,7 @@ from openpilot.common.realtime import config_realtime_process, DT_MDL
from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.system.swaglog import cloudlog from openpilot.system.swaglog import cloudlog
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.selfdrive.locationd.helpers import PointBuckets
HISTORY = 5 # secs HISTORY = 5 # secs
POINTS_PER_BUCKET = 1500 POINTS_PER_BUCKET = 1500
@ -43,55 +44,13 @@ def slope2rot(slope):
return np.array([[cos, -sin], [sin, cos]]) return np.array([[cos, -sin], [sin, cos]])
class NPQueue: class TorqueBuckets(PointBuckets):
def __init__(self, maxlen, rowsize):
self.maxlen = maxlen
self.arr = np.empty((0, rowsize))
def __len__(self):
return len(self.arr)
def append(self, pt):
if len(self.arr) < self.maxlen:
self.arr = np.append(self.arr, [pt], axis=0)
else:
self.arr[:-1] = self.arr[1:]
self.arr[-1] = pt
class PointBuckets:
def __init__(self, x_bounds, min_points, min_points_total):
self.x_bounds = x_bounds
self.buckets = {bounds: NPQueue(maxlen=POINTS_PER_BUCKET, rowsize=3) for bounds in x_bounds}
self.buckets_min_points = dict(zip(x_bounds, min_points, strict=True))
self.min_points_total = min_points_total
def bucket_lengths(self):
return [len(v) for v in self.buckets.values()]
def __len__(self):
return sum(self.bucket_lengths())
def is_valid(self):
return all(len(v) >= min_pts for v, min_pts in zip(self.buckets.values(), self.buckets_min_points.values(), strict=True)) \
and (self.__len__() >= self.min_points_total)
def add_point(self, x, y): def add_point(self, x, y):
for bound_min, bound_max in self.x_bounds: for bound_min, bound_max in self.x_bounds:
if (x >= bound_min) and (x < bound_max): if (x >= bound_min) and (x < bound_max):
self.buckets[(bound_min, bound_max)].append([x, 1.0, y]) self.buckets[(bound_min, bound_max)].append([x, 1.0, y])
break break
def get_points(self, num_points=None):
points = np.vstack([x.arr for x in self.buckets.values()])
if num_points is None:
return points
return points[np.random.choice(np.arange(len(points)), min(len(points), num_points), replace=False)]
def load_points(self, points):
for x, y in points:
self.add_point(x, y)
class TorqueEstimator: class TorqueEstimator:
def __init__(self, CP, decimated=False): def __init__(self, CP, decimated=False):
@ -175,7 +134,11 @@ class TorqueEstimator:
self.resets += 1.0 self.resets += 1.0
self.decay = MIN_FILTER_DECAY self.decay = MIN_FILTER_DECAY
self.raw_points = defaultdict(lambda: deque(maxlen=self.hist_len)) self.raw_points = defaultdict(lambda: deque(maxlen=self.hist_len))
self.filtered_points = PointBuckets(x_bounds=STEER_BUCKET_BOUNDS, min_points=self.min_bucket_points, min_points_total=self.min_points_total) self.filtered_points = TorqueBuckets(x_bounds=STEER_BUCKET_BOUNDS,
min_points=self.min_bucket_points,
min_points_total=self.min_points_total,
points_per_bucket=POINTS_PER_BUCKET,
rowsize=3)
def estimate_params(self): def estimate_params(self):
points = self.filtered_points.get_points(self.fit_points) points = self.filtered_points.get_points(self.fit_points)
@ -255,14 +218,11 @@ class TorqueEstimator:
return msg return msg
def main(sm=None, pm=None): def main():
config_realtime_process([0, 1, 2, 3], 5) config_realtime_process([0, 1, 2, 3], 5)
if sm is None: pm = messaging.PubMaster(['liveTorqueParameters'])
sm = messaging.SubMaster(['carControl', 'carState', 'liveLocationKalman'], poll=['liveLocationKalman']) sm = messaging.SubMaster(['carControl', 'carState', 'liveLocationKalman'], poll=['liveLocationKalman'])
if pm is None:
pm = messaging.PubMaster(['liveTorqueParameters'])
params = Params() params = Params()
with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP: with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP:

@ -45,18 +45,20 @@ snpe_rpath = lenvCython['RPATH'] + [snpe_rpath_qcom if arch == "larch64" else sn
cython_libs = envCython["LIBS"] + libs cython_libs = envCython["LIBS"] + libs
snpemodel_lib = lenv.Library('snpemodel', ['runners/snpemodel.cc']) snpemodel_lib = lenv.Library('snpemodel', ['runners/snpemodel.cc'])
commonmodel_lib = lenv.Library('commonmodel', common_src) commonmodel_lib = lenv.Library('commonmodel', common_src)
driving_lib = lenv.Library('driving', ['models/driving.cc'])
lenvCython.Program('runners/runmodel_pyx.so', 'runners/runmodel_pyx.pyx', LIBS=cython_libs, FRAMEWORKS=frameworks) lenvCython.Program('runners/runmodel_pyx.so', 'runners/runmodel_pyx.pyx', LIBS=cython_libs, FRAMEWORKS=frameworks)
lenvCython.Program('runners/snpemodel_pyx.so', 'runners/snpemodel_pyx.pyx', LIBS=[snpemodel_lib, snpe_lib, *cython_libs], FRAMEWORKS=frameworks, RPATH=snpe_rpath) lenvCython.Program('runners/snpemodel_pyx.so', 'runners/snpemodel_pyx.pyx', LIBS=[snpemodel_lib, snpe_lib, *cython_libs], FRAMEWORKS=frameworks, RPATH=snpe_rpath)
lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LIBS=[commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks) lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LIBS=[commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks)
lenvCython.Program('models/driving_pyx.so', 'models/driving_pyx.pyx', LIBS=[driving_lib, commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks)
# Get model metadata
fn = File("models/supercombo").abspath
cmd = f'python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx'
files = sum([lenv.Glob("#"+x) for x in open(File("#release/files_common").abspath).read().split("\n") if x.endswith("get_model_metadata.py")], [])
lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"]+files, cmd)
# Build thneed model # Build thneed model
if arch == "larch64" or GetOption('pc_thneed'): if arch == "larch64" or GetOption('pc_thneed'):
fn = File("models/supercombo").abspath tinygrad_opts = ["NOLOCALS=1", "IMAGE=2", "GPU=1"]
tinygrad_opts = ["NATIVE_EXPLOG=1", "VALIDHACKS=1", "OPTLOCAL=1", "IMAGE=2", "GPU=1", "ENABLE_METHOD_CACHE=1"]
if not GetOption('pc_thneed'): if not GetOption('pc_thneed'):
# use FLOAT16 on device for speed + don't cache the CL kernels for space # use FLOAT16 on device for speed + don't cache the CL kernels for space
tinygrad_opts += ["FLOAT16=1", "PYOPENCL_NO_CACHE=1"] tinygrad_opts += ["FLOAT16=1", "PYOPENCL_NO_CACHE=1"]

@ -1,7 +1,80 @@
IDX_N = 33 import numpy as np
def index_function(idx, max_val=192, max_idx=32): def index_function(idx, max_val=192, max_idx=32):
return (max_val) * ((idx/max_idx)**2) return (max_val) * ((idx/max_idx)**2)
class ModelConstants:
# time and distance indices
IDX_N = 33
T_IDXS = [index_function(idx, max_val=10.0) for idx in range(IDX_N)]
X_IDXS = [index_function(idx, max_val=192.0) for idx in range(IDX_N)]
LEAD_T_IDXS = [0., 2., 4., 6., 8., 10.]
LEAD_T_OFFSETS = [0., 2., 4.]
META_T_IDXS = [2., 4., 6., 8., 10.]
T_IDXS = [index_function(idx, max_val=10.0) for idx in range(IDX_N)] # model inputs constants
MODEL_FREQ = 20
FEATURE_LEN = 512
HISTORY_BUFFER_LEN = 99
DESIRE_LEN = 8
TRAFFIC_CONVENTION_LEN = 2
NAV_FEATURE_LEN = 256
NAV_INSTRUCTION_LEN = 150
DRIVING_STYLE_LEN = 12
LAT_PLANNER_STATE_LEN = 4
# model outputs constants
FCW_THRESHOLDS_5MS2 = np.array([.05, .05, .15, .15, .15], dtype=np.float32)
FCW_THRESHOLDS_3MS2 = np.array([.7, .7], dtype=np.float32)
DISENGAGE_WIDTH = 5
POSE_WIDTH = 6
WIDE_FROM_DEVICE_WIDTH = 3
SIM_POSE_WIDTH = 6
LEAD_WIDTH = 4
LANE_LINES_WIDTH = 2
ROAD_EDGES_WIDTH = 2
PLAN_WIDTH = 15
DESIRE_PRED_WIDTH = 8
LAT_PLANNER_SOLUTION_WIDTH = 4
NUM_LANE_LINES = 4
NUM_ROAD_EDGES = 2
LEAD_TRAJ_LEN = 6
DESIRE_PRED_LEN = 4
PLAN_MHP_N = 5
LEAD_MHP_N = 2
PLAN_MHP_SELECTION = 1
LEAD_MHP_SELECTION = 3
FCW_THRESHOLD_5MS2_HIGH = 0.15
FCW_THRESHOLD_5MS2_LOW = 0.05
FCW_THRESHOLD_3MS2 = 0.7
CONFIDENCE_BUFFER_LEN = 5
RYG_GREEN = 0.01165
RYG_YELLOW = 0.06157
# model outputs slices
class Plan:
POSITION = slice(0, 3)
VELOCITY = slice(3, 6)
ACCELERATION = slice(6, 9)
T_FROM_CURRENT_EULER = slice(9, 12)
ORIENTATION_RATE = slice(12, 15)
class Meta:
ENGAGED = slice(0, 1)
# next 2, 4, 6, 8, 10 seconds
GAS_DISENGAGE = slice(1, 36, 7)
BRAKE_DISENGAGE = slice(2, 36, 7)
STEER_OVERRIDE = slice(3, 36, 7)
HARD_BRAKE_3 = slice(4, 36, 7)
HARD_BRAKE_4 = slice(5, 36, 7)
HARD_BRAKE_5 = slice(6, 36, 7)
GAS_PRESS = slice(7, 36, 7)
# next 0, 2, 4, 6, 8, 10 seconds
LEFT_BLINKER = slice(36, 48, 2)
RIGHT_BLINKER = slice(37, 48, 2)

@ -0,0 +1,186 @@
import capnp
import numpy as np
from typing import Dict
from cereal import log
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan, Meta
ConfidenceClass = log.ModelDataV2.ConfidenceClass
class PublishState:
def __init__(self):
self.disengage_buffer = np.zeros(ModelConstants.CONFIDENCE_BUFFER_LEN*ModelConstants.DISENGAGE_WIDTH, dtype=np.float32)
self.prev_brake_5ms2_probs = np.zeros(ModelConstants.DISENGAGE_WIDTH, dtype=np.float32)
self.prev_brake_3ms2_probs = np.zeros(ModelConstants.DISENGAGE_WIDTH, dtype=np.float32)
def fill_xyzt(builder, t, x, y, z, x_std=None, y_std=None, z_std=None):
builder.t = t
builder.x = x.tolist()
builder.y = y.tolist()
builder.z = z.tolist()
if x_std is not None:
builder.xStd = x_std.tolist()
if y_std is not None:
builder.yStd = y_std.tolist()
if z_std is not None:
builder.zStd = z_std.tolist()
def fill_xyvat(builder, t, x, y, v, a, x_std=None, y_std=None, v_std=None, a_std=None):
builder.t = t
builder.x = x.tolist()
builder.y = y.tolist()
builder.v = v.tolist()
builder.a = a.tolist()
if x_std is not None:
builder.xStd = x_std.tolist()
if y_std is not None:
builder.yStd = y_std.tolist()
if v_std is not None:
builder.vStd = v_std.tolist()
if a_std is not None:
builder.aStd = a_std.tolist()
def fill_model_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str, np.ndarray], publish_state: PublishState,
vipc_frame_id: int, vipc_frame_id_extra: int, frame_id: int, frame_drop: float,
timestamp_eof: int, timestamp_llk: int, model_execution_time: float,
nav_enabled: bool, valid: bool) -> None:
frame_age = frame_id - vipc_frame_id if frame_id > vipc_frame_id else 0
msg.valid = valid
modelV2 = msg.modelV2
modelV2.frameId = vipc_frame_id
modelV2.frameIdExtra = vipc_frame_id_extra
modelV2.frameAge = frame_age
modelV2.frameDropPerc = frame_drop * 100
modelV2.timestampEof = timestamp_eof
modelV2.locationMonoTime = timestamp_llk
modelV2.modelExecutionTime = model_execution_time
modelV2.navEnabled = nav_enabled
# plan
position = modelV2.position
fill_xyzt(position, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.POSITION].T, *net_output_data['plan_stds'][0,:,Plan.POSITION].T)
velocity = modelV2.velocity
fill_xyzt(velocity, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.VELOCITY].T)
acceleration = modelV2.acceleration
fill_xyzt(acceleration, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ACCELERATION].T)
orientation = modelV2.orientation
fill_xyzt(orientation, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.T_FROM_CURRENT_EULER].T)
orientation_rate = modelV2.orientationRate
fill_xyzt(orientation_rate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T)
# lateral planning
solution = modelV2.lateralPlannerSolution
solution.x, solution.y, solution.yaw, solution.yawRate = [net_output_data['lat_planner_solution'][0,:,i].tolist() for i in range(4)]
solution.xStd, solution.yStd, solution.yawStd, solution.yawRateStd = [net_output_data['lat_planner_solution_stds'][0,:,i].tolist() for i in range(4)]
# times at X_IDXS according to model plan
PLAN_T_IDXS = [np.nan] * ModelConstants.IDX_N
PLAN_T_IDXS[0] = 0.0
plan_x = net_output_data['plan'][0,:,Plan.POSITION][:,0].tolist()
for xidx in range(1, ModelConstants.IDX_N):
tidx = 0
# increment tidx until we find an element that's further away than the current xidx
while tidx < ModelConstants.IDX_N - 1 and plan_x[tidx+1] < ModelConstants.X_IDXS[xidx]:
tidx += 1
if tidx == ModelConstants.IDX_N - 1:
# if the Plan doesn't extend far enough, set plan_t to the max value (10s), then break
PLAN_T_IDXS[xidx] = ModelConstants.T_IDXS[ModelConstants.IDX_N - 1]
break
# interpolate to find `t` for the current xidx
current_x_val = plan_x[tidx]
next_x_val = plan_x[tidx+1]
p = (ModelConstants.X_IDXS[xidx] - current_x_val) / (next_x_val - current_x_val)
PLAN_T_IDXS[xidx] = p * ModelConstants.T_IDXS[tidx+1] + (1 - p) * ModelConstants.T_IDXS[tidx]
# lane lines
modelV2.init('laneLines', 4)
for i in range(4):
lane_line = modelV2.laneLines[i]
fill_xyzt(lane_line, PLAN_T_IDXS, np.array(ModelConstants.X_IDXS), net_output_data['lane_lines'][0,i,:,0], net_output_data['lane_lines'][0,i,:,1])
modelV2.laneLineStds = net_output_data['lane_lines_stds'][0,:,0,0].tolist()
modelV2.laneLineProbs = net_output_data['lane_lines_prob'][0,1::2].tolist()
# road edges
modelV2.init('roadEdges', 2)
for i in range(2):
road_edge = modelV2.roadEdges[i]
fill_xyzt(road_edge, PLAN_T_IDXS, np.array(ModelConstants.X_IDXS), net_output_data['road_edges'][0,i,:,0], net_output_data['road_edges'][0,i,:,1])
modelV2.roadEdgeStds = net_output_data['road_edges_stds'][0,:,0,0].tolist()
# leads
modelV2.init('leadsV3', 3)
for i in range(3):
lead = modelV2.leadsV3[i]
fill_xyvat(lead, ModelConstants.LEAD_T_IDXS, *net_output_data['lead'][0,i].T, *net_output_data['lead_stds'][0,i].T)
lead.prob = net_output_data['lead_prob'][0,i].tolist()
lead.probTime = ModelConstants.LEAD_T_OFFSETS[i]
# meta
meta = modelV2.meta
meta.desireState = net_output_data['desire_state'][0].reshape(-1).tolist()
meta.desirePrediction = net_output_data['desire_pred'][0].reshape(-1).tolist()
meta.engagedProb = net_output_data['meta'][0,Meta.ENGAGED].item()
meta.init('disengagePredictions')
disengage_predictions = meta.disengagePredictions
disengage_predictions.t = ModelConstants.META_T_IDXS
disengage_predictions.brakeDisengageProbs = net_output_data['meta'][0,Meta.BRAKE_DISENGAGE].tolist()
disengage_predictions.gasDisengageProbs = net_output_data['meta'][0,Meta.GAS_DISENGAGE].tolist()
disengage_predictions.steerOverrideProbs = net_output_data['meta'][0,Meta.STEER_OVERRIDE].tolist()
disengage_predictions.brake3MetersPerSecondSquaredProbs = net_output_data['meta'][0,Meta.HARD_BRAKE_3].tolist()
disengage_predictions.brake4MetersPerSecondSquaredProbs = net_output_data['meta'][0,Meta.HARD_BRAKE_4].tolist()
disengage_predictions.brake5MetersPerSecondSquaredProbs = net_output_data['meta'][0,Meta.HARD_BRAKE_5].tolist()
publish_state.prev_brake_5ms2_probs[:-1] = publish_state.prev_brake_5ms2_probs[1:]
publish_state.prev_brake_5ms2_probs[-1] = net_output_data['meta'][0,Meta.HARD_BRAKE_5][0]
publish_state.prev_brake_3ms2_probs[:-1] = publish_state.prev_brake_3ms2_probs[1:]
publish_state.prev_brake_3ms2_probs[-1] = net_output_data['meta'][0,Meta.HARD_BRAKE_3][0]
hard_brake_predicted = (publish_state.prev_brake_5ms2_probs > ModelConstants.FCW_THRESHOLDS_5MS2).all() and \
(publish_state.prev_brake_3ms2_probs > ModelConstants.FCW_THRESHOLDS_3MS2).all()
meta.hardBrakePredicted = hard_brake_predicted.item()
# temporal pose
temporal_pose = modelV2.temporalPose
temporal_pose.trans = net_output_data['sim_pose'][0,:3].tolist()
temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:3].tolist()
temporal_pose.rot = net_output_data['sim_pose'][0,3:].tolist()
temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,3:].tolist()
# confidence
if vipc_frame_id % (2*ModelConstants.MODEL_FREQ) == 0:
# any disengage prob
brake_disengage_probs = net_output_data['meta'][0,Meta.BRAKE_DISENGAGE]
gas_disengage_probs = net_output_data['meta'][0,Meta.GAS_DISENGAGE]
steer_override_probs = net_output_data['meta'][0,Meta.STEER_OVERRIDE]
any_disengage_probs = 1-((1-brake_disengage_probs)*(1-gas_disengage_probs)*(1-steer_override_probs))
# independent disengage prob for each 2s slice
ind_disengage_probs = np.r_[any_disengage_probs[0], np.diff(any_disengage_probs) / (1 - any_disengage_probs[:-1])]
# rolling buf for 2, 4, 6, 8, 10s
publish_state.disengage_buffer[:-ModelConstants.DISENGAGE_WIDTH] = publish_state.disengage_buffer[ModelConstants.DISENGAGE_WIDTH:]
publish_state.disengage_buffer[-ModelConstants.DISENGAGE_WIDTH:] = ind_disengage_probs
score = 0.
for i in range(ModelConstants.DISENGAGE_WIDTH):
score += publish_state.disengage_buffer[i*ModelConstants.DISENGAGE_WIDTH+ModelConstants.DISENGAGE_WIDTH-1-i].item() / ModelConstants.DISENGAGE_WIDTH
if score < ModelConstants.RYG_GREEN:
modelV2.confidence = ConfidenceClass.green
elif score < ModelConstants.RYG_YELLOW:
modelV2.confidence = ConfidenceClass.yellow
else:
modelV2.confidence = ConfidenceClass.red
def fill_pose_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str, np.ndarray],
vipc_frame_id: int, vipc_dropped_frames: int, timestamp_eof: int, live_calib_seen: bool) -> None:
msg.valid = live_calib_seen & (vipc_dropped_frames < 1)
cameraOdometry = msg.cameraOdometry
cameraOdometry.frameId = vipc_frame_id
cameraOdometry.timestampEof = timestamp_eof
cameraOdometry.trans = net_output_data['pose'][0,:3].tolist()
cameraOdometry.rot = net_output_data['pose'][0,3:].tolist()
cameraOdometry.wideFromDeviceEuler = net_output_data['wide_from_device_euler'][0,:].tolist()
cameraOdometry.roadTransformTrans = net_output_data['road_transform'][0,:3].tolist()
cameraOdometry.transStd = net_output_data['pose_stds'][0,:3].tolist()
cameraOdometry.rotStd = net_output_data['pose_stds'][0,3:].tolist()
cameraOdometry.wideFromDeviceEulerStd = net_output_data['wide_from_device_euler_stds'][0,:].tolist()
cameraOdometry.roadTransformTransStd = net_output_data['road_transform_stds'][0,:3].tolist()

@ -0,0 +1,29 @@
#!/usr/bin/env python3
import sys
import pathlib
import onnx
import codecs
import pickle
from typing import Tuple
def get_name_and_shape(value_info:onnx.ValueInfoProto) -> Tuple[str, Tuple[int,...]]:
shape = tuple([int(dim.dim_value) for dim in value_info.type.tensor_type.shape.dim])
name = value_info.name
return name, shape
if __name__ == "__main__":
model_path = pathlib.Path(sys.argv[1])
model = onnx.load(str(model_path))
i = [x.key for x in model.metadata_props].index('output_slices')
output_slices = model.metadata_props[i].value
metadata = {}
metadata['output_slices'] = pickle.loads(codecs.decode(output_slices.encode(), "base64"))
metadata['input_shapes'] = dict([get_name_and_shape(x) for x in model.graph.input])
metadata['output_shapes'] = dict([get_name_and_shape(x) for x in model.graph.output])
metadata_path = model_path.parent / (model_path.stem + '_metadata.pkl')
with open(metadata_path, 'wb') as f:
pickle.dump(metadata, f)
print(f'saved metadata to {metadata_path}')

@ -1,7 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys import sys
import time import time
import pickle
import numpy as np import numpy as np
import cereal.messaging as messaging
from pathlib import Path from pathlib import Path
from typing import Dict, Optional from typing import Dict, Optional
from setproctitle import setproctitle from setproctitle import setproctitle
@ -15,18 +17,17 @@ from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.realtime import config_realtime_process from openpilot.common.realtime import config_realtime_process
from openpilot.common.transformations.model import get_warp_matrix from openpilot.common.transformations.model import get_warp_matrix
from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime
from openpilot.selfdrive.modeld.constants import T_IDXS from openpilot.selfdrive.modeld.parse_model_outputs import Parser
from openpilot.selfdrive.controls.lib.lateral_planner import TRAJECTORY_SIZE from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.modeld.models.commonmodel_pyx import ModelFrame, CLContext from openpilot.selfdrive.modeld.models.commonmodel_pyx import ModelFrame, CLContext
from openpilot.selfdrive.modeld.models.driving_pyx import (
PublishState, create_model_msg, create_pose_msg,
FEATURE_LEN, HISTORY_BUFFER_LEN, DESIRE_LEN, TRAFFIC_CONVENTION_LEN, NAV_FEATURE_LEN, NAV_INSTRUCTION_LEN,
LAT_PLANNER_STATE_LEN, OUTPUT_SIZE, NET_OUTPUT_SIZE, MODEL_FREQ)
MODEL_PATHS = { MODEL_PATHS = {
ModelRunner.THNEED: Path(__file__).parent / 'models/supercombo.thneed', ModelRunner.THNEED: Path(__file__).parent / 'models/supercombo.thneed',
ModelRunner.ONNX: Path(__file__).parent / 'models/supercombo.onnx'} ModelRunner.ONNX: Path(__file__).parent / 'models/supercombo.onnx'}
METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl'
class FrameMeta: class FrameMeta:
frame_id: int = 0 frame_id: int = 0
timestamp_sof: int = 0 timestamp_sof: int = 0
@ -47,29 +48,39 @@ class ModelState:
def __init__(self, context: CLContext): def __init__(self, context: CLContext):
self.frame = ModelFrame(context) self.frame = ModelFrame(context)
self.wide_frame = ModelFrame(context) self.wide_frame = ModelFrame(context)
self.prev_desire = np.zeros(DESIRE_LEN, dtype=np.float32) self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
self.output = np.zeros(NET_OUTPUT_SIZE, dtype=np.float32)
self.inputs = { self.inputs = {
'desire': np.zeros(DESIRE_LEN * (HISTORY_BUFFER_LEN+1), dtype=np.float32), 'desire': np.zeros(ModelConstants.DESIRE_LEN * (ModelConstants.HISTORY_BUFFER_LEN+1), dtype=np.float32),
'traffic_convention': np.zeros(TRAFFIC_CONVENTION_LEN, dtype=np.float32), 'traffic_convention': np.zeros(ModelConstants.TRAFFIC_CONVENTION_LEN, dtype=np.float32),
'lat_planner_state': np.zeros(LAT_PLANNER_STATE_LEN, dtype=np.float32), 'lat_planner_state': np.zeros(ModelConstants.LAT_PLANNER_STATE_LEN, dtype=np.float32),
'nav_features': np.zeros(NAV_FEATURE_LEN, dtype=np.float32), 'nav_features': np.zeros(ModelConstants.NAV_FEATURE_LEN, dtype=np.float32),
'nav_instructions': np.zeros(NAV_INSTRUCTION_LEN, dtype=np.float32), 'nav_instructions': np.zeros(ModelConstants.NAV_INSTRUCTION_LEN, dtype=np.float32),
'features_buffer': np.zeros(HISTORY_BUFFER_LEN * FEATURE_LEN, dtype=np.float32), 'features_buffer': np.zeros(ModelConstants.HISTORY_BUFFER_LEN * ModelConstants.FEATURE_LEN, dtype=np.float32),
} }
with open(METADATA_PATH, 'rb') as f:
model_metadata = pickle.load(f)
self.output_slices = model_metadata['output_slices']
net_output_size = model_metadata['output_shapes']['outputs'][1]
self.output = np.zeros(net_output_size, dtype=np.float32)
self.parser = Parser()
self.model = ModelRunner(MODEL_PATHS, self.output, Runtime.GPU, False, context) self.model = ModelRunner(MODEL_PATHS, self.output, Runtime.GPU, False, context)
self.model.addInput("input_imgs", None) self.model.addInput("input_imgs", None)
self.model.addInput("big_input_imgs", None) self.model.addInput("big_input_imgs", None)
for k,v in self.inputs.items(): for k,v in self.inputs.items():
self.model.addInput(k, v) self.model.addInput(k, v)
def slice_outputs(self, model_outputs: np.ndarray) -> Dict[str, np.ndarray]:
return {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()}
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray, def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
inputs: Dict[str, np.ndarray], prepare_only: bool) -> Optional[np.ndarray]: inputs: Dict[str, np.ndarray], prepare_only: bool) -> Optional[Dict[str, np.ndarray]]:
# Model decides when action is completed, so desire input is just a pulse triggered on rising edge # Model decides when action is completed, so desire input is just a pulse triggered on rising edge
inputs['desire'][0] = 0 inputs['desire'][0] = 0
self.inputs['desire'][:-DESIRE_LEN] = self.inputs['desire'][DESIRE_LEN:] self.inputs['desire'][:-ModelConstants.DESIRE_LEN] = self.inputs['desire'][ModelConstants.DESIRE_LEN:]
self.inputs['desire'][-DESIRE_LEN:] = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0) self.inputs['desire'][-ModelConstants.DESIRE_LEN:] = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0)
self.prev_desire[:] = inputs['desire'] self.prev_desire[:] = inputs['desire']
self.inputs['traffic_convention'][:] = inputs['traffic_convention'] self.inputs['traffic_convention'][:] = inputs['traffic_convention']
@ -86,14 +97,13 @@ class ModelState:
return None return None
self.model.execute() self.model.execute()
self.inputs['features_buffer'][:-FEATURE_LEN] = self.inputs['features_buffer'][FEATURE_LEN:] outputs = self.parser.parse_outputs(self.slice_outputs(self.output))
# TODO: self.output should be cast to the modeld output struct then these 2 tricky slicings can be removed
self.inputs['features_buffer'][-FEATURE_LEN:] = self.output[OUTPUT_SIZE:OUTPUT_SIZE+FEATURE_LEN] self.inputs['features_buffer'][:-ModelConstants.FEATURE_LEN] = self.inputs['features_buffer'][ModelConstants.FEATURE_LEN:]
lat_planning_output = self.output[OUTPUT_SIZE-2*LAT_PLANNER_STATE_LEN*TRAJECTORY_SIZE:OUTPUT_SIZE-LAT_PLANNER_STATE_LEN*TRAJECTORY_SIZE] self.inputs['features_buffer'][-ModelConstants.FEATURE_LEN:] = outputs['hidden_state'][0, :]
lat_planning_output = lat_planning_output.reshape((TRAJECTORY_SIZE, LAT_PLANNER_STATE_LEN)) self.inputs['lat_planner_state'][2] = interp(DT_MDL, ModelConstants.T_IDXS, outputs['lat_planner_solution'][0, :, 2])
self.inputs['lat_planner_state'][2] = interp(DT_MDL, T_IDXS, lat_planning_output[:, 2]) self.inputs['lat_planner_state'][3] = interp(DT_MDL, ModelConstants.T_IDXS, outputs['lat_planner_solution'][0, :, 3])
self.inputs['lat_planner_state'][3] = interp(DT_MDL, T_IDXS, lat_planning_output[:, 3]) return outputs
return self.output
def main(): def main():
@ -132,22 +142,21 @@ def main():
pm = PubMaster(["modelV2", "cameraOdometry"]) pm = PubMaster(["modelV2", "cameraOdometry"])
sm = SubMaster(["lateralPlan", "roadCameraState", "liveCalibration", "driverMonitoringState", "navModel", "navInstruction"]) sm = SubMaster(["lateralPlan", "roadCameraState", "liveCalibration", "driverMonitoringState", "navModel", "navInstruction"])
state = PublishState() publish_state = PublishState()
params = Params() params = Params()
# setup filter to track dropped frames # setup filter to track dropped frames
frame_dropped_filter = FirstOrderFilter(0., 10., 1. / MODEL_FREQ) frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_FREQ)
frame_id = 0 frame_id = 0
last_vipc_frame_id = 0 last_vipc_frame_id = 0
run_count = 0 run_count = 0
# last = 0.0
model_transform_main = np.zeros((3, 3), dtype=np.float32) model_transform_main = np.zeros((3, 3), dtype=np.float32)
model_transform_extra = np.zeros((3, 3), dtype=np.float32) model_transform_extra = np.zeros((3, 3), dtype=np.float32)
live_calib_seen = False live_calib_seen = False
driving_style = np.array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], dtype=np.float32) driving_style = np.array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], dtype=np.float32)
nav_features = np.zeros(NAV_FEATURE_LEN, dtype=np.float32) nav_features = np.zeros(ModelConstants.NAV_FEATURE_LEN, dtype=np.float32)
nav_instructions = np.zeros(NAV_INSTRUCTION_LEN, dtype=np.float32) nav_instructions = np.zeros(ModelConstants.NAV_INSTRUCTION_LEN, dtype=np.float32)
buf_main, buf_extra = None, None buf_main, buf_extra = None, None
meta_main = FrameMeta() meta_main = FrameMeta()
meta_extra = FrameMeta() meta_extra = FrameMeta()
@ -200,8 +209,8 @@ def main():
traffic_convention = np.zeros(2) traffic_convention = np.zeros(2)
traffic_convention[int(is_rhd)] = 1 traffic_convention[int(is_rhd)] = 1
vec_desire = np.zeros(DESIRE_LEN, dtype=np.float32) vec_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
if desire >= 0 and desire < DESIRE_LEN: if desire >= 0 and desire < ModelConstants.DESIRE_LEN:
vec_desire[desire] = 1 vec_desire[desire] = 1
# Enable/disable nav features # Enable/disable nav features
@ -254,13 +263,15 @@ def main():
model_execution_time = mt2 - mt1 model_execution_time = mt2 - mt1
if model_output is not None: if model_output is not None:
pm.send("modelV2", create_model_msg(model_output, state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, modelv2_send = messaging.new_message('modelV2')
meta_main.timestamp_eof, timestamp_llk, model_execution_time, nav_enabled, live_calib_seen)) posenet_send = messaging.new_message('cameraOdometry')
pm.send("cameraOdometry", create_pose_msg(model_output, meta_main.frame_id, vipc_dropped_frames, meta_main.timestamp_eof, live_calib_seen)) fill_model_msg(modelv2_send, model_output, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio,
meta_main.timestamp_eof, timestamp_llk, model_execution_time, nav_enabled, live_calib_seen)
fill_pose_msg(posenet_send, model_output, meta_main.frame_id, vipc_dropped_frames, meta_main.timestamp_eof, live_calib_seen)
pm.send('modelV2', modelv2_send)
pm.send('cameraOdometry', posenet_send)
# print("model process: %.2fms, from last %.2fms, vipc_frame_id %u, frame_id, %u, frame_drop %.3f" %
# ((mt2 - mt1)*1000, (mt1 - last)*1000, meta_extra.frame_id, frame_id, frame_drop_ratio))
# last = mt1
last_vipc_frame_id = meta_main.frame_id last_vipc_frame_id = meta_main.frame_id

@ -20,7 +20,11 @@ To view the architecture of the ONNX networks, you can use [netron](https://netr
* **traffic convention** * **traffic convention**
* one-hot encoded vector to tell model whether traffic is right-hand or left-hand traffic : 2 * one-hot encoded vector to tell model whether traffic is right-hand or left-hand traffic : 2
* **feature buffer** * **feature buffer**
* A buffer of intermediate features that gets appended to the current feature to form a 5 seconds temporal context (at 20FPS) : 99 * 128 * A buffer of intermediate features that gets appended to the current feature to form a 5 seconds temporal context (at 20FPS) : 99 * 512
* **nav features**
* 1 * 150
* **nav instructions**
* 1 * 256
### Supercombo output format (Full size: XXX x float32) ### Supercombo output format (Full size: XXX x float32)

@ -1,358 +0,0 @@
#include "selfdrive/modeld/models/driving.h"
#include <cstring>
void fill_lead(cereal::ModelDataV2::LeadDataV3::Builder lead, const ModelOutputLeads &leads, int t_idx, float prob_t) {
std::array<float, LEAD_TRAJ_LEN> lead_t = {0.0, 2.0, 4.0, 6.0, 8.0, 10.0};
const auto &best_prediction = leads.get_best_prediction(t_idx);
lead.setProb(sigmoid(leads.prob[t_idx]));
lead.setProbTime(prob_t);
std::array<float, LEAD_TRAJ_LEN> lead_x, lead_y, lead_v, lead_a;
std::array<float, LEAD_TRAJ_LEN> lead_x_std, lead_y_std, lead_v_std, lead_a_std;
for (int i=0; i<LEAD_TRAJ_LEN; i++) {
lead_x[i] = best_prediction.mean[i].x;
lead_y[i] = best_prediction.mean[i].y;
lead_v[i] = best_prediction.mean[i].velocity;
lead_a[i] = best_prediction.mean[i].acceleration;
lead_x_std[i] = exp(best_prediction.std[i].x);
lead_y_std[i] = exp(best_prediction.std[i].y);
lead_v_std[i] = exp(best_prediction.std[i].velocity);
lead_a_std[i] = exp(best_prediction.std[i].acceleration);
}
lead.setT(to_kj_array_ptr(lead_t));
lead.setX(to_kj_array_ptr(lead_x));
lead.setY(to_kj_array_ptr(lead_y));
lead.setV(to_kj_array_ptr(lead_v));
lead.setA(to_kj_array_ptr(lead_a));
lead.setXStd(to_kj_array_ptr(lead_x_std));
lead.setYStd(to_kj_array_ptr(lead_y_std));
lead.setVStd(to_kj_array_ptr(lead_v_std));
lead.setAStd(to_kj_array_ptr(lead_a_std));
}
void fill_meta(cereal::ModelDataV2::MetaData::Builder meta, const ModelOutputMeta &meta_data, PublishState &ps) {
std::array<float, DESIRE_LEN> desire_state_softmax;
softmax(meta_data.desire_state_prob.array.data(), desire_state_softmax.data(), DESIRE_LEN);
std::array<float, DESIRE_PRED_LEN * DESIRE_LEN> desire_pred_softmax;
for (int i=0; i<DESIRE_PRED_LEN; i++) {
softmax(meta_data.desire_pred_prob[i].array.data(), desire_pred_softmax.data() + (i * DESIRE_LEN), DESIRE_LEN);
}
std::array<float, DISENGAGE_LEN> lat_long_t = {2, 4, 6, 8, 10};
std::array<float, DISENGAGE_LEN> gas_disengage_sigmoid, brake_disengage_sigmoid, steer_override_sigmoid,
brake_3ms2_sigmoid, brake_4ms2_sigmoid, brake_5ms2_sigmoid;
for (int i=0; i<DISENGAGE_LEN; i++) {
gas_disengage_sigmoid[i] = sigmoid(meta_data.disengage_prob[i].gas_disengage);
brake_disengage_sigmoid[i] = sigmoid(meta_data.disengage_prob[i].brake_disengage);
steer_override_sigmoid[i] = sigmoid(meta_data.disengage_prob[i].steer_override);
brake_3ms2_sigmoid[i] = sigmoid(meta_data.disengage_prob[i].brake_3ms2);
brake_4ms2_sigmoid[i] = sigmoid(meta_data.disengage_prob[i].brake_4ms2);
brake_5ms2_sigmoid[i] = sigmoid(meta_data.disengage_prob[i].brake_5ms2);
//gas_pressed_sigmoid[i] = sigmoid(meta_data.disengage_prob[i].gas_pressed);
}
std::memmove(ps.prev_brake_5ms2_probs.data(), &ps.prev_brake_5ms2_probs[1], 4*sizeof(float));
std::memmove(ps.prev_brake_3ms2_probs.data(), &ps.prev_brake_3ms2_probs[1], 2*sizeof(float));
ps.prev_brake_5ms2_probs[4] = brake_5ms2_sigmoid[0];
ps.prev_brake_3ms2_probs[2] = brake_3ms2_sigmoid[0];
bool above_fcw_threshold = true;
for (int i=0; i<ps.prev_brake_5ms2_probs.size(); i++) {
float threshold = i < 2 ? FCW_THRESHOLD_5MS2_LOW : FCW_THRESHOLD_5MS2_HIGH;
above_fcw_threshold = above_fcw_threshold && ps.prev_brake_5ms2_probs[i] > threshold;
}
for (int i=0; i<ps.prev_brake_3ms2_probs.size(); i++) {
above_fcw_threshold = above_fcw_threshold && ps.prev_brake_3ms2_probs[i] > FCW_THRESHOLD_3MS2;
}
auto disengage = meta.initDisengagePredictions();
disengage.setT(to_kj_array_ptr(lat_long_t));
disengage.setGasDisengageProbs(to_kj_array_ptr(gas_disengage_sigmoid));
disengage.setBrakeDisengageProbs(to_kj_array_ptr(brake_disengage_sigmoid));
disengage.setSteerOverrideProbs(to_kj_array_ptr(steer_override_sigmoid));
disengage.setBrake3MetersPerSecondSquaredProbs(to_kj_array_ptr(brake_3ms2_sigmoid));
disengage.setBrake4MetersPerSecondSquaredProbs(to_kj_array_ptr(brake_4ms2_sigmoid));
disengage.setBrake5MetersPerSecondSquaredProbs(to_kj_array_ptr(brake_5ms2_sigmoid));
meta.setEngagedProb(sigmoid(meta_data.engaged_prob));
meta.setDesirePrediction(to_kj_array_ptr(desire_pred_softmax));
meta.setDesireState(to_kj_array_ptr(desire_state_softmax));
meta.setHardBrakePredicted(above_fcw_threshold);
}
void fill_confidence(cereal::ModelDataV2::Builder &framed, PublishState &ps) {
if (framed.getFrameId() % (2*MODEL_FREQ) == 0) {
// update every 2s to match predictions interval
auto dbps = framed.getMeta().getDisengagePredictions().getBrakeDisengageProbs();
auto dgps = framed.getMeta().getDisengagePredictions().getGasDisengageProbs();
auto dsps = framed.getMeta().getDisengagePredictions().getSteerOverrideProbs();
float any_dp[DISENGAGE_LEN];
float dp_ind[DISENGAGE_LEN];
for (int i = 0; i < DISENGAGE_LEN; i++) {
any_dp[i] = 1 - ((1-dbps[i])*(1-dgps[i])*(1-dsps[i])); // any disengage prob
}
dp_ind[0] = any_dp[0];
for (int i = 0; i < DISENGAGE_LEN-1; i++) {
dp_ind[i+1] = (any_dp[i+1] - any_dp[i]) / (1 - any_dp[i]); // independent disengage prob for each 2s slice
}
// rolling buf for 2, 4, 6, 8, 10s
std::memmove(&ps.disengage_buffer[0], &ps.disengage_buffer[DISENGAGE_LEN], sizeof(float) * DISENGAGE_LEN * (DISENGAGE_LEN-1));
std::memcpy(&ps.disengage_buffer[DISENGAGE_LEN * (DISENGAGE_LEN-1)], &dp_ind[0], sizeof(float) * DISENGAGE_LEN);
}
float score = 0;
for (int i = 0; i < DISENGAGE_LEN; i++) {
score += ps.disengage_buffer[i*DISENGAGE_LEN+DISENGAGE_LEN-1-i] / DISENGAGE_LEN;
}
if (score < RYG_GREEN) {
framed.setConfidence(cereal::ModelDataV2::ConfidenceClass::GREEN);
} else if (score < RYG_YELLOW) {
framed.setConfidence(cereal::ModelDataV2::ConfidenceClass::YELLOW);
} else {
framed.setConfidence(cereal::ModelDataV2::ConfidenceClass::RED);
}
}
template<size_t size>
void fill_xyzt(cereal::XYZTData::Builder xyzt, const std::array<float, size> &t,
const std::array<float, size> &x, const std::array<float, size> &y, const std::array<float, size> &z) {
xyzt.setT(to_kj_array_ptr(t));
xyzt.setX(to_kj_array_ptr(x));
xyzt.setY(to_kj_array_ptr(y));
xyzt.setZ(to_kj_array_ptr(z));
}
template<size_t size>
void fill_xyzt(cereal::XYZTData::Builder xyzt, const std::array<float, size> &t,
const std::array<float, size> &x, const std::array<float, size> &y, const std::array<float, size> &z,
const std::array<float, size> &x_std, const std::array<float, size> &y_std, const std::array<float, size> &z_std) {
fill_xyzt(xyzt, t, x, y, z);
xyzt.setXStd(to_kj_array_ptr(x_std));
xyzt.setYStd(to_kj_array_ptr(y_std));
xyzt.setZStd(to_kj_array_ptr(z_std));
}
void fill_plan(cereal::ModelDataV2::Builder &framed, const ModelOutputPlanPrediction &plan) {
std::array<float, TRAJECTORY_SIZE> pos_x, pos_y, pos_z;
std::array<float, TRAJECTORY_SIZE> pos_x_std, pos_y_std, pos_z_std;
std::array<float, TRAJECTORY_SIZE> vel_x, vel_y, vel_z;
std::array<float, TRAJECTORY_SIZE> rot_x, rot_y, rot_z;
std::array<float, TRAJECTORY_SIZE> acc_x, acc_y, acc_z;
std::array<float, TRAJECTORY_SIZE> rot_rate_x, rot_rate_y, rot_rate_z;
for (int i=0; i<TRAJECTORY_SIZE; i++) {
pos_x[i] = plan.mean[i].position.x;
pos_y[i] = plan.mean[i].position.y;
pos_z[i] = plan.mean[i].position.z;
pos_x_std[i] = exp(plan.std[i].position.x);
pos_y_std[i] = exp(plan.std[i].position.y);
pos_z_std[i] = exp(plan.std[i].position.z);
vel_x[i] = plan.mean[i].velocity.x;
vel_y[i] = plan.mean[i].velocity.y;
vel_z[i] = plan.mean[i].velocity.z;
acc_x[i] = plan.mean[i].acceleration.x;
acc_y[i] = plan.mean[i].acceleration.y;
acc_z[i] = plan.mean[i].acceleration.z;
rot_x[i] = plan.mean[i].rotation.x;
rot_y[i] = plan.mean[i].rotation.y;
rot_z[i] = plan.mean[i].rotation.z;
rot_rate_x[i] = plan.mean[i].rotation_rate.x;
rot_rate_y[i] = plan.mean[i].rotation_rate.y;
rot_rate_z[i] = plan.mean[i].rotation_rate.z;
}
fill_xyzt(framed.initPosition(), T_IDXS_FLOAT, pos_x, pos_y, pos_z, pos_x_std, pos_y_std, pos_z_std);
fill_xyzt(framed.initVelocity(), T_IDXS_FLOAT, vel_x, vel_y, vel_z);
fill_xyzt(framed.initAcceleration(), T_IDXS_FLOAT, acc_x, acc_y, acc_z);
fill_xyzt(framed.initOrientation(), T_IDXS_FLOAT, rot_x, rot_y, rot_z);
fill_xyzt(framed.initOrientationRate(), T_IDXS_FLOAT, rot_rate_x, rot_rate_y, rot_rate_z);
}
void fill_lateral_planner(cereal::ModelDataV2::Builder &framed, const LateralPlannerOutput &model_lateral_planner_solution) {
std::array<float, TRAJECTORY_SIZE> lateral_plan_solution_x, lateral_plan_solution_y, lateral_plan_solution_yaw, lateral_plan_solution_yaw_rate;
std::array<float, TRAJECTORY_SIZE> lateral_plan_solution_x_std, lateral_plan_solution_y_std, lateral_plan_solution_yaw_std, lateral_plan_solution_yaw_rate_std;
for (int i=0; i<TRAJECTORY_SIZE; i++) {
lateral_plan_solution_x[i] = model_lateral_planner_solution.mean[i].x;
lateral_plan_solution_y[i] = model_lateral_planner_solution.mean[i].y;
lateral_plan_solution_yaw[i] = model_lateral_planner_solution.mean[i].yaw;
lateral_plan_solution_yaw_rate[i] = model_lateral_planner_solution.mean[i].yaw_rate;
lateral_plan_solution_x_std[i] = exp(model_lateral_planner_solution.std[i].x);
lateral_plan_solution_y_std[i] = exp(model_lateral_planner_solution.std[i].y);
lateral_plan_solution_yaw_std[i] = exp(model_lateral_planner_solution.std[i].yaw);
lateral_plan_solution_yaw_rate_std[i] = exp(model_lateral_planner_solution.std[i].yaw_rate);
}
auto lateral_planner_solution = framed.initLateralPlannerSolution();
lateral_planner_solution.setX(to_kj_array_ptr(lateral_plan_solution_x));
lateral_planner_solution.setY(to_kj_array_ptr(lateral_plan_solution_y));
lateral_planner_solution.setYaw(to_kj_array_ptr(lateral_plan_solution_yaw));
lateral_planner_solution.setYawRate(to_kj_array_ptr(lateral_plan_solution_yaw_rate));
lateral_planner_solution.setXStd(to_kj_array_ptr(lateral_plan_solution_x_std));
lateral_planner_solution.setYStd(to_kj_array_ptr(lateral_plan_solution_y_std));
lateral_planner_solution.setYawStd(to_kj_array_ptr(lateral_plan_solution_yaw_std));
lateral_planner_solution.setYawRateStd(to_kj_array_ptr(lateral_plan_solution_yaw_rate_std));
}
void fill_lane_lines(cereal::ModelDataV2::Builder &framed, const std::array<float, TRAJECTORY_SIZE> &plan_t,
const ModelOutputLaneLines &lanes) {
std::array<float, TRAJECTORY_SIZE> left_far_y, left_far_z;
std::array<float, TRAJECTORY_SIZE> left_near_y, left_near_z;
std::array<float, TRAJECTORY_SIZE> right_near_y, right_near_z;
std::array<float, TRAJECTORY_SIZE> right_far_y, right_far_z;
for (int j=0; j<TRAJECTORY_SIZE; j++) {
left_far_y[j] = lanes.mean.left_far[j].y;
left_far_z[j] = lanes.mean.left_far[j].z;
left_near_y[j] = lanes.mean.left_near[j].y;
left_near_z[j] = lanes.mean.left_near[j].z;
right_near_y[j] = lanes.mean.right_near[j].y;
right_near_z[j] = lanes.mean.right_near[j].z;
right_far_y[j] = lanes.mean.right_far[j].y;
right_far_z[j] = lanes.mean.right_far[j].z;
}
auto lane_lines = framed.initLaneLines(4);
fill_xyzt(lane_lines[0], plan_t, X_IDXS_FLOAT, left_far_y, left_far_z);
fill_xyzt(lane_lines[1], plan_t, X_IDXS_FLOAT, left_near_y, left_near_z);
fill_xyzt(lane_lines[2], plan_t, X_IDXS_FLOAT, right_near_y, right_near_z);
fill_xyzt(lane_lines[3], plan_t, X_IDXS_FLOAT, right_far_y, right_far_z);
framed.setLaneLineStds({
exp(lanes.std.left_far[0].y),
exp(lanes.std.left_near[0].y),
exp(lanes.std.right_near[0].y),
exp(lanes.std.right_far[0].y),
});
framed.setLaneLineProbs({
sigmoid(lanes.prob.left_far.val),
sigmoid(lanes.prob.left_near.val),
sigmoid(lanes.prob.right_near.val),
sigmoid(lanes.prob.right_far.val),
});
}
void fill_road_edges(cereal::ModelDataV2::Builder &framed, const std::array<float, TRAJECTORY_SIZE> &plan_t,
const ModelOutputRoadEdges &edges) {
std::array<float, TRAJECTORY_SIZE> left_y, left_z;
std::array<float, TRAJECTORY_SIZE> right_y, right_z;
for (int j=0; j<TRAJECTORY_SIZE; j++) {
left_y[j] = edges.mean.left[j].y;
left_z[j] = edges.mean.left[j].z;
right_y[j] = edges.mean.right[j].y;
right_z[j] = edges.mean.right[j].z;
}
auto road_edges = framed.initRoadEdges(2);
fill_xyzt(road_edges[0], plan_t, X_IDXS_FLOAT, left_y, left_z);
fill_xyzt(road_edges[1], plan_t, X_IDXS_FLOAT, right_y, right_z);
framed.setRoadEdgeStds({
exp(edges.std.left[0].y),
exp(edges.std.right[0].y),
});
}
void fill_model(cereal::ModelDataV2::Builder &framed, const ModelOutput &net_outputs, PublishState &ps) {
const auto &best_plan = net_outputs.plans.get_best_prediction();
std::array<float, TRAJECTORY_SIZE> plan_t;
std::fill_n(plan_t.data(), plan_t.size(), NAN);
plan_t[0] = 0.0;
for (int xidx=1, tidx=0; xidx<TRAJECTORY_SIZE; xidx++) {
// increment tidx until we find an element that's further away than the current xidx
for (int next_tid = tidx + 1; next_tid < TRAJECTORY_SIZE && best_plan.mean[next_tid].position.x < X_IDXS[xidx]; next_tid++) {
tidx++;
}
if (tidx == TRAJECTORY_SIZE - 1) {
// if the Plan doesn't extend far enough, set plan_t to the max value (10s), then break
plan_t[xidx] = T_IDXS[TRAJECTORY_SIZE - 1];
break;
}
// interpolate to find `t` for the current xidx
float current_x_val = best_plan.mean[tidx].position.x;
float next_x_val = best_plan.mean[tidx+1].position.x;
float p = (X_IDXS[xidx] - current_x_val) / (next_x_val - current_x_val);
plan_t[xidx] = p * T_IDXS[tidx+1] + (1 - p) * T_IDXS[tidx];
}
fill_plan(framed, best_plan);
fill_lateral_planner(framed, net_outputs.lateral_planner_solution);
fill_lane_lines(framed, plan_t, net_outputs.lane_lines);
fill_road_edges(framed, plan_t, net_outputs.road_edges);
// meta
fill_meta(framed.initMeta(), net_outputs.meta, ps);
// confidence
fill_confidence(framed, ps);
// leads
auto leads = framed.initLeadsV3(LEAD_MHP_SELECTION);
std::array<float, LEAD_MHP_SELECTION> t_offsets = {0.0, 2.0, 4.0};
for (int i=0; i<LEAD_MHP_SELECTION; i++) {
fill_lead(leads[i], net_outputs.leads, i, t_offsets[i]);
}
// temporal pose
const auto &v_mean = net_outputs.temporal_pose.velocity_mean;
const auto &r_mean = net_outputs.temporal_pose.rotation_mean;
const auto &v_std = net_outputs.temporal_pose.velocity_std;
const auto &r_std = net_outputs.temporal_pose.rotation_std;
auto temporal_pose = framed.initTemporalPose();
temporal_pose.setTrans({v_mean.x, v_mean.y, v_mean.z});
temporal_pose.setRot({r_mean.x, r_mean.y, r_mean.z});
temporal_pose.setTransStd({exp(v_std.x), exp(v_std.y), exp(v_std.z)});
temporal_pose.setRotStd({exp(r_std.x), exp(r_std.y), exp(r_std.z)});
}
void fill_model_msg(MessageBuilder &msg, float *net_output_data, PublishState &ps, uint32_t vipc_frame_id, uint32_t vipc_frame_id_extra, uint32_t frame_id, float frame_drop,
uint64_t timestamp_eof, uint64_t timestamp_llk, float model_execution_time, const bool nav_enabled, const bool valid) {
const uint32_t frame_age = (frame_id > vipc_frame_id) ? (frame_id - vipc_frame_id) : 0;
auto framed = msg.initEvent(valid).initModelV2();
framed.setFrameId(vipc_frame_id);
framed.setFrameIdExtra(vipc_frame_id_extra);
framed.setFrameAge(frame_age);
framed.setFrameDropPerc(frame_drop * 100);
framed.setTimestampEof(timestamp_eof);
framed.setLocationMonoTime(timestamp_llk);
framed.setModelExecutionTime(model_execution_time);
framed.setNavEnabled(nav_enabled);
if (send_raw_pred) {
framed.setRawPredictions(kj::ArrayPtr<const float>(net_output_data, NET_OUTPUT_SIZE).asBytes());
}
fill_model(framed, *((ModelOutput*) net_output_data), ps);
}
void fill_pose_msg(MessageBuilder &msg, float *net_output_data, uint32_t vipc_frame_id, uint32_t vipc_dropped_frames, uint64_t timestamp_eof, const bool valid) {
const ModelOutput &net_outputs = *((ModelOutput*) net_output_data);
const auto &v_mean = net_outputs.pose.velocity_mean;
const auto &r_mean = net_outputs.pose.rotation_mean;
const auto &t_mean = net_outputs.wide_from_device_euler.mean;
const auto &v_std = net_outputs.pose.velocity_std;
const auto &r_std = net_outputs.pose.rotation_std;
const auto &t_std = net_outputs.wide_from_device_euler.std;
const auto &road_transform_trans_mean = net_outputs.road_transform.position_mean;
const auto &road_transform_trans_std = net_outputs.road_transform.position_std;
auto posenetd = msg.initEvent(valid && (vipc_dropped_frames < 1)).initCameraOdometry();
posenetd.setTrans({v_mean.x, v_mean.y, v_mean.z});
posenetd.setRot({r_mean.x, r_mean.y, r_mean.z});
posenetd.setWideFromDeviceEuler({t_mean.x, t_mean.y, t_mean.z});
posenetd.setRoadTransformTrans({road_transform_trans_mean.x, road_transform_trans_mean.y, road_transform_trans_mean.z});
posenetd.setTransStd({exp(v_std.x), exp(v_std.y), exp(v_std.z)});
posenetd.setRotStd({exp(r_std.x), exp(r_std.y), exp(r_std.z)});
posenetd.setWideFromDeviceEulerStd({exp(t_std.x), exp(t_std.y), exp(t_std.z)});
posenetd.setRoadTransformTransStd({exp(road_transform_trans_std.x), exp(road_transform_trans_std.y), exp(road_transform_trans_std.z)});
posenetd.setTimestampEof(timestamp_eof);
posenetd.setFrameId(vipc_frame_id);
}

@ -1,273 +0,0 @@
#pragma once
#include <array>
#include <memory>
#include "cereal/messaging/messaging.h"
#include "common/modeldata.h"
#include "common/util.h"
#include "selfdrive/modeld/models/commonmodel.h"
#include "selfdrive/modeld/runners/run.h"
constexpr int FEATURE_LEN = 512;
constexpr int HISTORY_BUFFER_LEN = 99;
constexpr int DESIRE_LEN = 8;
constexpr int DESIRE_PRED_LEN = 4;
constexpr int TRAFFIC_CONVENTION_LEN = 2;
constexpr int LAT_PLANNER_STATE_LEN = 4;
constexpr int NAV_FEATURE_LEN = 256;
constexpr int NAV_INSTRUCTION_LEN = 150;
constexpr int DRIVING_STYLE_LEN = 12;
constexpr int MODEL_FREQ = 20;
constexpr int DISENGAGE_LEN = 5;
constexpr int BLINKER_LEN = 6;
constexpr int META_STRIDE = 7;
constexpr int PLAN_MHP_N = 5;
constexpr int LEAD_MHP_N = 2;
constexpr int LEAD_TRAJ_LEN = 6;
constexpr int LEAD_MHP_SELECTION = 3;
// Padding to get output shape as multiple of 4
constexpr int PAD_SIZE = 2;
constexpr float FCW_THRESHOLD_5MS2_HIGH = 0.15;
constexpr float FCW_THRESHOLD_5MS2_LOW = 0.05;
constexpr float FCW_THRESHOLD_3MS2 = 0.7;
struct ModelOutputXYZ {
float x;
float y;
float z;
};
static_assert(sizeof(ModelOutputXYZ) == sizeof(float)*3);
struct ModelOutputYZ {
float y;
float z;
};
static_assert(sizeof(ModelOutputYZ) == sizeof(float)*2);
struct LateralPlannerOutputElement {
float x;
float y;
float yaw;
float yaw_rate;
};
static_assert(sizeof(LateralPlannerOutputElement) == sizeof(float)*4);
struct LateralPlannerOutput {
std::array<LateralPlannerOutputElement, TRAJECTORY_SIZE> mean;
std::array<LateralPlannerOutputElement, TRAJECTORY_SIZE> std;
};
static_assert(sizeof(LateralPlannerOutput) == (sizeof(LateralPlannerOutputElement)*TRAJECTORY_SIZE*2));
struct ModelOutputPlanElement {
ModelOutputXYZ position;
ModelOutputXYZ velocity;
ModelOutputXYZ acceleration;
ModelOutputXYZ rotation;
ModelOutputXYZ rotation_rate;
};
static_assert(sizeof(ModelOutputPlanElement) == sizeof(ModelOutputXYZ)*5);
struct ModelOutputPlanPrediction {
std::array<ModelOutputPlanElement, TRAJECTORY_SIZE> mean;
std::array<ModelOutputPlanElement, TRAJECTORY_SIZE> std;
float prob;
};
static_assert(sizeof(ModelOutputPlanPrediction) == (sizeof(ModelOutputPlanElement)*TRAJECTORY_SIZE*2) + sizeof(float));
struct ModelOutputPlans {
std::array<ModelOutputPlanPrediction, PLAN_MHP_N> prediction;
constexpr const ModelOutputPlanPrediction &get_best_prediction() const {
int max_idx = 0;
for (int i = 1; i < prediction.size(); i++) {
if (prediction[i].prob > prediction[max_idx].prob) {
max_idx = i;
}
}
return prediction[max_idx];
}
};
static_assert(sizeof(ModelOutputPlans) == sizeof(ModelOutputPlanPrediction)*PLAN_MHP_N);
struct ModelOutputLinesXY {
std::array<ModelOutputYZ, TRAJECTORY_SIZE> left_far;
std::array<ModelOutputYZ, TRAJECTORY_SIZE> left_near;
std::array<ModelOutputYZ, TRAJECTORY_SIZE> right_near;
std::array<ModelOutputYZ, TRAJECTORY_SIZE> right_far;
};
static_assert(sizeof(ModelOutputLinesXY) == sizeof(ModelOutputYZ)*TRAJECTORY_SIZE*4);
struct ModelOutputLineProbVal {
float val_deprecated;
float val;
};
static_assert(sizeof(ModelOutputLineProbVal) == sizeof(float)*2);
struct ModelOutputLinesProb {
ModelOutputLineProbVal left_far;
ModelOutputLineProbVal left_near;
ModelOutputLineProbVal right_near;
ModelOutputLineProbVal right_far;
};
static_assert(sizeof(ModelOutputLinesProb) == sizeof(ModelOutputLineProbVal)*4);
struct ModelOutputLaneLines {
ModelOutputLinesXY mean;
ModelOutputLinesXY std;
ModelOutputLinesProb prob;
};
static_assert(sizeof(ModelOutputLaneLines) == (sizeof(ModelOutputLinesXY)*2) + sizeof(ModelOutputLinesProb));
struct ModelOutputEdgessXY {
std::array<ModelOutputYZ, TRAJECTORY_SIZE> left;
std::array<ModelOutputYZ, TRAJECTORY_SIZE> right;
};
static_assert(sizeof(ModelOutputEdgessXY) == sizeof(ModelOutputYZ)*TRAJECTORY_SIZE*2);
struct ModelOutputRoadEdges {
ModelOutputEdgessXY mean;
ModelOutputEdgessXY std;
};
static_assert(sizeof(ModelOutputRoadEdges) == (sizeof(ModelOutputEdgessXY)*2));
struct ModelOutputLeadElement {
float x;
float y;
float velocity;
float acceleration;
};
static_assert(sizeof(ModelOutputLeadElement) == sizeof(float)*4);
struct ModelOutputLeadPrediction {
std::array<ModelOutputLeadElement, LEAD_TRAJ_LEN> mean;
std::array<ModelOutputLeadElement, LEAD_TRAJ_LEN> std;
std::array<float, LEAD_MHP_SELECTION> prob;
};
static_assert(sizeof(ModelOutputLeadPrediction) == (sizeof(ModelOutputLeadElement)*LEAD_TRAJ_LEN*2) + (sizeof(float)*LEAD_MHP_SELECTION));
struct ModelOutputLeads {
std::array<ModelOutputLeadPrediction, LEAD_MHP_N> prediction;
std::array<float, LEAD_MHP_SELECTION> prob;
constexpr const ModelOutputLeadPrediction &get_best_prediction(int t_idx) const {
int max_idx = 0;
for (int i = 1; i < prediction.size(); i++) {
if (prediction[i].prob[t_idx] > prediction[max_idx].prob[t_idx]) {
max_idx = i;
}
}
return prediction[max_idx];
}
};
static_assert(sizeof(ModelOutputLeads) == (sizeof(ModelOutputLeadPrediction)*LEAD_MHP_N) + (sizeof(float)*LEAD_MHP_SELECTION));
struct ModelOutputPose {
ModelOutputXYZ velocity_mean;
ModelOutputXYZ rotation_mean;
ModelOutputXYZ velocity_std;
ModelOutputXYZ rotation_std;
};
static_assert(sizeof(ModelOutputPose) == sizeof(ModelOutputXYZ)*4);
struct ModelOutputWideFromDeviceEuler {
ModelOutputXYZ mean;
ModelOutputXYZ std;
};
static_assert(sizeof(ModelOutputWideFromDeviceEuler) == sizeof(ModelOutputXYZ)*2);
struct ModelOutputTemporalPose {
ModelOutputXYZ velocity_mean;
ModelOutputXYZ rotation_mean;
ModelOutputXYZ velocity_std;
ModelOutputXYZ rotation_std;
};
static_assert(sizeof(ModelOutputTemporalPose) == sizeof(ModelOutputXYZ)*4);
struct ModelOutputRoadTransform {
ModelOutputXYZ position_mean;
ModelOutputXYZ rotation_mean;
ModelOutputXYZ position_std;
ModelOutputXYZ rotation_std;
};
static_assert(sizeof(ModelOutputRoadTransform) == sizeof(ModelOutputXYZ)*4);
struct ModelOutputDisengageProb {
float gas_disengage;
float brake_disengage;
float steer_override;
float brake_3ms2;
float brake_4ms2;
float brake_5ms2;
float gas_pressed;
};
static_assert(sizeof(ModelOutputDisengageProb) == sizeof(float)*7);
struct ModelOutputBlinkerProb {
float left;
float right;
};
static_assert(sizeof(ModelOutputBlinkerProb) == sizeof(float)*2);
struct ModelOutputDesireProb {
union {
struct {
float none;
float turn_left;
float turn_right;
float lane_change_left;
float lane_change_right;
float keep_left;
float keep_right;
float null;
};
struct {
std::array<float, DESIRE_LEN> array;
};
};
};
static_assert(sizeof(ModelOutputDesireProb) == sizeof(float)*DESIRE_LEN);
struct ModelOutputMeta {
ModelOutputDesireProb desire_state_prob;
float engaged_prob;
std::array<ModelOutputDisengageProb, DISENGAGE_LEN> disengage_prob;
std::array<ModelOutputBlinkerProb, BLINKER_LEN> blinker_prob;
std::array<ModelOutputDesireProb, DESIRE_PRED_LEN> desire_pred_prob;
};
static_assert(sizeof(ModelOutputMeta) == sizeof(ModelOutputDesireProb) + sizeof(float) + (sizeof(ModelOutputDisengageProb)*DISENGAGE_LEN) + (sizeof(ModelOutputBlinkerProb)*BLINKER_LEN) + (sizeof(ModelOutputDesireProb)*DESIRE_PRED_LEN));
struct ModelOutputFeatures {
std::array<float, FEATURE_LEN> feature;
};
static_assert(sizeof(ModelOutputFeatures) == (sizeof(float)*FEATURE_LEN));
struct ModelOutput {
const ModelOutputPlans plans;
const ModelOutputLaneLines lane_lines;
const ModelOutputRoadEdges road_edges;
const ModelOutputLeads leads;
const ModelOutputMeta meta;
const ModelOutputPose pose;
const ModelOutputWideFromDeviceEuler wide_from_device_euler;
const ModelOutputTemporalPose temporal_pose;
const ModelOutputRoadTransform road_transform;
const LateralPlannerOutput lateral_planner_solution;
};
constexpr int OUTPUT_SIZE = sizeof(ModelOutput) / sizeof(float);
constexpr int NET_OUTPUT_SIZE = OUTPUT_SIZE + FEATURE_LEN + PAD_SIZE;
struct PublishState {
std::array<float, DISENGAGE_LEN * DISENGAGE_LEN> disengage_buffer = {};
std::array<float, 5> prev_brake_5ms2_probs = {};
std::array<float, 3> prev_brake_3ms2_probs = {};
};
void fill_model_msg(MessageBuilder &msg, float *net_output_data, PublishState &ps, uint32_t vipc_frame_id, uint32_t vipc_frame_id_extra, uint32_t frame_id, float frame_drop,
uint64_t timestamp_eof, uint64_t timestamp_llk, float model_execution_time, const bool nav_enabled, const bool valid);
void fill_pose_msg(MessageBuilder &msg, float *net_outputs, uint32_t vipc_frame_id, uint32_t vipc_dropped_frames, uint64_t timestamp_eof, const bool valid);

@ -1,26 +0,0 @@
# distutils: language = c++
from libcpp cimport bool
from libc.stdint cimport uint32_t, uint64_t
cdef extern from "cereal/messaging/messaging.h":
cdef cppclass MessageBuilder:
size_t getSerializedSize()
int serializeToBuffer(unsigned char *, size_t)
cdef extern from "selfdrive/modeld/models/driving.h":
cdef int FEATURE_LEN
cdef int HISTORY_BUFFER_LEN
cdef int DESIRE_LEN
cdef int TRAFFIC_CONVENTION_LEN
cdef int LAT_PLANNER_STATE_LEN
cdef int DRIVING_STYLE_LEN
cdef int NAV_FEATURE_LEN
cdef int NAV_INSTRUCTION_LEN
cdef int OUTPUT_SIZE
cdef int NET_OUTPUT_SIZE
cdef int MODEL_FREQ
cdef struct PublishState: pass
void fill_model_msg(MessageBuilder, float *, PublishState, uint32_t, uint32_t, uint32_t, float, uint64_t, uint64_t, float, bool, bool)
void fill_pose_msg(MessageBuilder, float *, uint32_t, uint32_t, uint64_t, bool)

@ -1,54 +0,0 @@
# distutils: language = c++
# cython: c_string_encoding=ascii
import numpy as np
cimport numpy as cnp
from libcpp cimport bool
from libc.string cimport memcpy
from libc.stdint cimport uint32_t, uint64_t
from .commonmodel cimport mat3
from .driving cimport FEATURE_LEN as CPP_FEATURE_LEN, HISTORY_BUFFER_LEN as CPP_HISTORY_BUFFER_LEN, DESIRE_LEN as CPP_DESIRE_LEN, \
TRAFFIC_CONVENTION_LEN as CPP_TRAFFIC_CONVENTION_LEN, DRIVING_STYLE_LEN as CPP_DRIVING_STYLE_LEN, \
NAV_FEATURE_LEN as CPP_NAV_FEATURE_LEN, NAV_INSTRUCTION_LEN as CPP_NAV_INSTRUCTION_LEN, \
LAT_PLANNER_STATE_LEN as CPP_LAT_PLANNER_STATE_LEN, \
OUTPUT_SIZE as CPP_OUTPUT_SIZE, NET_OUTPUT_SIZE as CPP_NET_OUTPUT_SIZE, MODEL_FREQ as CPP_MODEL_FREQ
from .driving cimport MessageBuilder, PublishState as cppPublishState
from .driving cimport fill_model_msg, fill_pose_msg
FEATURE_LEN = CPP_FEATURE_LEN
HISTORY_BUFFER_LEN = CPP_HISTORY_BUFFER_LEN
DESIRE_LEN = CPP_DESIRE_LEN
TRAFFIC_CONVENTION_LEN = CPP_TRAFFIC_CONVENTION_LEN
LAT_PLANNER_STATE_LEN = CPP_LAT_PLANNER_STATE_LEN
DRIVING_STYLE_LEN = CPP_DRIVING_STYLE_LEN
NAV_FEATURE_LEN = CPP_NAV_FEATURE_LEN
NAV_INSTRUCTION_LEN = CPP_NAV_INSTRUCTION_LEN
OUTPUT_SIZE = CPP_OUTPUT_SIZE
NET_OUTPUT_SIZE = CPP_NET_OUTPUT_SIZE
MODEL_FREQ = CPP_MODEL_FREQ
cdef class PublishState:
cdef cppPublishState state
def create_model_msg(float[:] model_outputs, PublishState ps, uint32_t vipc_frame_id, uint32_t vipc_frame_id_extra, uint32_t frame_id, float frame_drop,
uint64_t timestamp_eof, uint64_t timestamp_llk, float model_execution_time, bool nav_enabled, bool valid):
cdef MessageBuilder msg
fill_model_msg(msg, &model_outputs[0], ps.state, vipc_frame_id, vipc_frame_id_extra, frame_id, frame_drop,
timestamp_eof, timestamp_llk, model_execution_time, nav_enabled, valid)
output_size = msg.getSerializedSize()
output_data = bytearray(output_size)
cdef unsigned char * output_ptr = output_data
assert msg.serializeToBuffer(output_ptr, output_size) > 0, "output buffer is too small to serialize"
return bytes(output_data)
def create_pose_msg(float[:] model_outputs, uint32_t vipc_frame_id, uint32_t vipc_dropped_frames, uint64_t timestamp_eof, bool valid):
cdef MessageBuilder msg
fill_pose_msg(msg, &model_outputs[0], vipc_frame_id, vipc_dropped_frames, timestamp_eof, valid)
output_size = msg.getSerializedSize()
output_data = bytearray(output_size)
cdef unsigned char * output_ptr = output_data
assert msg.serializeToBuffer(output_ptr, output_size) > 0, "output buffer is too small to serialize"
return bytes(output_data)

@ -13,13 +13,13 @@ from cereal.visionipc import VisionIpcClient, VisionStreamType
from openpilot.system.swaglog import cloudlog from openpilot.system.swaglog import cloudlog
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.common.realtime import set_realtime_priority from openpilot.common.realtime import set_realtime_priority
from openpilot.selfdrive.modeld.constants import IDX_N from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime
NAV_INPUT_SIZE = 256*256 NAV_INPUT_SIZE = 256*256
NAV_FEATURE_LEN = 256 NAV_FEATURE_LEN = 256
NAV_DESIRE_LEN = 32 NAV_DESIRE_LEN = 32
NAV_OUTPUT_SIZE = 2*2*IDX_N + NAV_DESIRE_LEN + NAV_FEATURE_LEN NAV_OUTPUT_SIZE = 2*2*ModelConstants.IDX_N + NAV_DESIRE_LEN + NAV_FEATURE_LEN
MODEL_PATHS = { MODEL_PATHS = {
ModelRunner.SNPE: Path(__file__).parent / 'models/navmodel_q.dlc', ModelRunner.SNPE: Path(__file__).parent / 'models/navmodel_q.dlc',
ModelRunner.ONNX: Path(__file__).parent / 'models/navmodel.onnx'} ModelRunner.ONNX: Path(__file__).parent / 'models/navmodel.onnx'}
@ -31,8 +31,8 @@ class NavModelOutputXY(ctypes.Structure):
class NavModelOutputPlan(ctypes.Structure): class NavModelOutputPlan(ctypes.Structure):
_fields_ = [ _fields_ = [
("mean", NavModelOutputXY*IDX_N), ("mean", NavModelOutputXY*ModelConstants.IDX_N),
("std", NavModelOutputXY*IDX_N)] ("std", NavModelOutputXY*ModelConstants.IDX_N)]
class NavModelResult(ctypes.Structure): class NavModelResult(ctypes.Structure):
_fields_ = [ _fields_ = [

@ -0,0 +1,101 @@
import numpy as np
from typing import Dict
from openpilot.selfdrive.modeld.constants import ModelConstants
def sigmoid(x):
return 1. / (1. + np.exp(-x))
def softmax(x, axis=-1):
x -= np.max(x, axis=axis, keepdims=True)
if x.dtype == np.float32 or x.dtype == np.float64:
np.exp(x, out=x)
else:
x = np.exp(x)
x /= np.sum(x, axis=axis, keepdims=True)
return x
class Parser:
def __init__(self, ignore_missing=False):
self.ignore_missing = ignore_missing
def check_missing(self, outs, name):
if name not in outs and not self.ignore_missing:
raise ValueError(f"Missing output {name}")
return name not in outs
def parse_categorical_crossentropy(self, name, outs, out_shape=None):
if self.check_missing(outs, name):
return
raw = outs[name]
if out_shape is not None:
raw = raw.reshape((raw.shape[0],) + out_shape)
outs[name] = softmax(raw, axis=-1)
def parse_binary_crossentropy(self, name, outs):
if self.check_missing(outs, name):
return
raw = outs[name]
outs[name] = sigmoid(raw)
def parse_mdn(self, name, outs, in_N=0, out_N=1, out_shape=None):
if self.check_missing(outs, name):
return
raw = outs[name]
raw = raw.reshape((raw.shape[0], max(in_N, 1), -1))
pred_mu = raw[:,:,:(raw.shape[2] - out_N)//2]
n_values = (raw.shape[2] - out_N)//2
pred_mu = raw[:,:,:n_values]
pred_std = np.exp(raw[:,:,n_values: 2*n_values])
if in_N > 1:
weights = np.zeros((raw.shape[0], in_N, out_N), dtype=raw.dtype)
for i in range(out_N):
weights[:,:,i - out_N] = softmax(raw[:,:,i - out_N], axis=-1)
if out_N == 1:
for fidx in range(weights.shape[0]):
idxs = np.argsort(weights[fidx][:,0])[::-1]
weights[fidx] = weights[fidx][idxs]
pred_mu[fidx] = pred_mu[fidx][idxs]
pred_std[fidx] = pred_std[fidx][idxs]
full_shape = tuple([raw.shape[0], in_N] + list(out_shape))
outs[name + '_weights'] = weights
outs[name + '_hypotheses'] = pred_mu.reshape(full_shape)
outs[name + '_stds_hypotheses'] = pred_std.reshape(full_shape)
pred_mu_final = np.zeros((raw.shape[0], out_N, n_values), dtype=raw.dtype)
pred_std_final = np.zeros((raw.shape[0], out_N, n_values), dtype=raw.dtype)
for fidx in range(weights.shape[0]):
for hidx in range(out_N):
idxs = np.argsort(weights[fidx,:,hidx])[::-1]
pred_mu_final[fidx, hidx] = pred_mu[fidx, idxs[0]]
pred_std_final[fidx, hidx] = pred_std[fidx, idxs[0]]
else:
pred_mu_final = pred_mu
pred_std_final = pred_std
if out_N > 1:
final_shape = tuple([raw.shape[0], out_N] + list(out_shape))
else:
final_shape = tuple([raw.shape[0],] + list(out_shape))
outs[name] = pred_mu_final.reshape(final_shape)
outs[name + '_stds'] = pred_std_final.reshape(final_shape)
def parse_outputs(self, outs: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]:
self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION,
out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH))
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,))
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
for k in ['lead_prob', 'lane_lines_prob', 'meta']:
self.parse_binary_crossentropy(k, outs)
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,))
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH))
return outs

@ -1,32 +0,0 @@
import struct
import json
def load_thneed(fn):
with open(fn, "rb") as f:
json_len = struct.unpack("I", f.read(4))[0]
jdat = json.loads(f.read(json_len).decode('latin_1'))
weights = f.read()
ptr = 0
for o in jdat['objects']:
if o['needs_load']:
nptr = ptr + o['size']
o['data'] = weights[ptr:nptr]
ptr = nptr
for o in jdat['binaries']:
nptr = ptr + o['length']
o['data'] = weights[ptr:nptr]
ptr = nptr
return jdat
def save_thneed(jdat, fn):
new_weights = []
for o in jdat['objects'] + jdat['binaries']:
if 'data' in o:
new_weights.append(o['data'])
del o['data']
new_weights_bytes = b''.join(new_weights)
with open(fn, "wb") as f:
j = json.dumps(jdat, ensure_ascii=False).encode('latin_1')
f.write(struct.pack("I", len(j)))
f.write(j)
f.write(new_weights_bytes)

@ -10,15 +10,12 @@ from openpilot.selfdrive.controls.lib.events import Events
from openpilot.selfdrive.monitoring.driver_monitor import DriverStatus from openpilot.selfdrive.monitoring.driver_monitor import DriverStatus
def dmonitoringd_thread(sm=None, pm=None): def dmonitoringd_thread():
gc.disable() gc.disable()
set_realtime_priority(2) set_realtime_priority(2)
if pm is None: pm = messaging.PubMaster(['driverMonitoringState'])
pm = messaging.PubMaster(['driverMonitoringState']) sm = messaging.SubMaster(['driverStateV2', 'liveCalibration', 'carState', 'controlsState', 'modelV2'], poll=['driverStateV2'])
if sm is None:
sm = messaging.SubMaster(['driverStateV2', 'liveCalibration', 'carState', 'controlsState', 'modelV2'], poll=['driverStateV2'])
driver_status = DriverStatus(rhd_saved=Params().get_bool("IsRhdDetected")) driver_status = DriverStatus(rhd_saved=Params().get_bool("IsRhdDetected"))
@ -89,8 +86,8 @@ def dmonitoringd_thread(sm=None, pm=None):
driver_status.wheel_on_right == (driver_status.wheelpos_learner.filtered_stat.M > driver_status.settings._WHEELPOS_THRESHOLD)): driver_status.wheel_on_right == (driver_status.wheelpos_learner.filtered_stat.M > driver_status.settings._WHEELPOS_THRESHOLD)):
put_bool_nonblocking("IsRhdDetected", driver_status.wheel_on_right) put_bool_nonblocking("IsRhdDetected", driver_status.wheel_on_right)
def main(sm=None, pm=None): def main():
dmonitoringd_thread(sm, pm) dmonitoringd_thread()
if __name__ == '__main__': if __name__ == '__main__':

@ -344,11 +344,9 @@ class RouteEngine:
# TODO: Check for going wrong way in segment # TODO: Check for going wrong way in segment
def main(sm=None, pm=None): def main():
if sm is None: pm = messaging.PubMaster(['navInstruction', 'navRoute'])
sm = messaging.SubMaster(['liveLocationKalman', 'managerState']) sm = messaging.SubMaster(['liveLocationKalman', 'managerState'])
if pm is None:
pm = messaging.PubMaster(['navInstruction', 'navRoute'])
rk = Ratekeeper(1.0) rk = Ratekeeper(1.0)
route_engine = RouteEngine(sm, pm) route_engine = RouteEngine(sm, pm)

@ -1,9 +1,6 @@
if [ $1 = "base" ]; then if [ $1 = "base" ]; then
export DOCKER_IMAGE=openpilot-base export DOCKER_IMAGE=openpilot-base
export DOCKER_FILE=Dockerfile.openpilot_base export DOCKER_FILE=Dockerfile.openpilot_base
elif [ $1 = "docs" ]; then
export DOCKER_IMAGE=openpilot-docs
export DOCKER_FILE=docs/docker/Dockerfile
elif [ $1 = "sim" ]; then elif [ $1 = "sim" ]; then
export DOCKER_IMAGE=openpilot-sim export DOCKER_IMAGE=openpilot-sim
export DOCKER_FILE=tools/sim/Dockerfile.sim export DOCKER_FILE=tools/sim/Dockerfile.sim

@ -6,7 +6,7 @@ from cereal import log
import cereal.messaging as messaging import cereal.messaging as messaging
from openpilot.common.realtime import Ratekeeper, DT_MDL from openpilot.common.realtime import Ratekeeper, DT_MDL
from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState
from openpilot.selfdrive.modeld.constants import T_IDXS from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner
from openpilot.selfdrive.controls.radard import _LEAD_ACCEL_TAU from openpilot.selfdrive.controls.radard import _LEAD_ACCEL_TAU
@ -100,13 +100,13 @@ class Plant:
# this is to ensure lead policy is effective when model # this is to ensure lead policy is effective when model
# does not predict slowdown in e2e mode # does not predict slowdown in e2e mode
position = log.XYZTData.new_message() position = log.XYZTData.new_message()
position.x = [float(x) for x in (self.speed + 0.5) * np.array(T_IDXS)] position.x = [float(x) for x in (self.speed + 0.5) * np.array(ModelConstants.T_IDXS)]
model.modelV2.position = position model.modelV2.position = position
velocity = log.XYZTData.new_message() velocity = log.XYZTData.new_message()
velocity.x = [float(x) for x in (self.speed + 0.5) * np.ones_like(T_IDXS)] velocity.x = [float(x) for x in (self.speed + 0.5) * np.ones_like(ModelConstants.T_IDXS)]
model.modelV2.velocity = velocity model.modelV2.velocity = velocity
acceleration = log.XYZTData.new_message() acceleration = log.XYZTData.new_message()
acceleration.x = [float(x) for x in np.zeros_like(T_IDXS)] acceleration.x = [float(x) for x in np.zeros_like(ModelConstants.T_IDXS)]
model.modelV2.acceleration = acceleration model.modelV2.acceleration = acceleration
control.controlsState.longControlState = LongCtrlState.pid if self.enabled else LongCtrlState.off control.controlsState.longControlState = LongCtrlState.pid if self.enabled else LongCtrlState.off

@ -52,10 +52,10 @@ Then, check in the new logs using git-lfs. Make sure to also update the `ref_com
Process replay test suite exposes programmatic APIs for simultaneously running processes or groups of processes on provided logs. Process replay test suite exposes programmatic APIs for simultaneously running processes or groups of processes on provided logs.
```py ```py
def replay_process_with_name(name: Union[str, Iterable[str]], lr: Union[LogReader, List[capnp._DynamicStructReader]], *args, **kwargs) -> List[capnp._DynamicStructReader]: def replay_process_with_name(name: Union[str, Iterable[str]], lr: LogIterable, *args, **kwargs) -> List[capnp._DynamicStructReader]:
def replay_process( def replay_process(
cfg: Union[ProcessConfig, Iterable[ProcessConfig]], lr: Union[LogReader, List[capnp._DynamicStructReader]], frs: Optional[Dict[str, Any]] = None, cfg: Union[ProcessConfig, Iterable[ProcessConfig]], lr: LogIterable, frs: Optional[Dict[str, Any]] = None,
fingerprint: Optional[str] = None, return_all_logs: bool = False, custom_params: Optional[Dict[str, Any]] = None, disable_progress: bool = False fingerprint: Optional[str] = None, return_all_logs: bool = False, custom_params: Optional[Dict[str, Any]] = None, disable_progress: bool = False
) -> List[capnp._DynamicStructReader]: ) -> List[capnp._DynamicStructReader]:
``` ```

@ -90,43 +90,53 @@ def compare_logs(log1, log2, ignore_fields=None, ignore_msgs=None, tolerance=Non
return diff return diff
def format_process_diff(diff):
diff_short, diff_long = "", ""
if isinstance(diff, str):
diff_short += f" {diff}\n"
diff_long += f"\t{diff}\n"
else:
cnt: Dict[str, int] = {}
for d in diff:
diff_long += f"\t{str(d)}\n"
k = str(d[1])
cnt[k] = 1 if k not in cnt else cnt[k] + 1
for k, v in sorted(cnt.items()):
diff_short += f" {k}: {v}\n"
return diff_short, diff_long
def format_diff(results, log_paths, ref_commit): def format_diff(results, log_paths, ref_commit):
diff1, diff2 = "", "" diff_short, diff_long = "", ""
diff2 += f"***** tested against commit {ref_commit} *****\n" diff_long += f"***** tested against commit {ref_commit} *****\n"
failed = False failed = False
for segment, result in list(results.items()): for segment, result in list(results.items()):
diff1 += f"***** results for segment {segment} *****\n" diff_short += f"***** results for segment {segment} *****\n"
diff2 += f"***** differences for segment {segment} *****\n" diff_long += f"***** differences for segment {segment} *****\n"
for proc, diff in list(result.items()): for proc, diff in list(result.items()):
# long diff diff_long += f"*** process: {proc} ***\n"
diff2 += f"*** process: {proc} ***\n" diff_long += f"\tref: {log_paths[segment][proc]['ref']}\n"
diff2 += f"\tref: {log_paths[segment][proc]['ref']}\n" diff_long += f"\tnew: {log_paths[segment][proc]['new']}\n\n"
diff2 += f"\tnew: {log_paths[segment][proc]['new']}\n\n"
diff_short += f" {proc}\n"
# short diff
diff1 += f" {proc}\n" if isinstance(diff, str) or len(diff):
if isinstance(diff, str): diff_short += f" ref: {log_paths[segment][proc]['ref']}\n"
diff1 += f" ref: {log_paths[segment][proc]['ref']}\n" diff_short += f" new: {log_paths[segment][proc]['new']}\n\n"
diff1 += f" new: {log_paths[segment][proc]['new']}\n\n"
diff1 += f" {diff}\n"
failed = True failed = True
elif len(diff):
diff1 += f" ref: {log_paths[segment][proc]['ref']}\n"
diff1 += f" new: {log_paths[segment][proc]['new']}\n\n"
cnt: Dict[str, int] = {} proc_diff_short, proc_diff_long = format_process_diff(diff)
for d in diff:
diff2 += f"\t{str(d)}\n"
k = str(d[1]) diff_long += proc_diff_long
cnt[k] = 1 if k not in cnt else cnt[k] + 1 diff_short += proc_diff_short
for k, v in sorted(cnt.items()): return diff_short, diff_long, failed
diff1 += f" {k}: {v}\n"
failed = True
return diff1, diff2, failed
if __name__ == "__main__": if __name__ == "__main__":
@ -135,7 +145,7 @@ if __name__ == "__main__":
ignore_fields = sys.argv[3:] or ["logMonoTime", "controlsState.startMonoTime", "controlsState.cumLagMs"] ignore_fields = sys.argv[3:] or ["logMonoTime", "controlsState.startMonoTime", "controlsState.cumLagMs"]
results = {"segment": {"proc": compare_logs(log1, log2, ignore_fields)}} results = {"segment": {"proc": compare_logs(log1, log2, ignore_fields)}}
log_paths = {"segment": {"proc": {"ref": sys.argv[1], "new": sys.argv[2]}}} log_paths = {"segment": {"proc": {"ref": sys.argv[1], "new": sys.argv[2]}}}
diff1, diff2, failed = format_diff(results, log_paths, None) diff_short, diff_long, failed = format_diff(results, log_paths, None)
print(diff2) print(diff_long)
print(diff1) print(diff_short)

@ -212,13 +212,13 @@ if __name__ == "__main__":
results: Any = {TEST_ROUTE: {}} results: Any = {TEST_ROUTE: {}}
log_paths: Any = {TEST_ROUTE: {"models": {'ref': BASE_URL + log_fn, 'new': log_fn}}} log_paths: Any = {TEST_ROUTE: {"models": {'ref': BASE_URL + log_fn, 'new': log_fn}}}
results[TEST_ROUTE]["models"] = compare_logs(cmp_log, log_msgs, tolerance=tolerance, ignore_fields=ignore) results[TEST_ROUTE]["models"] = compare_logs(cmp_log, log_msgs, tolerance=tolerance, ignore_fields=ignore)
diff1, diff2, failed = format_diff(results, log_paths, ref_commit) diff_short, diff_long, failed = format_diff(results, log_paths, ref_commit)
print(diff2) print(diff_long)
print('-------------\n'*5) print('-------------\n'*5)
print(diff1) print(diff_short)
with open("model_diff.txt", "w") as f: with open("model_diff.txt", "w") as f:
f.write(diff2) f.write(diff_long)
except Exception as e: except Exception as e:
print(str(e)) print(str(e))
failed = True failed = True

@ -1 +1 @@
ed2d58ec217fafb7b6b8f5e27ec622acd9e734f4 0e0f55cf3bb2cf79b44adf190e6387a83deb6646

@ -26,7 +26,7 @@ from openpilot.selfdrive.manager.process_config import managed_processes
from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_camera_state, available_streams from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_camera_state, available_streams
from openpilot.selfdrive.test.process_replay.migration import migrate_all from openpilot.selfdrive.test.process_replay.migration import migrate_all
from openpilot.selfdrive.test.process_replay.capture import ProcessOutputCapture from openpilot.selfdrive.test.process_replay.capture import ProcessOutputCapture
from openpilot.tools.lib.logreader import LogReader from openpilot.tools.lib.logreader import LogIterable
# Numpy gives different results based on CPU features after version 19 # Numpy gives different results based on CPU features after version 19
NUMPY_TOLERANCE = 1e-7 NUMPY_TOLERANCE = 1e-7
@ -224,7 +224,7 @@ class ProcessContainer:
def start( def start(
self, params_config: Dict[str, Any], environ_config: Dict[str, Any], self, params_config: Dict[str, Any], environ_config: Dict[str, Any],
all_msgs: Union[LogReader, List[capnp._DynamicStructReader]], all_msgs: LogIterable,
fingerprint: Optional[str], capture_output: bool fingerprint: Optional[str], capture_output: bool
): ):
with self.prefix as p: with self.prefix as p:
@ -599,7 +599,7 @@ def get_process_config(name: str) -> ProcessConfig:
raise Exception(f"Cannot find process config with name: {name}") from ex raise Exception(f"Cannot find process config with name: {name}") from ex
def get_custom_params_from_lr(lr: Union[LogReader, List[capnp._DynamicStructReader]], initial_state: str = "first") -> Dict[str, Any]: def get_custom_params_from_lr(lr: LogIterable, initial_state: str = "first") -> Dict[str, Any]:
""" """
Use this to get custom params dict based on provided logs. Use this to get custom params dict based on provided logs.
Useful when replaying following processes: calibrationd, paramsd, torqued Useful when replaying following processes: calibrationd, paramsd, torqued
@ -631,8 +631,7 @@ def get_custom_params_from_lr(lr: Union[LogReader, List[capnp._DynamicStructRead
return custom_params return custom_params
def replay_process_with_name(name: Union[str, Iterable[str]], lr: Union[LogReader, def replay_process_with_name(name: Union[str, Iterable[str]], lr: LogIterable, *args, **kwargs) -> List[capnp._DynamicStructReader]:
List[capnp._DynamicStructReader]], *args, **kwargs) -> List[capnp._DynamicStructReader]:
if isinstance(name, str): if isinstance(name, str):
cfgs = [get_process_config(name)] cfgs = [get_process_config(name)]
elif isinstance(name, Iterable): elif isinstance(name, Iterable):
@ -644,7 +643,7 @@ def replay_process_with_name(name: Union[str, Iterable[str]], lr: Union[LogReade
def replay_process( def replay_process(
cfg: Union[ProcessConfig, Iterable[ProcessConfig]], lr: Union[LogReader, List[capnp._DynamicStructReader]], frs: Optional[Dict[str, Any]] = None, cfg: Union[ProcessConfig, Iterable[ProcessConfig]], lr: LogIterable, frs: Optional[Dict[str, Any]] = None,
fingerprint: Optional[str] = None, return_all_logs: bool = False, custom_params: Optional[Dict[str, Any]] = None, fingerprint: Optional[str] = None, return_all_logs: bool = False, custom_params: Optional[Dict[str, Any]] = None,
captured_output_store: Optional[Dict[str, Dict[str, str]]] = None, disable_progress: bool = False captured_output_store: Optional[Dict[str, Dict[str, str]]] = None, disable_progress: bool = False
) -> List[capnp._DynamicStructReader]: ) -> List[capnp._DynamicStructReader]:
@ -672,7 +671,7 @@ def replay_process(
def _replay_multi_process( def _replay_multi_process(
cfgs: List[ProcessConfig], lr: Union[LogReader, List[capnp._DynamicStructReader]], frs: Optional[Dict[str, Any]], fingerprint: Optional[str], cfgs: List[ProcessConfig], lr: LogIterable, frs: Optional[Dict[str, Any]], fingerprint: Optional[str],
custom_params: Optional[Dict[str, Any]], captured_output_store: Optional[Dict[str, Dict[str, str]]], disable_progress: bool custom_params: Optional[Dict[str, Any]], captured_output_store: Optional[Dict[str, Dict[str, str]]], disable_progress: bool
) -> List[capnp._DynamicStructReader]: ) -> List[capnp._DynamicStructReader]:
if fingerprint is not None: if fingerprint is not None:
@ -799,7 +798,7 @@ def generate_environ_config(CP=None, fingerprint=None, log_dir=None) -> Dict[str
return environ_dict return environ_dict
def check_openpilot_enabled(msgs: Union[LogReader, List[capnp._DynamicStructReader]]) -> bool: def check_openpilot_enabled(msgs: LogIterable) -> bool:
cur_enabled_count = 0 cur_enabled_count = 0
max_enabled_count = 0 max_enabled_count = 0
for msg in msgs: for msg in msgs:

@ -11,12 +11,12 @@ from openpilot.selfdrive.test.process_replay.process_replay import CONFIGS, FAKE
from openpilot.selfdrive.test.update_ci_routes import upload_route from openpilot.selfdrive.test.update_ci_routes import upload_route
from openpilot.tools.lib.route import Route from openpilot.tools.lib.route import Route
from openpilot.tools.lib.framereader import FrameReader from openpilot.tools.lib.framereader import FrameReader
from openpilot.tools.lib.logreader import LogReader from openpilot.tools.lib.logreader import LogReader, LogIterable
from openpilot.tools.lib.helpers import save_log from openpilot.tools.lib.helpers import save_log
def regen_segment( def regen_segment(
lr: Union[LogReader, List[capnp._DynamicStructReader]], frs: Optional[Dict[str, Any]] = None, lr: LogIterable, frs: Optional[Dict[str, Any]] = None,
processes: Iterable[ProcessConfig] = CONFIGS, disable_tqdm: bool = False processes: Iterable[ProcessConfig] = CONFIGS, disable_tqdm: bool = False
) -> List[capnp._DynamicStructReader]: ) -> List[capnp._DynamicStructReader]:
all_msgs = sorted(lr, key=lambda m: m.logMonoTime) all_msgs = sorted(lr, key=lambda m: m.logMonoTime)

@ -61,7 +61,7 @@ segments = [
] ]
# dashcamOnly makes don't need to be tested until a full port is done # dashcamOnly makes don't need to be tested until a full port is done
excluded_interfaces = ["mock", "mazda", "tesla"] excluded_interfaces = ["mock", "tesla"]
BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/" BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/"
REF_COMMIT_FN = os.path.join(PROC_REPLAY_DIR, "ref_commit") REF_COMMIT_FN = os.path.join(PROC_REPLAY_DIR, "ref_commit")
@ -207,11 +207,11 @@ if __name__ == "__main__":
if not args.upload_only: if not args.upload_only:
results[segment][proc] = result results[segment][proc] = result
diff1, diff2, failed = format_diff(results, log_paths, ref_commit) diff_short, diff_long, failed = format_diff(results, log_paths, ref_commit)
if not upload: if not upload:
with open(os.path.join(PROC_REPLAY_DIR, "diff.txt"), "w") as f: with open(os.path.join(PROC_REPLAY_DIR, "diff.txt"), "w") as f:
f.write(diff2) f.write(diff_long)
print(diff1) print(diff_short)
if failed: if failed:
print("TEST FAILED") print("TEST FAILED")

@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import bz2
import math import math
import json import json
import os import os
@ -34,9 +35,9 @@ PROCS = {
"selfdrive.controls.plannerd": 16.5, "selfdrive.controls.plannerd": 16.5,
"./_ui": 18.0, "./_ui": 18.0,
"selfdrive.locationd.paramsd": 9.0, "selfdrive.locationd.paramsd": 9.0,
"./_sensord": 7.0, "./sensord": 7.0,
"selfdrive.controls.radard": 4.5, "selfdrive.controls.radard": 4.5,
"selfdrive.modeld.modeld": 8.0, "selfdrive.modeld.modeld": 13.0,
"selfdrive.modeld.dmonitoringmodeld": 8.0, "selfdrive.modeld.dmonitoringmodeld": 8.0,
"selfdrive.modeld.navmodeld": 1.0, "selfdrive.modeld.navmodeld": 1.0,
"selfdrive.thermald.thermald": 3.87, "selfdrive.thermald.thermald": 3.87,
@ -161,6 +162,7 @@ class TestOnroad(unittest.TestCase):
# use the second segment by default as it's the first full segment # use the second segment by default as it's the first full segment
cls.lr = list(LogReader(os.path.join(str(cls.segments[1]), "rlog"))) cls.lr = list(LogReader(os.path.join(str(cls.segments[1]), "rlog")))
cls.log_path = cls.segments[1]
@cached_property @cached_property
def service_msgs(self): def service_msgs(self):
@ -191,6 +193,26 @@ class TestOnroad(unittest.TestCase):
big_logs = [f for f, n in cnt.most_common(3) if n / sum(cnt.values()) > 30.] big_logs = [f for f, n in cnt.most_common(3) if n / sum(cnt.values()) > 30.]
self.assertEqual(len(big_logs), 0, f"Log spam: {big_logs}") self.assertEqual(len(big_logs), 0, f"Log spam: {big_logs}")
def test_log_sizes(self):
for f in self.log_path.iterdir():
assert f.is_file()
sz = f.stat().st_size / 1e6
if f.name in ("qlog", "rlog"):
with open(f, 'rb') as ff:
sz = len(bz2.compress(ff.read())) / 1e6
if f.name == "qcamera.ts":
assert 2.15 < sz < 2.35
elif f.name == "qlog":
assert 0.7 < sz < 1.0
elif f.name == "rlog":
assert 5 < sz < 50
elif f.name.endswith('.hevc'):
assert 70 < sz < 77
else:
raise NotImplementedError
def test_ui_timings(self): def test_ui_timings(self):
result = "\n" result = "\n"
result += "------------------------------------------------\n" result += "------------------------------------------------\n"

@ -1,5 +1,4 @@
#!/bin/sh #!/bin/sh
cd "$(dirname "$0")" cd "$(dirname "$0")"
export LD_LIBRARY_PATH="/system/lib64:$LD_LIBRARY_PATH"
export QT_QPA_PLATFORM="offscreen" export QT_QPA_PLATFORM="offscreen"
exec ./_soundd exec ./_soundd

@ -4,5 +4,4 @@ if [ -f /TICI ] && [ ! -f qt/spinner ]; then
cp qt/spinner_larch64 qt/spinner cp qt/spinner_larch64 qt/spinner
fi fi
export LD_LIBRARY_PATH="/system/lib64:$LD_LIBRARY_PATH"
exec ./qt/spinner "$1" exec ./qt/spinner "$1"

@ -4,5 +4,4 @@ if [ -f /TICI ] && [ ! -f qt/text ]; then
cp qt/text_larch64 qt/text cp qt/text_larch64 qt/text
fi fi
export LD_LIBRARY_PATH="/system/lib64:$LD_LIBRARY_PATH"
exec ./qt/text "$1" exec ./qt/text "$1"

@ -1,5 +1,4 @@
#!/bin/sh #!/bin/sh
cd "$(dirname "$0")" cd "$(dirname "$0")"
export LD_LIBRARY_PATH="/system/lib64:$LD_LIBRARY_PATH"
export QT_DBL_CLICK_DIST=150 export QT_DBL_CLICK_DIST=150
exec ./_ui exec ./_ui

@ -26,7 +26,7 @@ typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert;
const mat3 DEFAULT_CALIBRATION = {{ 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0 }}; const mat3 DEFAULT_CALIBRATION = {{ 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0 }};
const vec3 default_face_kpts_3d[] = { constexpr vec3 default_face_kpts_3d[] = {
{-5.98, -51.20, 8.00}, {-17.64, -49.14, 8.00}, {-23.81, -46.40, 8.00}, {-29.98, -40.91, 8.00}, {-32.04, -37.49, 8.00}, {-5.98, -51.20, 8.00}, {-17.64, -49.14, 8.00}, {-23.81, -46.40, 8.00}, {-29.98, -40.91, 8.00}, {-32.04, -37.49, 8.00},
{-34.10, -32.00, 8.00}, {-36.16, -21.03, 8.00}, {-36.16, 6.40, 8.00}, {-35.47, 10.51, 8.00}, {-32.73, 19.43, 8.00}, {-34.10, -32.00, 8.00}, {-36.16, -21.03, 8.00}, {-36.16, 6.40, 8.00}, {-35.47, 10.51, 8.00}, {-32.73, 19.43, 8.00},
{-29.30, 26.29, 8.00}, {-24.50, 33.83, 8.00}, {-19.01, 41.37, 8.00}, {-14.21, 46.17, 8.00}, {-12.16, 47.54, 8.00}, {-29.30, 26.29, 8.00}, {-24.50, 33.83, 8.00}, {-19.01, 41.37, 8.00}, {-14.21, 46.17, 8.00}, {-12.16, 47.54, 8.00},

@ -0,0 +1,115 @@
#!/usr/bin/env python3
import os
import math
import time
import binascii
import requests
import serial
import subprocess
def post(url, payload):
print()
print("POST to", url)
r = requests.post(
url,
data=payload,
verify=False,
headers={
"Content-Type": "application/json",
"X-Admin-Protocol": "gsma/rsp/v2.2.0",
"charset": "utf-8",
"User-Agent": "gsma-rsp-lpad",
},
)
print("resp", r)
print("resp text", repr(r.text))
print()
r.raise_for_status()
ret = f"HTTP/1.1 {r.status_code}"
ret += ''.join(f"{k}: {v}" for k, v in r.headers.items() if k != 'Connection')
return ret.encode() + r.content
class LPA:
def __init__(self):
self.dev = serial.Serial('/dev/ttyUSB2', baudrate=57600, timeout=1, bytesize=8)
self.dev.reset_input_buffer()
self.dev.reset_output_buffer()
assert "OK" in self.at("AT")
def at(self, cmd):
print(f"==> {cmd}")
self.dev.write(cmd.encode() + b'\r\n')
r = b""
cnt = 0
while b"OK" not in r and b"ERROR" not in r and cnt < 20:
r += self.dev.read(8192).strip()
cnt += 1
r = r.decode()
print(f"<== {repr(r)}")
return r
def download_ota(self, qr):
return self.at(f'AT+QESIM="ota","{qr}"')
def download(self, qr):
smdp = qr.split('$')[1]
out = self.at(f'AT+QESIM="download","{qr}"')
for _ in range(5):
line = out.split("+QESIM: ")[1].split("\r\n\r\nOK")[0]
parts = [x.strip().strip('"') for x in line.split(',', maxsplit=4)]
print(repr(parts))
trans, ret, url, payloadlen, payload = parts
assert trans == "trans" and ret == "0"
assert len(payload) == int(payloadlen)
r = post(f"https://{smdp}/{url}", payload)
to_send = binascii.hexlify(r).decode()
chunk_len = 1400
for i in range(math.ceil(len(to_send) / chunk_len)):
state = 1 if (i+1)*chunk_len < len(to_send) else 0
data = to_send[i * chunk_len : (i+1)*chunk_len]
out = self.at(f'AT+QESIM="trans",{len(to_send)},{state},{i},{len(data)},"{data}"')
assert "OK" in out
if '+QESIM:"download",1' in out:
raise Exception("profile install failed")
elif '+QESIM:"download",0' in out:
print("done, successfully loaded")
break
def enable(self, iccid):
self.at(f'AT+QESIM="enable","{iccid}"')
def disable(self, iccid):
self.at(f'AT+QESIM="disable","{iccid}"')
def delete(self, iccid):
self.at(f'AT+QESIM="delete","{iccid}"')
def list_profiles(self):
out = self.at('AT+QESIM="list"')
return out.strip().splitlines()[1:]
if __name__ == "__main__":
import sys
if "RESTART" in os.environ:
subprocess.check_call("sudo systemctl stop ModemManager", shell=True)
subprocess.check_call("/usr/comma/lte/lte.sh stop_blocking", shell=True)
subprocess.check_call("/usr/comma/lte/lte.sh start", shell=True)
while not os.path.exists('/dev/ttyUSB2'):
time.sleep(1)
time.sleep(3)
lpa = LPA()
print(lpa.list_profiles())
if len(sys.argv) > 1:
lpa.download(sys.argv[1])
print(lpa.list_profiles())

@ -28,7 +28,7 @@ class Proc:
PROCS = [ PROCS = [
Proc('camerad', 2.1, msgs=['roadCameraState', 'wideRoadCameraState', 'driverCameraState']), Proc('camerad', 2.1, msgs=['roadCameraState', 'wideRoadCameraState', 'driverCameraState']),
Proc('modeld', 0.93, atol=0.2, msgs=['modelV2']), Proc('modeld', 1.0, atol=0.2, msgs=['modelV2']),
Proc('dmonitoringmodeld', 0.4, msgs=['driverStateV2']), Proc('dmonitoringmodeld', 0.4, msgs=['driverStateV2']),
Proc('encoderd', 0.23, msgs=[]), Proc('encoderd', 0.23, msgs=[]),
Proc('mapsd', 0.05, msgs=['mapRenderState']), Proc('mapsd', 0.05, msgs=['mapRenderState']),

@ -24,9 +24,12 @@
// ***** log metadata ***** // ***** log metadata *****
kj::Array<capnp::word> logger_build_init_data() { kj::Array<capnp::word> logger_build_init_data() {
uint64_t wall_time = nanos_since_epoch();
MessageBuilder msg; MessageBuilder msg;
auto init = msg.initEvent().initInitData(); auto init = msg.initEvent().initInitData();
init.setWallTimeNanos(wall_time);
init.setVersion(COMMA_VERSION); init.setVersion(COMMA_VERSION);
init.setDirty(!getenv("CLEAN")); init.setDirty(!getenv("CLEAN"));
init.setDeviceType(Hardware::get_device_type()); init.setDeviceType(Hardware::get_device_type());

@ -40,9 +40,9 @@ def apply_a_weighting(measurements: np.ndarray) -> np.ndarray:
class Mic: class Mic:
def __init__(self, pm): def __init__(self):
self.pm = pm
self.rk = Ratekeeper(RATE) self.rk = Ratekeeper(RATE)
self.pm = messaging.PubMaster(['microphone'])
self.measurements = np.empty(0) self.measurements = np.empty(0)
@ -93,11 +93,8 @@ class Mic:
self.update() self.update()
def main(pm=None): def main():
if pm is None: mic = Mic()
pm = messaging.PubMaster(['microphone'])
mic = Mic(pm)
mic.micd_thread() mic.micd_thread()

@ -1 +1 @@
_sensord sensord

@ -14,4 +14,4 @@ sensors = [
libs = [common, cereal, messaging, 'capnp', 'zmq', 'kj', 'pthread'] libs = [common, cereal, messaging, 'capnp', 'zmq', 'kj', 'pthread']
if arch == "larch64": if arch == "larch64":
libs.append('i2c') libs.append('i2c')
env.Program('_sensord', ['sensors_qcom2.cc'] + sensors, LIBS=libs) env.Program('sensord', ['sensors_qcom2.cc'] + sensors, LIBS=libs)

@ -1,4 +0,0 @@
#!/bin/sh
cd "$(dirname "$0")"
export LD_LIBRARY_PATH="/system/lib64:$LD_LIBRARY_PATH"
exec ./_sensord

@ -108,7 +108,7 @@ class TestSensord(unittest.TestCase):
os.environ["LSM_SELF_TEST"] = "1" os.environ["LSM_SELF_TEST"] = "1"
# read initial sensor values every test case can use # read initial sensor values every test case can use
os.system("pkill -f ./_sensord") os.system("pkill -f ./sensord")
try: try:
managed_processes["sensord"].start() managed_processes["sensord"].start()
cls.sample_secs = int(os.getenv("SAMPLE_SECS", "10")) cls.sample_secs = int(os.getenv("SAMPLE_SECS", "10"))

@ -1 +1 @@
Subproject commit d8dda2af3afcef0bb772fff580cfa8b3eabf7f69 Subproject commit 5a4a62ecaecb2bfd8bb0f77033aca46df4e668bd

@ -24,6 +24,7 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) {
delegate = new BinaryItemDelegate(this); delegate = new BinaryItemDelegate(this);
setItemDelegate(delegate); setItemDelegate(delegate);
horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
horizontalHeader()->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
verticalHeader()->setSectionsClickable(false); verticalHeader()->setSectionsClickable(false);
verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
verticalHeader()->setDefaultSectionSize(CELL_HEIGHT); verticalHeader()->setDefaultSectionSize(CELL_HEIGHT);

@ -370,6 +370,10 @@ void ChartsWidget::doAutoScroll() {
} }
} }
QSize ChartsWidget::minimumSizeHint() const {
return QSize(CHART_MIN_WIDTH, QWidget::minimumSizeHint().height());
}
void ChartsWidget::resizeEvent(QResizeEvent *event) { void ChartsWidget::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event); QWidget::resizeEvent(event);
updateLayout(); updateLayout();
@ -405,16 +409,15 @@ void ChartsWidget::removeAll() {
tabbar->removeTab(1); tabbar->removeTab(1);
} }
tab_charts.clear(); tab_charts.clear();
zoomReset();
if (!charts.isEmpty()) { if (!charts.isEmpty()) {
for (auto c : charts) { for (auto c : charts) {
delete c; delete c;
} }
charts.clear(); charts.clear();
updateToolBar();
emit seriesChanged(); emit seriesChanged();
} }
zoomReset();
} }
void ChartsWidget::alignCharts() { void ChartsWidget::alignCharts() {

@ -54,6 +54,7 @@ signals:
void seriesChanged(); void seriesChanged();
private: private:
QSize minimumSizeHint() const override;
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
bool event(QEvent *event) override; bool event(QEvent *event) override;
void alignCharts(); void alignCharts();

@ -134,11 +134,13 @@ void DetailWidget::refresh() {
for (auto s : binary_view->getOverlappingSignals()) { for (auto s : binary_view->getOverlappingSignals()) {
warnings.push_back(tr("%1 has overlapping bits.").arg(s->name)); warnings.push_back(tr("%1 has overlapping bits.").arg(s->name));
} }
name_label->setText(QString("%1 (%2)").arg(msgName(msg_id), msg->transmitter));
} else { } else {
warnings.push_back(tr("Drag-Select in binary view to create new signal.")); warnings.push_back(tr("Drag-Select in binary view to create new signal."));
name_label->setText(msgName(msg_id));
} }
QString msg_name = msg ? QString("%1 (%2)").arg(msg->name, msg->transmitter) : msgName(msg_id);
name_label->setText(msg_name);
name_label->setToolTip(msg_name);
remove_btn->setEnabled(msg != nullptr); remove_btn->setEnabled(msg != nullptr);
if (!warnings.isEmpty()) { if (!warnings.isEmpty()) {

@ -39,13 +39,15 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) {
group->setExclusive(true); group->setExclusive(true);
QHBoxLayout *control_layout = new QHBoxLayout(); QHBoxLayout *control_layout = new QHBoxLayout();
play_btn = new QPushButton(); play_btn = new QToolButton();
play_btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); play_btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
control_layout->addWidget(play_btn); control_layout->addWidget(play_btn);
if (can->liveStreaming()) { if (can->liveStreaming()) {
control_layout->addWidget(skip_to_end_btn = new QPushButton(utils::icon("skip-end-fill"), {})); control_layout->addWidget(skip_to_end_btn = new QToolButton(this));
skip_to_end_btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
skip_to_end_btn->setIcon(utils::icon("skip-end-fill"));
skip_to_end_btn->setToolTip(tr("Skip to the end")); skip_to_end_btn->setToolTip(tr("Skip to the end"));
QObject::connect(skip_to_end_btn, &QPushButton::clicked, [group]() { QObject::connect(skip_to_end_btn, &QToolButton::clicked, [group]() {
// set speed to 1.0 // set speed to 1.0
group->buttons()[2]->click(); group->buttons()[2]->click();
can->pause(false); can->pause(false);
@ -54,9 +56,11 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) {
} }
for (float speed : {0.1, 0.5, 1., 2.}) { for (float speed : {0.1, 0.5, 1., 2.}) {
QPushButton *btn = new QPushButton(QString("%1x").arg(speed), this); QToolButton *btn = new QToolButton(this);
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
btn->setText(QString("%1x").arg(speed));
btn->setCheckable(true); btn->setCheckable(true);
QObject::connect(btn, &QPushButton::clicked, [speed]() { can->setSpeed(speed); }); QObject::connect(btn, &QToolButton::clicked, [speed]() { can->setSpeed(speed); });
control_layout->addWidget(btn); control_layout->addWidget(btn);
group->addButton(btn); group->addButton(btn);
if (speed == 1.0) btn->setChecked(true); if (speed == 1.0) btn->setChecked(true);
@ -64,7 +68,7 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) {
main_layout->addLayout(control_layout); main_layout->addLayout(control_layout);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
QObject::connect(play_btn, &QPushButton::clicked, []() { can->pause(!can->isPaused()); }); QObject::connect(play_btn, &QToolButton::clicked, []() { can->pause(!can->isPaused()); });
QObject::connect(can, &AbstractStream::paused, this, &VideoWidget::updatePlayBtnState); QObject::connect(can, &AbstractStream::paused, this, &VideoWidget::updatePlayBtnState);
QObject::connect(can, &AbstractStream::resume, this, &VideoWidget::updatePlayBtnState); QObject::connect(can, &AbstractStream::resume, this, &VideoWidget::updatePlayBtnState);
QObject::connect(&settings, &Settings::changed, this, &VideoWidget::updatePlayBtnState); QObject::connect(&settings, &Settings::changed, this, &VideoWidget::updatePlayBtnState);

@ -9,8 +9,8 @@
#include <QFuture> #include <QFuture>
#include <QLabel> #include <QLabel>
#include <QPushButton>
#include <QSlider> #include <QSlider>
#include <QToolButton>
#include "selfdrive/ui/qt/widgets/cameraview.h" #include "selfdrive/ui/qt/widgets/cameraview.h"
#include "tools/cabana/streams/abstractstream.h" #include "tools/cabana/streams/abstractstream.h"
@ -80,8 +80,8 @@ protected:
double maximum_time = 0; double maximum_time = 0;
QLabel *end_time_label; QLabel *end_time_label;
QLabel *time_label; QLabel *time_label;
QPushButton *play_btn; QToolButton *play_btn;
QPushButton *skip_to_end_btn = nullptr; QToolButton *skip_to_end_btn = nullptr;
InfoLabel *alert_label; InfoLabel *alert_label;
Slider *slider; Slider *slider;
}; };

@ -54,8 +54,8 @@ fi
eval "$(pyenv init --path)" eval "$(pyenv init --path)"
echo "update pip" echo "update pip"
pip install pip==23.2.1 pip install pip==23.3
pip install poetry==1.5.1 pip install poetry==1.6.1
poetry config virtualenvs.prefer-active-python true --local poetry config virtualenvs.prefer-active-python true --local
poetry config virtualenvs.in-project true --local poetry config virtualenvs.in-project true --local

@ -3,7 +3,6 @@ import os
import pickle import pickle
import struct import struct
import subprocess import subprocess
import tempfile
import threading import threading
from enum import IntEnum from enum import IntEnum
from functools import wraps from functools import wraps
@ -61,16 +60,14 @@ def fingerprint_video(fn):
def ffprobe(fn, fmt=None): def ffprobe(fn, fmt=None):
fn = resolve_name(fn) fn = resolve_name(fn)
cmd = ["ffprobe", cmd = ["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams"]
"-v", "quiet",
"-print_format", "json",
"-show_format", "-show_streams"]
if fmt: if fmt:
cmd += ["-f", fmt] cmd += ["-f", fmt]
cmd += [fn] cmd += ["-i", "-"]
try: try:
ffprobe_output = subprocess.check_output(cmd) with FileReader(fn) as f:
ffprobe_output = subprocess.check_output(cmd, input=f.read(4096))
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise DataUnreadableError(fn) from e raise DataUnreadableError(fn) from e
@ -170,31 +167,21 @@ def rgb24tonv12(rgb):
def decompress_video_data(rawdat, vid_fmt, w, h, pix_fmt): def decompress_video_data(rawdat, vid_fmt, w, h, pix_fmt):
# using a tempfile is much faster than proc.communicate for some reason threads = os.getenv("FFMPEG_THREADS", "0")
cuda = os.getenv("FFMPEG_CUDA", "0") == "1"
with tempfile.TemporaryFile() as tmpf: args = ["ffmpeg", "-v", "quiet",
tmpf.write(rawdat) "-threads", threads,
tmpf.seek(0) "-hwaccel", "none" if not cuda else "cuda",
"-c:v", "hevc",
threads = os.getenv("FFMPEG_THREADS", "0") "-vsync", "0",
cuda = os.getenv("FFMPEG_CUDA", "0") == "1" "-f", vid_fmt,
args = ["ffmpeg", "-flags2", "showall",
"-threads", threads, "-i", "-",
"-hwaccel", "none" if not cuda else "cuda", "-threads", threads,
"-c:v", "hevc", "-f", "rawvideo",
"-vsync", "0", "-pix_fmt", pix_fmt,
"-f", vid_fmt, "-"]
"-flags2", "showall", dat = subprocess.check_output(args, input=rawdat)
"-i", "pipe:0",
"-threads", threads,
"-f", "rawvideo",
"-pix_fmt", pix_fmt,
"pipe:1"]
with subprocess.Popen(args, stdin=tmpf, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) as proc:
# dat = proc.communicate()[0]
dat = proc.stdout.read()
if proc.wait() != 0:
raise DataUnreadableError("ffmpeg failed")
if pix_fmt == "rgb24": if pix_fmt == "rgb24":
ret = np.frombuffer(dat, dtype=np.uint8).reshape(-1, h, w, 3) ret = np.frombuffer(dat, dtype=np.uint8).reshape(-1, h, w, 3)

@ -6,11 +6,14 @@ import urllib.parse
import capnp import capnp
import warnings import warnings
from typing import Iterable, Iterator
from cereal import log as capnp_log from cereal import log as capnp_log
from openpilot.tools.lib.filereader import FileReader from openpilot.tools.lib.filereader import FileReader
from openpilot.tools.lib.route import Route, SegmentName from openpilot.tools.lib.route import Route, SegmentName
LogIterable = Iterable[capnp._DynamicStructReader]
# this is an iterator itself, and uses private variables from LogReader # this is an iterator itself, and uses private variables from LogReader
class MultiLogIterator: class MultiLogIterator:
def __init__(self, log_paths, sort_by_time=False): def __init__(self, log_paths, sort_by_time=False):
@ -30,7 +33,7 @@ class MultiLogIterator:
return self._log_readers[i] return self._log_readers[i]
def __iter__(self): def __iter__(self) -> Iterator[capnp._DynamicStructReader]:
return self return self
def _inc(self): def _inc(self):
@ -107,7 +110,7 @@ class LogReader:
def from_bytes(cls, dat): def from_bytes(cls, dat):
return cls("", dat=dat) return cls("", dat=dat)
def __iter__(self): def __iter__(self) -> Iterator[capnp._DynamicStructReader]:
for ent in self._ents: for ent in self._ents:
if self._only_union_types: if self._only_union_types:
try: try:

@ -116,9 +116,9 @@ def ui_thread(addr):
if yuv_img_raw is None or not yuv_img_raw.data.any(): if yuv_img_raw is None or not yuv_img_raw.data.any():
continue continue
imgff = np.frombuffer(yuv_img_raw.data, dtype=np.uint8).reshape((vipc_client.height * 3 // 2, vipc_client.width)) imgff = np.frombuffer(yuv_img_raw.data, dtype=np.uint8).reshape((len(yuv_img_raw.data) // vipc_client.stride, vipc_client.stride))
num_px = vipc_client.width * vipc_client.height num_px = vipc_client.width * vipc_client.height
bgr = cv2.cvtColor(imgff, cv2.COLOR_YUV2RGB_NV12) bgr = cv2.cvtColor(imgff[:vipc_client.height * 3 // 2, :vipc_client.width], cv2.COLOR_YUV2RGB_NV12)
zoom_matrix = _BB_TO_FULL_FRAME[num_px] zoom_matrix = _BB_TO_FULL_FRAME[num_px]
cv2.warpAffine(bgr, zoom_matrix[:2], (img.shape[1], img.shape[0]), dst=img, flags=cv2.WARP_INVERSE_MAP) cv2.warpAffine(bgr, zoom_matrix[:2], (img.shape[1], img.shape[0]), dst=img, flags=cv2.WARP_INVERSE_MAP)

@ -31,6 +31,7 @@ COPY ./panda ${OPENPILOT_PATH}/panda
COPY ./selfdrive ${OPENPILOT_PATH}/selfdrive COPY ./selfdrive ${OPENPILOT_PATH}/selfdrive
COPY ./system ${OPENPILOT_PATH}/system COPY ./system ${OPENPILOT_PATH}/system
COPY ./tools ${OPENPILOT_PATH}/tools COPY ./tools ${OPENPILOT_PATH}/tools
COPY ./release ${OPENPILOT_PATH}/release
RUN --mount=type=bind,source=.ci_cache/scons_cache,target=/tmp/scons_cache,rw scons -j$(nproc) --cache-readonly RUN --mount=type=bind,source=.ci_cache/scons_cache,target=/tmp/scons_cache,rw scons -j$(nproc) --cache-readonly

Loading…
Cancel
Save